Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

- Update azure-core-tracing-opentelemetry version and improve HTTP suppression to back off
when Azure SDK tracing was disabled.
([#12489](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/12489))

## Version 2.9.0 (2024-10-17)

### 📈 Enhancements
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ dependencies {
// Ensure no cross interference
testInstrumentation(project(":instrumentation:azure-core:azure-core-1.19:javaagent"))
testInstrumentation(project(":instrumentation:azure-core:azure-core-1.36:javaagent"))
testInstrumentation(project(":instrumentation:azure-core:azure-core-1.53:javaagent"))
}

val latestDepTest = findProperty("testLatestDeps") as Boolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ dependencies {
// Ensure no cross interference
testInstrumentation(project(":instrumentation:azure-core:azure-core-1.14:javaagent"))
testInstrumentation(project(":instrumentation:azure-core:azure-core-1.36:javaagent"))
testInstrumentation(project(":instrumentation:azure-core:azure-core-1.53:javaagent"))
}

val latestDepTest = findProperty("testLatestDeps") as Boolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ muzzle {
pass {
group.set("com.azure")
module.set("azure-core")
versions.set("[1.36.0,)")
versions.set("[1.36.0,1.53.0)")
assertInverse.set(true)
}
}
Expand All @@ -29,6 +29,7 @@ dependencies {
// Ensure no cross interference
testInstrumentation(project(":instrumentation:azure-core:azure-core-1.14:javaagent"))
testInstrumentation(project(":instrumentation:azure-core:azure-core-1.19:javaagent"))
testInstrumentation(project(":instrumentation:azure-core:azure-core-1.53:javaagent"))
}

val latestDepTest = findProperty("testLatestDeps") as Boolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.returns;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;

import com.azure.core.http.HttpResponse;
import io.opentelemetry.context.Scope;
Expand All @@ -35,30 +36,36 @@ public void transform(TypeTransformer transformer) {
isMethod()
.and(isPublic())
.and(named("send"))
.and(takesArgument(1, named("com.azure.core.util.Context")))
.and(returns(named("reactor.core.publisher.Mono"))),
this.getClass().getName() + "$SuppressNestedClientMonoAdvice");
transformer.applyAdviceToMethod(
isMethod()
.and(isPublic())
.and(named("sendSync"))
.and(takesArgument(1, named("com.azure.core.util.Context")))
.and(returns(named("com.azure.core.http.HttpResponse"))),
this.getClass().getName() + "$SuppressNestedClientSyncAdvice");
}

@SuppressWarnings("unused")
public static class SuppressNestedClientMonoAdvice {
@Advice.OnMethodExit(suppress = Throwable.class)
public static void asyncSendExit(@Advice.Return(readOnly = false) Mono<HttpResponse> mono) {
mono = disallowNestedClientSpanMono(mono);
public static void asyncSendExit(
@Advice.Argument(1) com.azure.core.util.Context azContext,
@Advice.Return(readOnly = false) Mono<HttpResponse> mono) {
mono = disallowNestedClientSpanMono(mono, azContext);
}
}

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

@Advice.OnMethodEnter(suppress = Throwable.class)
public static void syncSendEnter(@Advice.Local("otelScope") Scope scope) {
scope = disallowNestedClientSpanSync();
public static void syncSendEnter(
@Advice.Argument(1) com.azure.core.util.Context azContext,
@Advice.Local("otelScope") Scope scope) {
scope = disallowNestedClientSpanSync(azContext);
}

@Advice.OnMethodExit(suppress = Throwable.class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,9 @@ public void injectClasses(ClassInjector injector) {
public ElementMatcher.Junction<ClassLoader> classLoaderMatcher() {
// this class was introduced in azure-core 1.36
return hasClassesNamed("com.azure.core.util.tracing.TracerProvider")
.and(not(hasClassesNamed("com.azure.core.tracing.opentelemetry.OpenTelemetryTracer")));
.and(not(hasClassesNamed("com.azure.core.tracing.opentelemetry.OpenTelemetryTracer")))
// this class was introduced in azure-core 1.53
.and(not(hasClassesNamed("com.azure.core.util.LibraryTelemetryOptions")));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,24 @@

public class SuppressNestedClientHelper {

public static Scope disallowNestedClientSpanSync() {
public static Scope disallowNestedClientSpanSync(com.azure.core.util.Context azContext) {
Context parentContext = currentContext();
if (doesNotHaveClientSpan(parentContext)) {
boolean hasAzureClientSpan = azContext.getData("client-method-call-flag").isPresent();
if (doesNotHaveClientSpan(parentContext) && hasAzureClientSpan) {
return disallowNestedClientSpan(parentContext).makeCurrent();
}
return null;
}

public static <T> Mono<T> disallowNestedClientSpanMono(Mono<T> delegate) {
public static <T> Mono<T> disallowNestedClientSpanMono(
Mono<T> delegate, com.azure.core.util.Context azContext) {
return new Mono<T>() {
@Override
public void subscribe(CoreSubscriber<? super T> coreSubscriber) {
Context parentContext = currentContext();
if (doesNotHaveClientSpan(parentContext)) {

boolean hasAzureClientSpan = azContext.getData("client-method-call-flag").isPresent();
if (doesNotHaveClientSpan(parentContext) && hasAzureClientSpan) {
try (Scope ignored = disallowNestedClientSpan(parentContext).makeCurrent()) {
delegate.subscribe(coreSubscriber);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,7 @@ void testHelperClassesInjected() {
assertThat(azTracer.isEnabled()).isTrue();

assertThat(azTracer.getClass().getName())
.isEqualTo(
"io.opentelemetry.javaagent.instrumentation.azurecore.v1_36.shaded"
+ ".com.azure.core.tracing.opentelemetry.OpenTelemetryTracer");
.endsWith("com.azure.core.tracing.opentelemetry.OpenTelemetryTracer");
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,7 @@ void testHelperClassesInjected() {
assertThat(azTracer.isEnabled()).isTrue();

assertThat(azTracer.getClass().getName())
.isEqualTo(
"io.opentelemetry.javaagent.instrumentation.azurecore.v1_36.shaded"
+ ".com.azure.core.tracing.opentelemetry.OpenTelemetryTracer");
.endsWith(".shaded.com.azure.core.tracing.opentelemetry.OpenTelemetryTracer");
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ plugins {
group = "io.opentelemetry.javaagent.instrumentation"

dependencies {
implementation("com.azure:azure-core-tracing-opentelemetry:1.0.0-beta.42")
// this is the last good version that works with indy build
// update to 1.49 or latest once https://github.com/Azure/azure-sdk-for-java/pull/42586 is released.
implementation("com.azure:azure-core-tracing-opentelemetry:1.0.0-beta.45")
}

tasks {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
plugins {
id("otel.javaagent-instrumentation")
}

muzzle {
pass {
group.set("com.azure")
module.set("azure-core")
versions.set("[1.53.0,)")
assertInverse.set(true)
}
}

sourceSets {
main {
val shadedDep = project(":instrumentation:azure-core:azure-core-1.53:library-instrumentation-shaded")
output.dir(
shadedDep.file("build/extracted/shadow"),
"builtBy" to ":instrumentation:azure-core:azure-core-1.53:library-instrumentation-shaded:extractShadowJar"
)
}
}

dependencies {
compileOnly(project(":instrumentation:azure-core:azure-core-1.53:library-instrumentation-shaded", configuration = "shadow"))

library("com.azure:azure-core:1.53.0")

// Ensure no cross interference
testInstrumentation(project(":instrumentation:azure-core:azure-core-1.14:javaagent"))
testInstrumentation(project(":instrumentation:azure-core:azure-core-1.19:javaagent"))
testInstrumentation(project(":instrumentation:azure-core:azure-core-1.36:javaagent"))
}

val latestDepTest = findProperty("testLatestDeps") as Boolean

testing {
suites {
// using a test suite to ensure that classes from library-instrumentation-shaded that were
// extracted to the output directory are not available during tests
val testAzure by registering(JvmTestSuite::class) {
dependencies {
if (latestDepTest) {
implementation("com.azure:azure-core:+")
implementation("com.azure:azure-core-test:+")
} else {
implementation("com.azure:azure-core:1.53.0")
implementation("com.azure:azure-core-test:1.26.2")
}
}
}
}
}

tasks {
check {
dependsOn(testing.suites)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.instrumentation.azurecore.v1_53;

import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface;
import static io.opentelemetry.javaagent.instrumentation.azurecore.v1_53.SuppressNestedClientHelper.disallowNestedClientSpanMono;
import static io.opentelemetry.javaagent.instrumentation.azurecore.v1_53.SuppressNestedClientHelper.disallowNestedClientSpanSync;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.returns;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;

import com.azure.core.http.HttpResponse;
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;
import reactor.core.publisher.Mono;

public class AzureHttpClientInstrumentation implements TypeInstrumentation {

@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return implementsInterface(named("com.azure.core.http.HttpClient"));
}

@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
isMethod()
.and(isPublic())
.and(named("send"))
.and(takesArgument(1, named("com.azure.core.util.Context")))
.and(returns(named("reactor.core.publisher.Mono"))),
this.getClass().getName() + "$SuppressNestedClientMonoAdvice");
transformer.applyAdviceToMethod(
isMethod()
.and(isPublic())
.and(named("sendSync"))
.and(takesArgument(1, named("com.azure.core.util.Context")))
.and(returns(named("com.azure.core.http.HttpResponse"))),
this.getClass().getName() + "$SuppressNestedClientSyncAdvice");
}

@SuppressWarnings("unused")
public static class SuppressNestedClientMonoAdvice {
@Advice.OnMethodExit(suppress = Throwable.class)
public static void asyncSendExit(
@Advice.Argument(1) com.azure.core.util.Context azContext,
@Advice.Return(readOnly = false) Mono<HttpResponse> mono) {
mono = disallowNestedClientSpanMono(mono, azContext);
}
}

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

@Advice.OnMethodEnter(suppress = Throwable.class)
public static void syncSendEnter(
@Advice.Argument(1) com.azure.core.util.Context azContext,
@Advice.Local("otelScope") Scope scope) {
scope = disallowNestedClientSpanSync(azContext);
}

@Advice.OnMethodExit(suppress = Throwable.class)
public static void syncSendExit(
@Advice.Return HttpResponse response, @Advice.Local("otelScope") Scope scope) {
if (scope != null) {
scope.close();
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.instrumentation.azurecore.v1_53;

import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
import static java.util.Arrays.asList;
import static net.bytebuddy.matcher.ElementMatchers.namedOneOf;
import static net.bytebuddy.matcher.ElementMatchers.not;

import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.instrumentation.HelperResourceBuilder;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.extension.instrumentation.internal.ExperimentalInstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.internal.injection.ClassInjector;
import io.opentelemetry.javaagent.extension.instrumentation.internal.injection.InjectionMode;
import java.util.List;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;

@AutoService(InstrumentationModule.class)
public class AzureSdkInstrumentationModule extends InstrumentationModule
implements ExperimentalInstrumentationModule {
public AzureSdkInstrumentationModule() {
super("azure-core", "azure-core-1.53");
}

@Override
public void registerHelperResources(HelperResourceBuilder helperResourceBuilder) {
helperResourceBuilder.register(
"META-INF/services/com.azure.core.util.tracing.TracerProvider",
"azure-core-1.53/META-INF/services/com.azure.core.util.tracing.TracerProvider");
// some azure sdks (e.g. EventHubs) are still looking up Tracer via service loader
// and not yet using the new TracerProvider
helperResourceBuilder.register(
"META-INF/services/com.azure.core.util.tracing.Tracer",
"azure-core-1.53/META-INF/services/com.azure.core.util.tracing.Tracer");
}

@Override
public void injectClasses(ClassInjector injector) {
injector
.proxyBuilder(
"io.opentelemetry.javaagent.instrumentation.azurecore.v1_53.shaded.com.azure.core.tracing.opentelemetry.OpenTelemetryTracer")
.inject(InjectionMode.CLASS_ONLY);
injector
.proxyBuilder(
"io.opentelemetry.javaagent.instrumentation.azurecore.v1_53.shaded.com.azure.core.tracing.opentelemetry.OpenTelemetryTracerProvider")
.inject(InjectionMode.CLASS_ONLY);
}

@Override
public ElementMatcher.Junction<ClassLoader> classLoaderMatcher() {
// this class was introduced in azure-core 1.53
return hasClassesNamed("com.azure.core.util.LibraryTelemetryOptions")
.and(not(hasClassesNamed("com.azure.core.tracing.opentelemetry.OpenTelemetryTracer")));
}

@Override
public List<TypeInstrumentation> typeInstrumentations() {
return asList(new EmptyTypeInstrumentation(), new AzureHttpClientInstrumentation());
}

public static class EmptyTypeInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return namedOneOf(
"com.azure.core.util.tracing.TracerProvider", "com.azure.core.util.tracing.Tracer");
}

@Override
public void transform(TypeTransformer transformer) {
// Nothing to instrument, no methods to match
}
}
}
Loading
Loading