Skip to content

Commit f657fc4

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

File tree

6 files changed

+294
-0
lines changed

6 files changed

+294
-0
lines changed
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("[1.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:1.0.0")
19+
20+
testImplementation(project(":testing-common"))
21+
22+
latestDepTestLibrary("com.ctrip.framework.apollo:apollo-client:1.+")
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,25 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.apolloconfig.v1_0_0;
7+
8+
import static java.util.Collections.singletonList;
9+
10+
import com.google.auto.service.AutoService;
11+
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
12+
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
13+
import java.util.List;
14+
15+
@AutoService(InstrumentationModule.class)
16+
public class ApolloConfigInstrumentationModule extends InstrumentationModule {
17+
public ApolloConfigInstrumentationModule() {
18+
super("apolloconfig", "apolloconfig-1.0");
19+
}
20+
21+
@Override
22+
public List<TypeInstrumentation> typeInstrumentations() {
23+
return singletonList(new ApolloRepositoryChangeInstrumentation());
24+
}
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.apolloconfig.v1_0_0;
7+
8+
import static io.opentelemetry.api.common.AttributeKey.stringKey;
9+
10+
import io.opentelemetry.api.GlobalOpenTelemetry;
11+
import io.opentelemetry.api.common.AttributeKey;
12+
import io.opentelemetry.api.common.AttributesBuilder;
13+
import io.opentelemetry.api.trace.StatusCode;
14+
import io.opentelemetry.context.Context;
15+
import io.opentelemetry.context.ContextKey;
16+
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
17+
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
18+
import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor;
19+
import io.opentelemetry.instrumentation.api.instrumenter.SpanStatusExtractor;
20+
import javax.annotation.Nullable;
21+
22+
public final class ApolloConfigSingletons {
23+
24+
private static final String NAME = "io.opentelemetry.apolloconfig-1.0.0";
25+
private static final Instrumenter<String, Void> INSTRUMENTER;
26+
27+
private static final AttributeKey<String> CONFIG_NS_ATTRIBUTE_KEY = stringKey("config.namespace");
28+
public static final ContextKey<String> REPOSITORY_CHANGE_REPEAT_CONTEXT_KEY =
29+
ContextKey.named("apollo-config-repository-change-repeat");
30+
31+
static {
32+
AttributesExtractor<String, Void> attributesExtractor =
33+
new AttributesExtractor<String, Void>() {
34+
35+
@Override
36+
public void onStart(
37+
AttributesBuilder attributes, Context parentContext, String namespace) {
38+
if (namespace == null) {
39+
return;
40+
}
41+
42+
attributes.put(CONFIG_NS_ATTRIBUTE_KEY, namespace);
43+
}
44+
45+
@Override
46+
public void onEnd(
47+
AttributesBuilder attributes,
48+
Context context,
49+
String namespace,
50+
@Nullable Void unused,
51+
@Nullable Throwable error) {}
52+
};
53+
54+
SpanStatusExtractor<String, Void> spanStatusExtractor =
55+
(spanStatusBuilder, request, unused, error) -> {
56+
if (error != null) {
57+
spanStatusBuilder.setStatus(StatusCode.ERROR);
58+
}
59+
};
60+
61+
INSTRUMENTER =
62+
Instrumenter.<String, Void>builder(
63+
GlobalOpenTelemetry.get(), NAME, (event) -> "Apollo Config Repository Change")
64+
.setSpanStatusExtractor(spanStatusExtractor)
65+
.addAttributesExtractor(attributesExtractor)
66+
.buildInstrumenter(SpanKindExtractor.alwaysClient());
67+
}
68+
69+
public static Instrumenter<String, Void> instrumenter() {
70+
return INSTRUMENTER;
71+
}
72+
73+
private ApolloConfigSingletons() {}
74+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.apolloconfig.v1_0_0;
7+
8+
import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext;
9+
import static io.opentelemetry.javaagent.instrumentation.apolloconfig.v1_0_0.ApolloConfigSingletons.REPOSITORY_CHANGE_REPEAT_CONTEXT_KEY;
10+
import static io.opentelemetry.javaagent.instrumentation.apolloconfig.v1_0_0.ApolloConfigSingletons.instrumenter;
11+
import static net.bytebuddy.matcher.ElementMatchers.named;
12+
13+
import io.opentelemetry.context.Context;
14+
import io.opentelemetry.context.Scope;
15+
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
16+
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
17+
import net.bytebuddy.asm.Advice;
18+
import net.bytebuddy.description.type.TypeDescription;
19+
import net.bytebuddy.matcher.ElementMatcher;
20+
21+
public class ApolloRepositoryChangeInstrumentation implements TypeInstrumentation {
22+
23+
@Override
24+
public ElementMatcher<TypeDescription> typeMatcher() {
25+
return named("com.ctrip.framework.apollo.internals.AbstractConfigRepository");
26+
}
27+
28+
@Override
29+
public void transform(TypeTransformer transformer) {
30+
transformer.applyAdviceToMethod(
31+
named("fireRepositoryChange"), this.getClass().getName() + "$ApolloRepositoryChangeAdvice");
32+
}
33+
34+
@SuppressWarnings("unused")
35+
public static class ApolloRepositoryChangeAdvice {
36+
37+
@Advice.OnMethodEnter(suppress = Throwable.class)
38+
public static void onEnter(
39+
@Advice.Argument(value = 0) String namespace,
40+
@Advice.Local("otelContext") Context context,
41+
@Advice.Local("otelScope") Scope scope) {
42+
Context parentContext = currentContext();
43+
String repeat = parentContext.get(REPOSITORY_CHANGE_REPEAT_CONTEXT_KEY);
44+
if (repeat != null) {
45+
return;
46+
}
47+
if (!instrumenter().shouldStart(parentContext, namespace)) {
48+
return;
49+
}
50+
51+
context = instrumenter().start(parentContext, namespace);
52+
context = context.with(REPOSITORY_CHANGE_REPEAT_CONTEXT_KEY, "1");
53+
scope = context.makeCurrent();
54+
}
55+
56+
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
57+
public static void onExit(
58+
@Advice.Argument(value = 0) String namespace,
59+
@Advice.Thrown Throwable throwable,
60+
@Advice.Local("otelContext") Context context,
61+
@Advice.Local("otelScope") Scope scope) {
62+
if (scope == null) {
63+
return;
64+
}
65+
scope.close();
66+
instrumenter().end(context, namespace, null, throwable);
67+
}
68+
}
69+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.apolloconfig.v1_0_0;
7+
8+
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
9+
10+
import com.ctrip.framework.apollo.internals.AbstractConfigRepository;
11+
import com.ctrip.framework.apollo.internals.ConfigRepository;
12+
import com.ctrip.framework.apollo.internals.RepositoryChangeListener;
13+
import io.opentelemetry.api.common.AttributeKey;
14+
import io.opentelemetry.api.trace.SpanKind;
15+
import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
16+
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
17+
import io.opentelemetry.sdk.testing.assertj.AttributeAssertion;
18+
import java.util.ArrayList;
19+
import java.util.List;
20+
import java.util.Properties;
21+
import org.junit.jupiter.api.Test;
22+
import org.junit.jupiter.api.extension.RegisterExtension;
23+
24+
class ApolloRepositoryChangeTest {
25+
26+
@RegisterExtension
27+
private static final InstrumentationExtension testing = AgentInstrumentationExtension.create();
28+
29+
@Test
30+
void test() {
31+
String namespace = "application";
32+
33+
TestConfigRepository testConfigRepository = new TestConfigRepository(namespace);
34+
testConfigRepository.addChangeListener(new TestRepositoryChangeListener());
35+
testConfigRepository.sync();
36+
37+
checkRepositoryChange(namespace);
38+
}
39+
40+
private static void checkRepositoryChange(String namespace) {
41+
String spanName = "Apollo Config Repository Change";
42+
List<AttributeAssertion> attributeAssertions = new ArrayList<>();
43+
attributeAssertions.add(equalTo(AttributeKey.stringKey("config.namespace"), namespace));
44+
45+
testing.waitAndAssertTraces(
46+
trace ->
47+
trace.hasSpansSatisfyingExactly(
48+
span ->
49+
span.hasKind(SpanKind.CLIENT)
50+
.hasName(spanName)
51+
.hasAttributesSatisfyingExactly(attributeAssertions)));
52+
}
53+
54+
static class TestConfigRepository extends AbstractConfigRepository {
55+
56+
final String namespace;
57+
58+
public TestConfigRepository(String namespace) {
59+
this.namespace = namespace;
60+
}
61+
62+
@Override
63+
protected void sync() {
64+
this.fireRepositoryChange(this.namespace, new Properties());
65+
}
66+
67+
@Override
68+
public Properties getConfig() {
69+
return new Properties();
70+
}
71+
72+
@Override
73+
public void setUpstreamRepository(ConfigRepository upstreamConfigRepository) {}
74+
}
75+
76+
static class TestRepositoryChangeListener implements RepositoryChangeListener {
77+
78+
@Override
79+
public void onRepositoryChange(String namespace, Properties newProperties) {
80+
new AbstractConfigRepository() {
81+
@Override
82+
public Properties getConfig() {
83+
return newProperties;
84+
}
85+
86+
@Override
87+
public void setUpstreamRepository(ConfigRepository upstreamConfigRepository) {}
88+
89+
@Override
90+
protected void sync() {
91+
this.fireRepositoryChange(namespace, new Properties());
92+
}
93+
}.sync();
94+
}
95+
}
96+
}

settings.gradle.kts

+1
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ 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-1.0.0:javaagent")
195196
include(":instrumentation:armeria:armeria-1.3:javaagent")
196197
include(":instrumentation:armeria:armeria-1.3:library")
197198
include(":instrumentation:armeria:armeria-1.3:testing")

0 commit comments

Comments
 (0)