Skip to content

Commit 8dcf619

Browse files
committed
Add Apolloconfig Inst
#12787
1 parent 057ba16 commit 8dcf619

File tree

6 files changed

+276
-0
lines changed

6 files changed

+276
-0
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
plugins {
2+
id("otel.javaagent-instrumentation")
3+
}
4+
5+
muzzle {
6+
pass {
7+
group.set("com.ctrip.framework.apollo")
8+
module.set("apollo-client")
9+
versions.set("[2.0.0,2.3.0)")
10+
assertInverse.set(true)
11+
}
12+
}
13+
14+
dependencies {
15+
compileOnly("com.google.auto.value:auto-value-annotations")
16+
annotationProcessor("com.google.auto.value:auto-value")
17+
18+
library("com.ctrip.framework.apollo:apollo-client:2.0.0")
19+
20+
testImplementation(project(":instrumentation:apolloconfig:apolloconfig-2.0.0:testing"))
21+
22+
latestDepTestLibrary("com.ctrip.framework.apollo:apollo-client:2.0.+")
23+
}
24+
25+
tasks.withType<Test>().configureEach {
26+
// required on jdk17
27+
jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED")
28+
jvmArgs("-XX:+IgnoreUnrecognizedVMOptions")
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package io.opentelemetry.javaagent.instrumentation.apolloconfig.v2_0_0;
2+
3+
import com.google.auto.service.AutoService;
4+
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
5+
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
6+
import java.util.Collections;
7+
import java.util.List;
8+
9+
@AutoService(InstrumentationModule.class)
10+
public class ApolloConfigInstrumentationModule extends InstrumentationModule {
11+
public ApolloConfigInstrumentationModule() {
12+
super("apolloconfig", "apolloconfig-2.0.0");
13+
}
14+
15+
@Override
16+
public List<TypeInstrumentation> typeInstrumentations() {
17+
return Collections.singletonList(new ApolloRepositoryChangeInstrumentation());
18+
}
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package io.opentelemetry.javaagent.instrumentation.apolloconfig.v2_0_0;
2+
3+
4+
import static io.opentelemetry.api.common.AttributeKey.stringKey;
5+
6+
import io.opentelemetry.api.GlobalOpenTelemetry;
7+
import io.opentelemetry.api.common.AttributeKey;
8+
import io.opentelemetry.api.common.AttributesBuilder;
9+
import io.opentelemetry.api.trace.StatusCode;
10+
import io.opentelemetry.context.Context;
11+
import io.opentelemetry.context.ContextKey;
12+
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
13+
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
14+
import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor;
15+
import io.opentelemetry.instrumentation.api.instrumenter.SpanStatusExtractor;
16+
import javax.annotation.Nullable;
17+
18+
public class ApolloConfigSingletons {
19+
20+
private static final String NAME = "io.opentelemetry.javaagent.apolloconfig-2.0.0";
21+
private static final Instrumenter<String, Void> INSTRUMENTER;
22+
23+
private static final AttributeKey<String> CONFIG_NS_ATTRIBUTE_KEY = stringKey("config.namespace");
24+
public static final ContextKey<String> REPOSITORY_CHANGE_REPEAT_CONTEXT_KEY =
25+
ContextKey.named("apollo-config-repository-change-repeat");
26+
27+
static {
28+
AttributesExtractor<String, Void> attributesExtractor =
29+
new AttributesExtractor<String, Void>() {
30+
31+
@Override
32+
public void onStart(
33+
AttributesBuilder attributes, Context parentContext,
34+
String namespace) {
35+
if (namespace == null) {
36+
return;
37+
}
38+
39+
attributes.put(CONFIG_NS_ATTRIBUTE_KEY, namespace);
40+
}
41+
42+
@Override
43+
public void onEnd(
44+
AttributesBuilder attributes,
45+
Context context,
46+
String namespace,
47+
@Nullable Void unused,
48+
@Nullable Throwable error) {
49+
}
50+
};
51+
52+
SpanStatusExtractor<String, Void> spanStatusExtractor =
53+
(spanStatusBuilder, request, unused, error) -> {
54+
if (error != null) {
55+
spanStatusBuilder.setStatus(StatusCode.ERROR);
56+
}
57+
};
58+
59+
INSTRUMENTER = Instrumenter.<String, Void>builder(
60+
GlobalOpenTelemetry.get(),
61+
NAME,
62+
(event) -> "Apollo Config Repository Change")
63+
.setSpanStatusExtractor(spanStatusExtractor)
64+
.addAttributesExtractor(attributesExtractor)
65+
.buildInstrumenter(SpanKindExtractor.alwaysClient());
66+
}
67+
68+
public static Instrumenter<String, Void> instrumenter() {
69+
return INSTRUMENTER;
70+
}
71+
72+
private ApolloConfigSingletons() {}
73+
74+
75+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package io.opentelemetry.javaagent.instrumentation.apolloconfig.v2_0_0;
2+
3+
import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext;
4+
import static io.opentelemetry.javaagent.instrumentation.apolloconfig.v2_0_0.ApolloConfigSingletons.REPOSITORY_CHANGE_REPEAT_CONTEXT_KEY;
5+
import static io.opentelemetry.javaagent.instrumentation.apolloconfig.v2_0_0.ApolloConfigSingletons.instrumenter;
6+
import static net.bytebuddy.matcher.ElementMatchers.named;
7+
8+
import io.opentelemetry.context.Context;
9+
import io.opentelemetry.context.Scope;
10+
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
11+
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
12+
import net.bytebuddy.asm.Advice;
13+
import net.bytebuddy.description.type.TypeDescription;
14+
import net.bytebuddy.matcher.ElementMatcher;
15+
16+
public class ApolloRepositoryChangeInstrumentation implements TypeInstrumentation {
17+
18+
@Override
19+
public ElementMatcher<TypeDescription> typeMatcher() {
20+
return named("com.ctrip.framework.apollo.internals.AbstractConfigRepository");
21+
}
22+
23+
@Override
24+
public void transform(TypeTransformer transformer) {
25+
String adviceName = this.getClass().getName() + "$ApolloRepositoryChangeAdvice";
26+
transformer.applyAdviceToMethod(named("fireRepositoryChange"), adviceName);
27+
}
28+
29+
@SuppressWarnings("unused")
30+
public static class ApolloRepositoryChangeAdvice {
31+
32+
@Advice.OnMethodEnter(suppress = Throwable.class)
33+
public static void onEnter(
34+
@Advice.Argument(value = 0) String namespace,
35+
@Advice.Local("otelContext") Context context,
36+
@Advice.Local("otelScope") Scope scope) {
37+
Context parentContext = currentContext();
38+
String repeat = parentContext.get(REPOSITORY_CHANGE_REPEAT_CONTEXT_KEY);
39+
if (repeat != null) {
40+
return;
41+
}
42+
if (!instrumenter().shouldStart(parentContext, namespace)) {
43+
return;
44+
}
45+
46+
context = instrumenter().start(parentContext, namespace);
47+
context = context.with(REPOSITORY_CHANGE_REPEAT_CONTEXT_KEY, "1");
48+
scope = context.makeCurrent();
49+
}
50+
51+
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
52+
public static void onExit(
53+
@Advice.Argument(value = 0) String namespace,
54+
@Advice.Thrown Throwable throwable,
55+
@Advice.Local("otelContext") Context context,
56+
@Advice.Local("otelScope") Scope scope) {
57+
if (scope == null) {
58+
return;
59+
}
60+
scope.close();
61+
instrumenter().end(context, namespace, null, throwable);
62+
}
63+
64+
}
65+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package io.opentelemetry.instrumentation.apolloconfig.v2_0_0;
2+
3+
4+
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
5+
6+
import com.ctrip.framework.apollo.enums.ConfigSourceType;
7+
import com.ctrip.framework.apollo.internals.AbstractConfigRepository;
8+
import com.ctrip.framework.apollo.internals.ConfigRepository;
9+
import io.opentelemetry.api.common.AttributeKey;
10+
import io.opentelemetry.api.trace.SpanKind;
11+
import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
12+
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
13+
import io.opentelemetry.sdk.testing.assertj.AttributeAssertion;
14+
import java.util.ArrayList;
15+
import java.util.List;
16+
import java.util.Properties;
17+
import org.junit.jupiter.api.Test;
18+
import org.junit.jupiter.api.extension.RegisterExtension;
19+
20+
public abstract class ApolloRepositoryChangeTest {
21+
22+
@RegisterExtension
23+
private static final InstrumentationExtension testing = AgentInstrumentationExtension.create();
24+
25+
@Test
26+
void test() {
27+
String namespace = "application";
28+
TestConfigRepository testConfigRepository = new TestConfigRepository(namespace, true);
29+
testConfigRepository.sync();
30+
31+
checkRepositoryChange(
32+
"Apollo Config Repository Change",
33+
namespace
34+
);
35+
}
36+
37+
private static void checkRepositoryChange(
38+
String spanName,
39+
String namespace) {
40+
List<AttributeAssertion> attributeAssertions = new ArrayList<>();
41+
attributeAssertions.add(equalTo(AttributeKey.stringKey("config.namespace"), namespace));
42+
43+
testing.waitAndAssertTraces(
44+
trace ->
45+
trace.hasSpansSatisfyingExactly(
46+
span ->
47+
span.hasKind(SpanKind.CLIENT)
48+
.hasName(spanName)
49+
.hasAttributesSatisfyingExactly(attributeAssertions)));
50+
}
51+
52+
static class TestConfigRepository extends AbstractConfigRepository {
53+
54+
final String namespace;
55+
final Boolean hasNext;
56+
57+
public TestConfigRepository(String namespace, Boolean hasNext) {
58+
this.namespace = namespace;
59+
this.hasNext = Boolean.TRUE;
60+
}
61+
62+
@Override
63+
protected void sync() {
64+
this.fireRepositoryChange(this.namespace, new Properties());
65+
if (hasNext) {
66+
TestConfigRepository sub = new TestConfigRepository(namespace, false);
67+
sub.sync();
68+
}
69+
}
70+
71+
@Override
72+
public Properties getConfig() {
73+
return new Properties();
74+
}
75+
76+
@Override
77+
public void setUpstreamRepository(ConfigRepository upstreamConfigRepository) {
78+
79+
}
80+
81+
@Override
82+
public ConfigSourceType getSourceType() {
83+
return ConfigSourceType.NONE;
84+
}
85+
}
86+
}

settings.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,8 @@ include(":instrumentation:apache-httpclient:apache-httpclient-4.3:testing")
192192
include(":instrumentation:apache-httpclient:apache-httpclient-5.0:javaagent")
193193
include(":instrumentation:apache-httpclient:apache-httpclient-5.2:library")
194194
include(":instrumentation:apache-shenyu-2.4:javaagent")
195+
include(":instrumentation:apolloconfig:apolloconfig-2.0.0:javaagent")
196+
include(":instrumentation:apolloconfig:apolloconfig-2.0.0:testing")
195197
include(":instrumentation:armeria:armeria-1.3:javaagent")
196198
include(":instrumentation:armeria:armeria-1.3:library")
197199
include(":instrumentation:armeria:armeria-1.3:testing")

0 commit comments

Comments
 (0)