diff --git a/instrumentation/opentelemetry-annotations-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/otelannotations/WithSpanInstrumentation.java b/instrumentation/opentelemetry-annotations-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/otelannotations/WithSpanInstrumentation.java index 3f63a52ae713..7334865711ea 100644 --- a/instrumentation/opentelemetry-annotations-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/otelannotations/WithSpanInstrumentation.java +++ b/instrumentation/opentelemetry-annotations-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/otelannotations/WithSpanInstrumentation.java @@ -117,8 +117,6 @@ public static class WithSpanAdvice { public static void onEnter( @Advice.Origin Method originMethod, @Advice.Local("otelMethod") Method method, - @Advice.Local("otelOperationEndSupport") - AsyncOperationEndSupport operationEndSupport, @Advice.Local("otelContext") Context context, @Advice.Local("otelScope") Scope scope) { @@ -132,16 +130,12 @@ public static void onEnter( if (instrumenter.shouldStart(current, method)) { context = instrumenter.start(current, method); scope = context.makeCurrent(); - operationEndSupport = - AsyncOperationEndSupport.create(instrumenter, Object.class, method.getReturnType()); } } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void stopSpan( @Advice.Local("otelMethod") Method method, - @Advice.Local("otelOperationEndSupport") - AsyncOperationEndSupport operationEndSupport, @Advice.Local("otelContext") Context context, @Advice.Local("otelScope") Scope scope, @Advice.Return(typing = Assigner.Typing.DYNAMIC, readOnly = false) Object returnValue, @@ -150,6 +144,9 @@ public static void stopSpan( return; } scope.close(); + + AsyncOperationEndSupport operationEndSupport = + AsyncOperationEndSupport.create(instrumenter(), Object.class, method.getReturnType()); returnValue = operationEndSupport.asyncEnd(context, method, returnValue, throwable); } } diff --git a/instrumentation/reactor/reactor-3.1/javaagent/build.gradle.kts b/instrumentation/reactor/reactor-3.1/javaagent/build.gradle.kts index 7fb51121f9dd..5525a074390f 100644 --- a/instrumentation/reactor/reactor-3.1/javaagent/build.gradle.kts +++ b/instrumentation/reactor/reactor-3.1/javaagent/build.gradle.kts @@ -34,3 +34,21 @@ dependencies { // Looks like later versions on reactor need this dependency for some reason even though it is marked as optional. latestDepTestLibrary("io.micrometer:micrometer-core:1.+") } + +testing { + suites { + val testInitialization by registering(JvmTestSuite::class) { + dependencies { + implementation(project(":instrumentation:reactor:reactor-3.1:library")) + implementation("io.opentelemetry:opentelemetry-extension-annotations") + implementation("io.projectreactor:reactor-test:3.1.0.RELEASE") + } + } + } +} + +tasks { + check { + dependsOn(testing.suites) + } +} diff --git a/instrumentation/reactor/reactor-3.1/javaagent/src/testInitialization/java/io/opentelemetry/javaagent/instrumentation/reactor/InitializationTest.java b/instrumentation/reactor/reactor-3.1/javaagent/src/testInitialization/java/io/opentelemetry/javaagent/instrumentation/reactor/InitializationTest.java new file mode 100644 index 000000000000..199b7bdb1b51 --- /dev/null +++ b/instrumentation/reactor/reactor-3.1/javaagent/src/testInitialization/java/io/opentelemetry/javaagent/instrumentation/reactor/InitializationTest.java @@ -0,0 +1,49 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.reactor; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.extension.annotations.WithSpan; +import java.util.stream.Collectors; +import org.junit.jupiter.api.Test; +import reactor.core.Scannable; +import reactor.core.publisher.Mono; + +// Isolated test to use clean classloader because reactor instrumentation is applied on static +// initialization. +class InitializationTest { + + @Test + void contextPropagated() { + Mono mono = new Traced().traceMe(); + + // If reactor augmentation of WithSpan is working correctly, we will end up with these + // implementation details. + // TODO(anuraaga): This should just check actual context propagation instead of implementation + // but couldn't figure out how. + assertThat(((Scannable) mono).parents().collect(Collectors.toList())) + .anySatisfy( + op -> { + assertThat(op.getClass().getSimpleName()).isEqualTo("MonoFlatMap"); + assertThat(op) + .extracting("source") + .satisfies( + source -> + assertThat(source.getClass().getSimpleName()) + .isEqualTo("ScalarPropagatingMono")); + }); + + assertThat(mono.block()).isEqualTo("foo"); + } + + static class Traced { + @WithSpan + Mono traceMe() { + return Mono.just("foo"); + } + } +}