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 031bb5f
Show file tree
Hide file tree
Showing 6 changed files with 276 additions and 0 deletions.
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.0.0")

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

latestDepTestLibrary("com.ctrip.framework.apollo:apollo-client: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.v1_0_0;

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", "apolloconfig-1.0");
}

@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.v1_0_0;

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.javaagent.apolloconfig-1.0";
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.v1_0_0;

import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext;
import static io.opentelemetry.javaagent.instrumentation.apolloconfig.v1_0_0.ApolloConfigSingletons.REPOSITORY_CHANGE_REPEAT_CONTEXT_KEY;
import static io.opentelemetry.javaagent.instrumentation.apolloconfig.v1_0_0.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,78 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.instrumentation.apolloconfig.v1_0_0;

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

import com.ctrip.framework.apollo.internals.AbstractConfigRepository;
import com.ctrip.framework.apollo.internals.ConfigRepository;
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, true);
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;
final Boolean hasNext;

public TestConfigRepository(String namespace, Boolean hasNext) {
this.namespace = namespace;
this.hasNext = Boolean.TRUE;
}

@Override
protected void sync() {
this.fireRepositoryChange(this.namespace, new Properties());
if (hasNext) {
TestConfigRepository sub = new TestConfigRepository(namespace, false);
sub.sync();
}
}

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

@Override
public void setUpstreamRepository(ConfigRepository upstreamConfigRepository) {}
}
}
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:apolloconfig-1.0.0: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 031bb5f

Please sign in to comment.