Skip to content

Commit f75200a

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

File tree

7 files changed

+306
-0
lines changed

7 files changed

+306
-0
lines changed

docs/supported-libraries.md

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ These are the supported libraries and frameworks:
3131
| [Apache HttpAsyncClient](https://hc.apache.org/index.html) | 4.1+ | N/A | [HTTP Client Spans], [HTTP Client Metrics] |
3232
| [Apache HttpClient](https://hc.apache.org/index.html) | 2.0+ | [opentelemetry-apache-httpclient-4.3](../instrumentation/apache-httpclient/apache-httpclient-4.3/library),<br>[opentelemetry-apache-httpclient-5.2](../instrumentation/apache-httpclient/apache-httpclient-5.2/library) | [HTTP Client Spans], [HTTP Client Metrics] |
3333
| [Apache ShenYu](https://shenyu.apache.org/) | 2.4+ | N/A | Provides `http.route` [2] |
34+
| [Apollo Configuration Center/Apollo Client](https://www.apolloconfig.com/#/en/README) | 1.0+ | N/A | none |
3435
| [Apache Kafka Producer/Consumer API](https://kafka.apache.org/documentation/#producerapi) | 0.11+ | [opentelemetry-kafka-clients-2.6](../instrumentation/kafka/kafka-clients/kafka-clients-2.6/library) | [Messaging Spans] |
3536
| [Apache Kafka Streams API](https://kafka.apache.org/documentation/streams/) | 0.11+ | N/A | [Messaging Spans] |
3637
| [Apache MyFaces](https://myfaces.apache.org/) | 1.2+ (not including 3.x yet) | N/A | Provides `http.route` [2], Controller Spans [3] |
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.1.0")
19+
20+
testImplementation(project(":testing-common"))
21+
22+
latestDepTestLibrary("com.ctrip.framework.apollo:apollo-client:1.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.apolloclient.v1_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-apolloclient", "apolloconfig-apolloclient-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.apolloclient.v1_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-apolloclient-1.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.apolloclient.v1_0;
7+
8+
import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext;
9+
import static io.opentelemetry.javaagent.instrumentation.apolloconfig.apolloclient.v1_0.ApolloConfigSingletons.REPOSITORY_CHANGE_REPEAT_CONTEXT_KEY;
10+
import static io.opentelemetry.javaagent.instrumentation.apolloconfig.apolloclient.v1_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,107 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.apolloconfig.apolloclient.v1_0;
7+
8+
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
9+
10+
import com.ctrip.framework.apollo.enums.ConfigSourceType;
11+
import com.ctrip.framework.apollo.internals.AbstractConfigRepository;
12+
import com.ctrip.framework.apollo.internals.ConfigRepository;
13+
import com.ctrip.framework.apollo.internals.RepositoryChangeListener;
14+
import io.opentelemetry.api.common.AttributeKey;
15+
import io.opentelemetry.api.trace.SpanKind;
16+
import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
17+
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
18+
import io.opentelemetry.sdk.testing.assertj.AttributeAssertion;
19+
import java.util.ArrayList;
20+
import java.util.List;
21+
import java.util.Properties;
22+
import org.junit.jupiter.api.Test;
23+
import org.junit.jupiter.api.extension.RegisterExtension;
24+
25+
class ApolloRepositoryChangeTest {
26+
27+
@RegisterExtension
28+
private static final InstrumentationExtension testing = AgentInstrumentationExtension.create();
29+
30+
@Test
31+
void test() {
32+
String namespace = "application";
33+
34+
TestConfigRepository testConfigRepository = new TestConfigRepository(namespace);
35+
testConfigRepository.addChangeListener(new TestRepositoryChangeListener());
36+
testConfigRepository.sync();
37+
38+
checkRepositoryChange(namespace);
39+
}
40+
41+
private static void checkRepositoryChange(String namespace) {
42+
String spanName = "Apollo Config Repository Change";
43+
List<AttributeAssertion> attributeAssertions = new ArrayList<>();
44+
attributeAssertions.add(equalTo(AttributeKey.stringKey("config.namespace"), namespace));
45+
46+
testing.waitAndAssertTraces(
47+
trace ->
48+
trace.hasSpansSatisfyingExactly(
49+
span ->
50+
span.hasKind(SpanKind.CLIENT)
51+
.hasName(spanName)
52+
.hasAttributesSatisfyingExactly(attributeAssertions)));
53+
}
54+
55+
static class TestConfigRepository extends AbstractConfigRepository {
56+
57+
final String namespace;
58+
59+
public TestConfigRepository(String namespace) {
60+
this.namespace = namespace;
61+
}
62+
63+
@Override
64+
protected void sync() {
65+
this.fireRepositoryChange(this.namespace, new Properties());
66+
}
67+
68+
@Override
69+
public Properties getConfig() {
70+
return new Properties();
71+
}
72+
73+
@Override
74+
public void setUpstreamRepository(ConfigRepository upstreamConfigRepository) {}
75+
76+
@Override
77+
public ConfigSourceType getSourceType() {
78+
return ConfigSourceType.NONE;
79+
}
80+
}
81+
82+
static class TestRepositoryChangeListener implements RepositoryChangeListener {
83+
84+
@Override
85+
public void onRepositoryChange(String namespace, Properties newProperties) {
86+
new AbstractConfigRepository() {
87+
@Override
88+
public Properties getConfig() {
89+
return newProperties;
90+
}
91+
92+
@Override
93+
public void setUpstreamRepository(ConfigRepository upstreamConfigRepository) {}
94+
95+
@Override
96+
public ConfigSourceType getSourceType() {
97+
return ConfigSourceType.NONE;
98+
}
99+
100+
@Override
101+
protected void sync() {
102+
this.fireRepositoryChange(namespace, new Properties());
103+
}
104+
}.sync();
105+
}
106+
}
107+
}

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-apolloclient-1.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)