Skip to content

Commit

Permalink
Add Apolloconfig Inst
Browse files Browse the repository at this point in the history
  • Loading branch information
freshchen committed Nov 27, 2024
1 parent 057ba16 commit 0e437e7
Show file tree
Hide file tree
Showing 7 changed files with 306 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/supported-libraries.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ These are the supported libraries and frameworks:
| [Apache HttpAsyncClient](https://hc.apache.org/index.html) | 4.1+ | N/A | [HTTP Client Spans], [HTTP Client Metrics] |
| [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] |
| [Apache ShenYu](https://shenyu.apache.org/) | 2.4+ | N/A | Provides `http.route` [2] |
| [Apollo Configuration Center/Apollo Client](https://www.apolloconfig.com/#/en/README) | 1.0+ | N/A | none |
| [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] |
| [Apache Kafka Streams API](https://kafka.apache.org/documentation/streams/) | 0.11+ | N/A | [Messaging Spans] |
| [Apache MyFaces](https://myfaces.apache.org/) | 1.2+ (not including 3.x yet) | N/A | Provides `http.route` [2], Controller Spans [3] |
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
plugins {
id("otel.javaagent-instrumentation")
}

muzzle {
pass {
group.set("com.ctrip.framework.apollo")
module.set("apollo-client")
versions.set("[1.0.0,2.3.0]")
assertInverse.set(true)
}
}

dependencies {
compileOnly("com.google.auto.value:auto-value-annotations")
annotationProcessor("com.google.auto.value:auto-value")

library("com.ctrip.framework.apollo:apollo-client:1.1.0")

testImplementation(project(":testing-common"))

latestDepTestLibrary("com.ctrip.framework.apollo:apollo-client:1.1.+")
}

tasks.withType<Test>().configureEach {
// required on jdk17
jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED")
jvmArgs("-XX:+IgnoreUnrecognizedVMOptions")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.instrumentation.apolloconfig.apolloclient.v1_1;

import static java.util.Collections.singletonList;

import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import java.util.List;

@AutoService(InstrumentationModule.class)
public class ApolloConfigInstrumentationModule extends InstrumentationModule {
public ApolloConfigInstrumentationModule() {
super("apolloconfig-apolloclient", "apolloconfig-apolloclient-1.1");
}

@Override
public List<TypeInstrumentation> typeInstrumentations() {
return singletonList(new ApolloRepositoryChangeInstrumentation());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.instrumentation.apolloconfig.apolloclient.v1_1;

import static io.opentelemetry.api.common.AttributeKey.stringKey;

import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.AttributesBuilder;
import io.opentelemetry.api.trace.StatusCode;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.ContextKey;
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.SpanStatusExtractor;
import javax.annotation.Nullable;

public final class ApolloConfigSingletons {

private static final String NAME = "io.opentelemetry.apolloconfig-apolloclient-1.1";
private static final Instrumenter<String, Void> INSTRUMENTER;

private static final AttributeKey<String> CONFIG_NS_ATTRIBUTE_KEY = stringKey("config.namespace");
public static final ContextKey<String> REPOSITORY_CHANGE_REPEAT_CONTEXT_KEY =
ContextKey.named("apollo-config-repository-change-repeat");

static {
AttributesExtractor<String, Void> attributesExtractor =
new AttributesExtractor<String, Void>() {

@Override
public void onStart(
AttributesBuilder attributes, Context parentContext, String namespace) {
if (namespace == null) {
return;
}

attributes.put(CONFIG_NS_ATTRIBUTE_KEY, namespace);
}

@Override
public void onEnd(
AttributesBuilder attributes,
Context context,
String namespace,
@Nullable Void unused,
@Nullable Throwable error) {}
};

SpanStatusExtractor<String, Void> spanStatusExtractor =
(spanStatusBuilder, request, unused, error) -> {
if (error != null) {
spanStatusBuilder.setStatus(StatusCode.ERROR);
}
};

INSTRUMENTER =
Instrumenter.<String, Void>builder(
GlobalOpenTelemetry.get(), NAME, (event) -> "Apollo Config Repository Change")
.setSpanStatusExtractor(spanStatusExtractor)
.addAttributesExtractor(attributesExtractor)
.buildInstrumenter(SpanKindExtractor.alwaysClient());
}

public static Instrumenter<String, Void> instrumenter() {
return INSTRUMENTER;
}

private ApolloConfigSingletons() {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.instrumentation.apolloconfig.apolloclient.v1_1;

import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext;
import static io.opentelemetry.javaagent.instrumentation.apolloconfig.apolloclient.v1_1.ApolloConfigSingletons.REPOSITORY_CHANGE_REPEAT_CONTEXT_KEY;
import static io.opentelemetry.javaagent.instrumentation.apolloconfig.apolloclient.v1_1.ApolloConfigSingletons.instrumenter;
import static net.bytebuddy.matcher.ElementMatchers.named;

import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;

public class ApolloRepositoryChangeInstrumentation implements TypeInstrumentation {

@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("com.ctrip.framework.apollo.internals.AbstractConfigRepository");
}

@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
named("fireRepositoryChange"), this.getClass().getName() + "$ApolloRepositoryChangeAdvice");
}

@SuppressWarnings("unused")
public static class ApolloRepositoryChangeAdvice {

@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter(
@Advice.Argument(value = 0) String namespace,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
Context parentContext = currentContext();
String repeat = parentContext.get(REPOSITORY_CHANGE_REPEAT_CONTEXT_KEY);
if (repeat != null) {
return;
}
if (!instrumenter().shouldStart(parentContext, namespace)) {
return;
}

context = instrumenter().start(parentContext, namespace);
context = context.with(REPOSITORY_CHANGE_REPEAT_CONTEXT_KEY, "1");
scope = context.makeCurrent();
}

@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void onExit(
@Advice.Argument(value = 0) String namespace,
@Advice.Thrown Throwable throwable,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
if (scope == null) {
return;
}
scope.close();
instrumenter().end(context, namespace, null, throwable);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.instrumentation.apolloconfig.apolloclient.v1_1;

import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;

import com.ctrip.framework.apollo.enums.ConfigSourceType;
import com.ctrip.framework.apollo.internals.AbstractConfigRepository;
import com.ctrip.framework.apollo.internals.ConfigRepository;
import com.ctrip.framework.apollo.internals.RepositoryChangeListener;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.trace.SpanKind;
import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
import io.opentelemetry.sdk.testing.assertj.AttributeAssertion;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

class ApolloRepositoryChangeTest {

@RegisterExtension
private static final InstrumentationExtension testing = AgentInstrumentationExtension.create();

@Test
void test() {
String namespace = "application";

TestConfigRepository testConfigRepository = new TestConfigRepository(namespace);
testConfigRepository.addChangeListener(new TestRepositoryChangeListener());
testConfigRepository.sync();

checkRepositoryChange(namespace);
}

private static void checkRepositoryChange(String namespace) {
String spanName = "Apollo Config Repository Change";
List<AttributeAssertion> attributeAssertions = new ArrayList<>();
attributeAssertions.add(equalTo(AttributeKey.stringKey("config.namespace"), namespace));

testing.waitAndAssertTraces(
trace ->
trace.hasSpansSatisfyingExactly(
span ->
span.hasKind(SpanKind.CLIENT)
.hasName(spanName)
.hasAttributesSatisfyingExactly(attributeAssertions)));
}

static class TestConfigRepository extends AbstractConfigRepository {

final String namespace;

public TestConfigRepository(String namespace) {
this.namespace = namespace;
}

@Override
protected void sync() {
this.fireRepositoryChange(this.namespace, new Properties());
}

@Override
public Properties getConfig() {
return new Properties();
}

@Override
public void setUpstreamRepository(ConfigRepository upstreamConfigRepository) {}

@Override
public ConfigSourceType getSourceType() {
return ConfigSourceType.NONE;
}
}

static class TestRepositoryChangeListener implements RepositoryChangeListener {

@Override
public void onRepositoryChange(String namespace, Properties newProperties) {
new AbstractConfigRepository() {
@Override
public Properties getConfig() {
return newProperties;
}

@Override
public void setUpstreamRepository(ConfigRepository upstreamConfigRepository) {}

@Override
public ConfigSourceType getSourceType() {
return ConfigSourceType.NONE;
}

@Override
protected void sync() {
this.fireRepositoryChange(namespace, new Properties());
}
}.sync();
}
}
}
1 change: 1 addition & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ include(":instrumentation:apache-httpclient:apache-httpclient-4.3:testing")
include(":instrumentation:apache-httpclient:apache-httpclient-5.0:javaagent")
include(":instrumentation:apache-httpclient:apache-httpclient-5.2:library")
include(":instrumentation:apache-shenyu-2.4:javaagent")
include(":instrumentation:apolloconfig-apolloclient:apolloconfig-apolloclient-1.1:javaagent")
include(":instrumentation:armeria:armeria-1.3:javaagent")
include(":instrumentation:armeria:armeria-1.3:library")
include(":instrumentation:armeria:armeria-1.3:testing")
Expand Down

0 comments on commit 0e437e7

Please sign in to comment.