From 0bfb6980b02d2833ca839692963685efb5558abe Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Mon, 9 Jan 2023 20:31:47 -0800 Subject: [PATCH 01/10] Use new contextWrite when available --- .../reactor-3.1/javaagent/build.gradle.kts | 2 +- .../reactor/ContextPropagationOperator.java | 74 +++++++++++++++++-- 2 files changed, 69 insertions(+), 7 deletions(-) diff --git a/instrumentation/reactor/reactor-3.1/javaagent/build.gradle.kts b/instrumentation/reactor/reactor-3.1/javaagent/build.gradle.kts index 2797ef862153..6ee953f266f9 100644 --- a/instrumentation/reactor/reactor-3.1/javaagent/build.gradle.kts +++ b/instrumentation/reactor/reactor-3.1/javaagent/build.gradle.kts @@ -6,7 +6,7 @@ muzzle { pass { group.set("io.projectreactor") module.set("reactor-core") - versions.set("[3.1.0.RELEASE,3.5.0)") + versions.set("[3.1.0.RELEASE,)") extraDependency("io.opentelemetry:opentelemetry-api:1.0.0") assertInverse.set(true) excludeInstrumentationName("opentelemetry-api") diff --git a/instrumentation/reactor/reactor-3.1/library/src/main/java/io/opentelemetry/instrumentation/reactor/ContextPropagationOperator.java b/instrumentation/reactor/reactor-3.1/library/src/main/java/io/opentelemetry/instrumentation/reactor/ContextPropagationOperator.java index 7fe10e97e47d..571a60270532 100644 --- a/instrumentation/reactor/reactor-3.1/library/src/main/java/io/opentelemetry/instrumentation/reactor/ContextPropagationOperator.java +++ b/instrumentation/reactor/reactor-3.1/library/src/main/java/io/opentelemetry/instrumentation/reactor/ContextPropagationOperator.java @@ -22,9 +22,13 @@ package io.opentelemetry.instrumentation.reactor; +import static java.lang.invoke.MethodType.methodType; + import io.opentelemetry.context.Context; import io.opentelemetry.context.Scope; import io.opentelemetry.instrumentation.api.annotation.support.async.AsyncOperationEndStrategies; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; import java.util.function.BiFunction; import java.util.function.Function; import javax.annotation.Nullable; @@ -42,6 +46,28 @@ public final class ContextPropagationOperator { private static final Object VALUE = new Object(); + @Nullable + private static final MethodHandle MONO_CONTEXT_WRITE_METHOD = getContextWriteMethod(Mono.class); + + @Nullable + private static final MethodHandle FLUX_CONTEXT_WRITE_METHOD = getContextWriteMethod(Flux.class); + + @Nullable + private static MethodHandle getContextWriteMethod(Class type) { + MethodHandles.Lookup lookup = MethodHandles.publicLookup(); + try { + return lookup.findVirtual(type, "contextWrite", methodType(type, Function.class)); + } catch (NoSuchMethodException | IllegalAccessException e) { + // ignore + } + try { + return lookup.findVirtual(type, "subscriberContext", methodType(type, Function.class)); + } catch (NoSuchMethodException | IllegalAccessException e) { + // ignore + } + return null; + } + public static ContextPropagationOperator create() { return builder().build(); } @@ -133,29 +159,65 @@ public void resetOnEachOperator() { } /** Forces Mono to run in traceContext scope. */ + @SuppressWarnings("unchecked") public static Mono runWithContext(Mono publisher, Context tracingContext) { - if (!enabled) { + if (!enabled || MONO_CONTEXT_WRITE_METHOD == null) { return publisher; } // this hack forces 'publisher' to run in the onNext callback of `TracingSubscriber` // (created for this publisher) and with current() span that refers to span created here // without the hack, publisher runs in the onAssembly stage, before traceContext is made current - return ScalarPropagatingMono.create(publisher) - .subscriberContext(ctx -> storeOpenTelemetryContext(ctx, tracingContext)); + try { + return (Mono) + MONO_CONTEXT_WRITE_METHOD.invoke( + ScalarPropagatingMono.create(publisher), + new StoreOpenTelemetryContext(tracingContext)); + } catch (Throwable t) { + // rethrowing without any wrapping to avoid any change to the underlying application behavior + throw sneakyThrow(t); + } } /** Forces Flux to run in traceContext scope. */ + @SuppressWarnings("unchecked") public static Flux runWithContext(Flux publisher, Context tracingContext) { - if (!enabled) { + if (!enabled || FLUX_CONTEXT_WRITE_METHOD == null) { return publisher; } // this hack forces 'publisher' to run in the onNext callback of `TracingSubscriber` // (created for this publisher) and with current() span that refers to span created here // without the hack, publisher runs in the onAssembly stage, before traceContext is made current - return ScalarPropagatingFlux.create(publisher) - .subscriberContext(ctx -> storeOpenTelemetryContext(ctx, tracingContext)); + try { + return (Flux) + FLUX_CONTEXT_WRITE_METHOD.invoke( + ScalarPropagatingFlux.create(publisher), + new StoreOpenTelemetryContext(tracingContext)); + } catch (Throwable t) { + // rethrowing without any wrapping to avoid any change to the underlying application behavior + throw sneakyThrow(t); + } + } + + @SuppressWarnings({"TypeParameterUnusedInFormals", "unchecked"}) + private static T sneakyThrow(Throwable t) throws T { + throw (T) t; + } + + private static class StoreOpenTelemetryContext + implements Function { + + private final Context tracingContext; + + private StoreOpenTelemetryContext(Context tracingContext) { + this.tracingContext = tracingContext; + } + + @Override + public reactor.util.context.Context apply(reactor.util.context.Context context) { + return storeOpenTelemetryContext(context, tracingContext); + } } public static class Lifter From bc08b73d60308846721424fc9d4e88cd66169773 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Mon, 9 Jan 2023 20:34:19 -0800 Subject: [PATCH 02/10] In tests too --- ...ropagationOperatorInstrumentationTest.java | 70 +++++-- .../reactor/ReactorCoreTest.java | 174 ++++++++++++------ 2 files changed, 172 insertions(+), 72 deletions(-) diff --git a/instrumentation/reactor/reactor-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactor/ContextPropagationOperatorInstrumentationTest.java b/instrumentation/reactor/reactor-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactor/ContextPropagationOperatorInstrumentationTest.java index 757ea59f82d6..835130d0a9c3 100644 --- a/instrumentation/reactor/reactor-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactor/ContextPropagationOperatorInstrumentationTest.java +++ b/instrumentation/reactor/reactor-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactor/ContextPropagationOperatorInstrumentationTest.java @@ -6,6 +6,7 @@ package io.opentelemetry.javaagent.instrumentation.reactor; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.attributeEntry; +import static java.lang.invoke.MethodType.methodType; import static org.assertj.core.api.Assertions.assertThat; import io.opentelemetry.api.trace.Span; @@ -14,7 +15,11 @@ import io.opentelemetry.instrumentation.reactor.ContextPropagationOperator; import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; import java.time.Duration; +import java.util.function.Function; +import javax.annotation.Nullable; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import reactor.core.publisher.Flux; @@ -26,6 +31,25 @@ class ContextPropagationOperatorInstrumentationTest { @RegisterExtension static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); + @Nullable + private static final MethodHandle MONO_CONTEXT_WRITE_METHOD = getContextWriteMethod(Mono.class); + + @Nullable + private static MethodHandle getContextWriteMethod(Class type) { + MethodHandles.Lookup lookup = MethodHandles.publicLookup(); + try { + return lookup.findVirtual(type, "contextWrite", methodType(type, Function.class)); + } catch (NoSuchMethodException | IllegalAccessException e) { + // ignore + } + try { + return lookup.findVirtual(type, "subscriberContext", methodType(type, Function.class)); + } catch (NoSuchMethodException | IllegalAccessException e) { + // ignore + } + return null; + } + @Test void storeAndGetContext() { reactor.util.context.Context reactorContext = reactor.util.context.Context.empty(); @@ -136,18 +160,25 @@ void storeContextForcesItToBecomeCurrent() { Span span = testing.getOpenTelemetry().getTracer("test").spanBuilder("parent").startSpan(); - return Mono.delay(Duration.ofMillis(1)) - .flatMap( - unused -> { - // usual trick to force this to run under new TracingSubscriber with context - // written in the next call - return new ExtensionAnnotationsTracedWithSpan().mono(Mono.just("Value")); - }) - .subscriberContext( - ctx -> - ContextPropagationOperator.storeOpenTelemetryContext( - ctx, Context.current().with(span))) - .doFinally(unused -> span.end()); + Mono interim = + Mono.delay(Duration.ofMillis(1)) + .flatMap( + unused -> { + // usual trick to force this to run under new TracingSubscriber with + // context + // written in the next call + return new ExtensionAnnotationsTracedWithSpan() + .mono(Mono.just("Value")); + }); + try { + interim = + (Mono) + MONO_CONTEXT_WRITE_METHOD.invoke( + interim, new StoreOpenTelemetryContext(Context.current().with(span))); + } catch (Throwable e) { + throw new RuntimeException(e); + } + return interim.doFinally(unused -> span.end()); }); StepVerifier.create(result).expectNext("Value").verifyComplete(); @@ -161,4 +192,19 @@ void storeContextForcesItToBecomeCurrent() { .hasKind(SpanKind.INTERNAL) .hasParent(trace.getSpan(0)))); } + + private static class StoreOpenTelemetryContext + implements Function { + + private final Context tracingContext; + + private StoreOpenTelemetryContext(Context tracingContext) { + this.tracingContext = tracingContext; + } + + @Override + public reactor.util.context.Context apply(reactor.util.context.Context context) { + return ContextPropagationOperator.storeOpenTelemetryContext(context, tracingContext); + } + } } diff --git a/instrumentation/reactor/reactor-3.1/library/src/test/java/io/opentelemetry/instrumentation/reactor/ReactorCoreTest.java b/instrumentation/reactor/reactor-3.1/library/src/test/java/io/opentelemetry/instrumentation/reactor/ReactorCoreTest.java index eff9c205b0a1..b73bf53d6d33 100644 --- a/instrumentation/reactor/reactor-3.1/library/src/test/java/io/opentelemetry/instrumentation/reactor/ReactorCoreTest.java +++ b/instrumentation/reactor/reactor-3.1/library/src/test/java/io/opentelemetry/instrumentation/reactor/ReactorCoreTest.java @@ -6,6 +6,7 @@ package io.opentelemetry.instrumentation.reactor; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.attributeEntry; +import static java.lang.invoke.MethodType.methodType; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; @@ -16,7 +17,10 @@ import io.opentelemetry.context.Scope; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; import java.time.Duration; +import java.util.function.Function; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -27,11 +31,30 @@ import reactor.core.publisher.UnicastProcessor; import reactor.test.StepVerifier; +@SuppressWarnings("deprecation") // reasons class ReactorCoreTest extends AbstractReactorCoreTest { @RegisterExtension static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); + private static final MethodHandle MONO_CONTEXT_WRITE_METHOD = getContextWriteMethod(Mono.class); + + private static final MethodHandle FLUX_CONTEXT_WRITE_METHOD = getContextWriteMethod(Flux.class); + + private static MethodHandle getContextWriteMethod(Class type) { + MethodHandles.Lookup lookup = MethodHandles.publicLookup(); + try { + return lookup.findVirtual(type, "contextWrite", methodType(type, Function.class)); + } catch (NoSuchMethodException | IllegalAccessException e) { + // ignore + } + try { + return lookup.findVirtual(type, "subscriberContext", methodType(type, Function.class)); + } catch (NoSuchMethodException | IllegalAccessException e) { + throw new RuntimeException(e); + } + } + private final ContextPropagationOperator tracingOperator = ContextPropagationOperator.create(); private final Tracer tracer = testing.getOpenTelemetry().getTracer("test"); @@ -74,6 +97,7 @@ void monoInNonBlockingPublisherAssembly() { } @Test + @SuppressWarnings("unchecked") void fluxInNonBlockingPublisherAssembly() { Flux source = Flux.defer( @@ -83,30 +107,40 @@ void fluxInNonBlockingPublisherAssembly() { }); testing.runWithSpan( "parent", - () -> - ContextPropagationOperator.ScalarPropagatingFlux.create(source) - .doOnEach( - signal -> { - if (signal.isOnError()) { - // reactor 3.1 does not support getting context here yet - Span.current().setStatus(StatusCode.ERROR); - Span.current().end(); - } else if (signal.isOnComplete()) { - Span.current().end(); - } - }) - .subscriberContext( - ctx -> { - Context parent = - ContextPropagationOperator.getOpenTelemetryContext( - ctx, Context.current()); - - Span innerSpan = tracer.spanBuilder("inner").setParent(parent).startSpan(); - return ContextPropagationOperator.storeOpenTelemetryContext( - ctx, parent.with(innerSpan)); - }) - .collectList() - .block()); + () -> { + Flux interim = + ContextPropagationOperator.ScalarPropagatingFlux.create(source) + .doOnEach( + signal -> { + if (signal.isOnError()) { + // reactor 3.1 does not support getting context here yet + Span.current().setStatus(StatusCode.ERROR); + Span.current().end(); + } else if (signal.isOnComplete()) { + Span.current().end(); + } + }); + try { + interim = + (Flux) + FLUX_CONTEXT_WRITE_METHOD.invoke( + interim, + (Function) + ctx -> { + Context parent = + ContextPropagationOperator.getOpenTelemetryContext( + ctx, Context.current()); + + Span innerSpan = + tracer.spanBuilder("inner").setParent(parent).startSpan(); + return ContextPropagationOperator.storeOpenTelemetryContext( + ctx, parent.with(innerSpan)); + }); + } catch (Throwable t) { + throw new AssertionError(t); + } + interim.collectList().block(); + }); testing.waitAndAssertTraces( trace -> @@ -307,6 +341,7 @@ void doesNotOverrideInnerCurrentSpansAsync() { } @Test + @SuppressWarnings("unchecked") void doesNotOverrideInnerCurrentSpansWithThereIsOuterCurrent() { Flux publish = Flux.create( @@ -328,22 +363,31 @@ void doesNotOverrideInnerCurrentSpansWithThereIsOuterCurrent() { // I.e. tracing subscriber context at subscription time will be different from inner in onNext Span outer = tracer.spanBuilder("outer").startSpan(); try (Scope scope = outer.makeCurrent()) { - StepVerifier.create( - publish - .take(2) - .doOnNext( - n -> { - assertThat(Span.current().getSpanContext().isValid()).isTrue(); - Span.current().setAttribute("onNext", true); - }) - .subscriberContext( - // subscribers that know that their subscription can happen - // ahead of time and in the 'wrong' context, has to clean up 'wrong' context - context -> - ContextPropagationOperator.storeOpenTelemetryContext( - context, Context.root()))) - .expectNextCount(2) - .verifyComplete(); + Flux interim = + publish + .take(2) + .doOnNext( + n -> { + assertThat(Span.current().getSpanContext().isValid()).isTrue(); + Span.current().setAttribute("onNext", true); + }); + try { + interim = + (Flux) + FLUX_CONTEXT_WRITE_METHOD.invoke( + interim, + (Function) + context -> { + // subscribers that know that their subscription can happen + // ahead of time and in the 'wrong' context, has to clean up 'wrong' + // context + return ContextPropagationOperator.storeOpenTelemetryContext( + context, Context.root()); + }); + } catch (Throwable t) { + throw new AssertionError(t); + } + StepVerifier.create(interim).expectNextCount(2).verifyComplete(); outer.end(); } @@ -362,26 +406,36 @@ void doesNotOverrideInnerCurrentSpansWithThereIsOuterCurrent() { .hasAttributes(attributeEntry("onNext", true)))); } + @SuppressWarnings("unchecked") private Mono monoSpan(Mono mono, String spanName) { - return ContextPropagationOperator.ScalarPropagatingMono.create(mono) - .doOnEach( - signal -> { - if (signal.isOnError()) { - // reactor 3.1 does not support getting context here yet - Span.current().setStatus(StatusCode.ERROR); - Span.current().end(); - } else if (signal.isOnComplete()) { - Span.current().end(); - } - }) - .subscriberContext( - ctx -> { - Context parent = - ContextPropagationOperator.getOpenTelemetryContext(ctx, Context.current()); - - Span innerSpan = tracer.spanBuilder(spanName).setParent(parent).startSpan(); - return ContextPropagationOperator.storeOpenTelemetryContext( - ctx, parent.with(innerSpan)); - }); + + Mono interim = + ContextPropagationOperator.ScalarPropagatingMono.create(mono) + .doOnEach( + signal -> { + if (signal.isOnError()) { + // reactor 3.1 does not support getting context here yet + Span.current().setStatus(StatusCode.ERROR); + Span.current().end(); + } else if (signal.isOnComplete()) { + Span.current().end(); + } + }); + try { + return (Mono) + MONO_CONTEXT_WRITE_METHOD.invoke( + interim, + (Function) + ctx -> { + Context parent = + ContextPropagationOperator.getOpenTelemetryContext(ctx, Context.current()); + + Span innerSpan = tracer.spanBuilder(spanName).setParent(parent).startSpan(); + return ContextPropagationOperator.storeOpenTelemetryContext( + ctx, parent.with(innerSpan)); + }); + } catch (Throwable t) { + throw new AssertionError(t); + } } } From 49259e335767e328ace0b6ee5b24db4de41c76e3 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Mon, 9 Jan 2023 20:53:48 -0800 Subject: [PATCH 03/10] Revert "Add muzzle limits to fix CI (#7106)" This reverts commit d40377bbd7c5e8dcf8674dab7ce4f4c899ed77f2. --- .../reactor-netty-1.0/javaagent/build.gradle.kts | 6 +++--- .../spring/spring-webflux-5.0/javaagent/build.gradle.kts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/build.gradle.kts b/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/build.gradle.kts index 4efd42ffb7f9..3eac5b09ad01 100644 --- a/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/build.gradle.kts +++ b/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/build.gradle.kts @@ -3,17 +3,17 @@ plugins { } muzzle { - pass { + fail { group.set("io.projectreactor.netty") module.set("reactor-netty") - versions.set("[1.0.0,1.1.0)") + versions.set("[,1.0.0)") assertInverse.set(true) excludeInstrumentationName("netty") } pass { group.set("io.projectreactor.netty") module.set("reactor-netty-http") - versions.set("[1.0.0,1.1.0)") + versions.set("[1.0.0,)") assertInverse.set(true) excludeInstrumentationName("netty") } diff --git a/instrumentation/spring/spring-webflux-5.0/javaagent/build.gradle.kts b/instrumentation/spring/spring-webflux-5.0/javaagent/build.gradle.kts index dbc4296d5c52..2383bb9041ed 100644 --- a/instrumentation/spring/spring-webflux-5.0/javaagent/build.gradle.kts +++ b/instrumentation/spring/spring-webflux-5.0/javaagent/build.gradle.kts @@ -25,7 +25,7 @@ muzzle { name.set("netty_0.8.0+_with_spring-webflux:5.1.0") group.set("io.projectreactor.netty") module.set("reactor-netty") - versions.set("[0.8.0.RELEASE,1.1.0)") + versions.set("[0.8.0.RELEASE,)") extraDependency("org.springframework:spring-webflux:5.1.0.RELEASE") } From 8f79487710c2cc1cfb45ffb7da664e981b5ae450 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Tue, 10 Jan 2023 15:41:01 -0800 Subject: [PATCH 04/10] Webflux --- .../javaagent/build.gradle.kts | 4 ++-- ...uccessOrError.java => RouteOnSuccess.java} | 12 ++++------ .../server/RouterFunctionInstrumentation.java | 2 +- .../SpringWebfluxHttpAttributesGetter.java | 24 +++++++++++++++++-- 4 files changed, 30 insertions(+), 12 deletions(-) rename instrumentation/spring/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/server/{RouteOnSuccessOrError.java => RouteOnSuccess.java} (82%) diff --git a/instrumentation/spring/spring-webflux-5.0/javaagent/build.gradle.kts b/instrumentation/spring/spring-webflux-5.0/javaagent/build.gradle.kts index 2383bb9041ed..a01cf564f349 100644 --- a/instrumentation/spring/spring-webflux-5.0/javaagent/build.gradle.kts +++ b/instrumentation/spring/spring-webflux-5.0/javaagent/build.gradle.kts @@ -7,7 +7,7 @@ muzzle { name.set("webflux_5.0.0+_with_netty_0.8.0") group.set("org.springframework") module.set("spring-webflux") - versions.set("[5.0.0.RELEASE,6)") + versions.set("[5.0.0.RELEASE,)") assertInverse.set(true) extraDependency("io.projectreactor.netty:reactor-netty:0.8.0.RELEASE") } @@ -16,7 +16,7 @@ muzzle { name.set("webflux_5.0.0_with_ipc_0.7.0") group.set("org.springframework") module.set("spring-webflux") - versions.set("[5.0.0.RELEASE,6)") + versions.set("[5.0.0.RELEASE,)") assertInverse.set(true) extraDependency("io.projectreactor.ipc:reactor-netty:0.7.0.RELEASE") } diff --git a/instrumentation/spring/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/server/RouteOnSuccessOrError.java b/instrumentation/spring/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/server/RouteOnSuccess.java similarity index 82% rename from instrumentation/spring/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/server/RouteOnSuccessOrError.java rename to instrumentation/spring/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/server/RouteOnSuccess.java index 53f6e96c2abc..c1b2736bc0a2 100644 --- a/instrumentation/spring/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/server/RouteOnSuccessOrError.java +++ b/instrumentation/spring/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/server/RouteOnSuccess.java @@ -8,13 +8,13 @@ import io.opentelemetry.context.Context; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpRouteHolder; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpRouteSource; -import java.util.function.BiConsumer; +import java.util.function.Consumer; import java.util.regex.Pattern; import javax.annotation.Nullable; import org.springframework.web.reactive.function.server.HandlerFunction; import org.springframework.web.reactive.function.server.RouterFunction; -public class RouteOnSuccessOrError implements BiConsumer, Throwable> { +public class RouteOnSuccess implements Consumer> { private static final Pattern SPECIAL_CHARACTERS_REGEX = Pattern.compile("[()&|]"); private static final Pattern SPACES_REGEX = Pattern.compile("[ \\t]+"); @@ -23,15 +23,13 @@ public class RouteOnSuccessOrError implements BiConsumer, Thr @Nullable private final String route; - public RouteOnSuccessOrError(RouterFunction routerFunction) { + public RouteOnSuccess(RouterFunction routerFunction) { this.route = parseRoute(parsePredicateString(routerFunction)); } @Override - public void accept(HandlerFunction handler, Throwable throwable) { - if (handler != null) { - HttpRouteHolder.updateHttpRoute(Context.current(), HttpRouteSource.CONTROLLER, route); - } + public void accept(HandlerFunction handler) { + HttpRouteHolder.updateHttpRoute(Context.current(), HttpRouteSource.CONTROLLER, route); } private static String parsePredicateString(RouterFunction routerFunction) { diff --git a/instrumentation/spring/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/server/RouterFunctionInstrumentation.java b/instrumentation/spring/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/server/RouterFunctionInstrumentation.java index afa8ec70fd69..550e427918e9 100644 --- a/instrumentation/spring/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/server/RouterFunctionInstrumentation.java +++ b/instrumentation/spring/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/server/RouterFunctionInstrumentation.java @@ -67,7 +67,7 @@ public static void methodExit( @Advice.Return(readOnly = false) Mono> result, @Advice.Thrown Throwable throwable) { if (throwable == null) { - result = result.doOnSuccessOrError(new RouteOnSuccessOrError(thiz)); + result = result.doOnNext(new RouteOnSuccess(thiz)); } } } diff --git a/instrumentation/spring/spring-webflux-5.0/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/client/SpringWebfluxHttpAttributesGetter.java b/instrumentation/spring/spring-webflux-5.0/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/client/SpringWebfluxHttpAttributesGetter.java index 47933ed91b4b..624cb81ccea8 100644 --- a/instrumentation/spring/spring-webflux-5.0/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/client/SpringWebfluxHttpAttributesGetter.java +++ b/instrumentation/spring/spring-webflux-5.0/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/client/SpringWebfluxHttpAttributesGetter.java @@ -13,6 +13,7 @@ import java.lang.invoke.MethodType; import java.util.List; import javax.annotation.Nullable; +import org.springframework.http.HttpStatus; import org.springframework.web.reactive.function.client.ClientRequest; import org.springframework.web.reactive.function.client.ClientResponse; @@ -21,6 +22,7 @@ enum SpringWebfluxHttpAttributesGetter INSTANCE; private static final MethodHandle RAW_STATUS_CODE = findRawStatusCode(); + private static final MethodHandle FALLBACK_STATUS_CODE = findFallbackStatusCode(); // rawStatusCode() method was introduced in webflux 5.1 // prior to this method, the best we can get is HttpStatus enum, which only covers standard status @@ -34,6 +36,15 @@ private static MethodHandle findRawStatusCode() { } } + private static MethodHandle findFallbackStatusCode() { + try { + return MethodHandles.publicLookup() + .findVirtual(ClientResponse.class, "statusCode", MethodType.methodType(HttpStatus.class)); + } catch (IllegalAccessException | NoSuchMethodException e) { + return null; + } + } + @Override public String url(ClientRequest request) { return request.url().toString(); @@ -56,6 +67,7 @@ public List requestHeader(ClientRequest request, String name) { } @Override + @Nullable public Integer statusCode( ClientRequest request, ClientResponse response, @Nullable Throwable error) { if (RAW_STATUS_CODE != null) { @@ -67,8 +79,16 @@ public Integer statusCode( } } // prior to webflux 5.1, the best we can get is HttpStatus enum, which only covers standard - // status codes - return response.statusCode().value(); + // status codes (note: this method was removed in webflux 6.0) + if (FALLBACK_STATUS_CODE != null) { + try { + HttpStatus httpStatus = (HttpStatus) FALLBACK_STATUS_CODE.invokeExact(response); + return httpStatus.value(); + } catch (Throwable e) { + // Ignore + } + } + return null; } @Override From 12450e30f95431ddba3fc99df8e71748023bbe05 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Tue, 10 Jan 2023 18:25:45 -0800 Subject: [PATCH 05/10] switch --- .../reactor-netty-1.0/javaagent/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/build.gradle.kts b/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/build.gradle.kts index 3eac5b09ad01..68e48491aa9b 100644 --- a/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/build.gradle.kts +++ b/instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/build.gradle.kts @@ -3,10 +3,10 @@ plugins { } muzzle { - fail { + pass { group.set("io.projectreactor.netty") module.set("reactor-netty") - versions.set("[,1.0.0)") + versions.set("[1.0.0,)") assertInverse.set(true) excludeInstrumentationName("netty") } From f1cf68c3b73b138da24866076c0b64e4524cd4ba Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Wed, 11 Jan 2023 08:57:40 -0800 Subject: [PATCH 06/10] lambda --- ...ContextPropagationOperatorInstrumentationTest.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/instrumentation/reactor/reactor-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactor/ContextPropagationOperatorInstrumentationTest.java b/instrumentation/reactor/reactor-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactor/ContextPropagationOperatorInstrumentationTest.java index 835130d0a9c3..8375cf7256b3 100644 --- a/instrumentation/reactor/reactor-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactor/ContextPropagationOperatorInstrumentationTest.java +++ b/instrumentation/reactor/reactor-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactor/ContextPropagationOperatorInstrumentationTest.java @@ -174,7 +174,7 @@ void storeContextForcesItToBecomeCurrent() { interim = (Mono) MONO_CONTEXT_WRITE_METHOD.invoke( - interim, new StoreOpenTelemetryContext(Context.current().with(span))); + interim, new StoreOpenTelemetryContext(span)); } catch (Throwable e) { throw new RuntimeException(e); } @@ -196,15 +196,16 @@ void storeContextForcesItToBecomeCurrent() { private static class StoreOpenTelemetryContext implements Function { - private final Context tracingContext; + private final Span span; - private StoreOpenTelemetryContext(Context tracingContext) { - this.tracingContext = tracingContext; + private StoreOpenTelemetryContext(Span span) { + this.span = span; } @Override public reactor.util.context.Context apply(reactor.util.context.Context context) { - return ContextPropagationOperator.storeOpenTelemetryContext(context, tracingContext); + return ContextPropagationOperator.storeOpenTelemetryContext( + context, Context.current().with(span)); } } } From 8840f135de81226a6d22717137acab0abf421a04 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Wed, 11 Jan 2023 14:10:05 -0800 Subject: [PATCH 07/10] Looking ahead --- .../SpringWebfluxHttpAttributesGetter.java | 48 +------ .../spring/webflux/client/StatusCodes.java | 131 ++++++++++++++++++ 2 files changed, 132 insertions(+), 47 deletions(-) create mode 100644 instrumentation/spring/spring-webflux-5.0/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/client/StatusCodes.java diff --git a/instrumentation/spring/spring-webflux-5.0/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/client/SpringWebfluxHttpAttributesGetter.java b/instrumentation/spring/spring-webflux-5.0/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/client/SpringWebfluxHttpAttributesGetter.java index 624cb81ccea8..ecc98765def4 100644 --- a/instrumentation/spring/spring-webflux-5.0/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/client/SpringWebfluxHttpAttributesGetter.java +++ b/instrumentation/spring/spring-webflux-5.0/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/client/SpringWebfluxHttpAttributesGetter.java @@ -8,12 +8,8 @@ import static java.util.Collections.emptyList; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpClientAttributesGetter; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; import java.util.List; import javax.annotation.Nullable; -import org.springframework.http.HttpStatus; import org.springframework.web.reactive.function.client.ClientRequest; import org.springframework.web.reactive.function.client.ClientResponse; @@ -21,30 +17,6 @@ enum SpringWebfluxHttpAttributesGetter implements HttpClientAttributesGetter { INSTANCE; - private static final MethodHandle RAW_STATUS_CODE = findRawStatusCode(); - private static final MethodHandle FALLBACK_STATUS_CODE = findFallbackStatusCode(); - - // rawStatusCode() method was introduced in webflux 5.1 - // prior to this method, the best we can get is HttpStatus enum, which only covers standard status - // codes (see usage below) - private static MethodHandle findRawStatusCode() { - try { - return MethodHandles.publicLookup() - .findVirtual(ClientResponse.class, "rawStatusCode", MethodType.methodType(int.class)); - } catch (IllegalAccessException | NoSuchMethodException e) { - return null; - } - } - - private static MethodHandle findFallbackStatusCode() { - try { - return MethodHandles.publicLookup() - .findVirtual(ClientResponse.class, "statusCode", MethodType.methodType(HttpStatus.class)); - } catch (IllegalAccessException | NoSuchMethodException e) { - return null; - } - } - @Override public String url(ClientRequest request) { return request.url().toString(); @@ -70,25 +42,7 @@ public List requestHeader(ClientRequest request, String name) { @Nullable public Integer statusCode( ClientRequest request, ClientResponse response, @Nullable Throwable error) { - if (RAW_STATUS_CODE != null) { - // rawStatusCode() method was introduced in webflux 5.1 - try { - return (int) RAW_STATUS_CODE.invokeExact(response); - } catch (Throwable ignored) { - // Ignore - } - } - // prior to webflux 5.1, the best we can get is HttpStatus enum, which only covers standard - // status codes (note: this method was removed in webflux 6.0) - if (FALLBACK_STATUS_CODE != null) { - try { - HttpStatus httpStatus = (HttpStatus) FALLBACK_STATUS_CODE.invokeExact(response); - return httpStatus.value(); - } catch (Throwable e) { - // Ignore - } - } - return null; + return StatusCodes.get(response); } @Override diff --git a/instrumentation/spring/spring-webflux-5.0/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/client/StatusCodes.java b/instrumentation/spring/spring-webflux-5.0/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/client/StatusCodes.java new file mode 100644 index 000000000000..aa2f0702613b --- /dev/null +++ b/instrumentation/spring/spring-webflux-5.0/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/client/StatusCodes.java @@ -0,0 +1,131 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.spring.webflux.client; + +import static java.util.logging.Level.FINE; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.util.function.Function; +import java.util.logging.Logger; +import javax.annotation.Nullable; +import org.springframework.http.HttpStatus; +import org.springframework.web.reactive.function.client.ClientResponse; + +final class StatusCodes { + + private static final Logger logger = Logger.getLogger(StatusCodes.class.getName()); + + @Nullable + private static final Function statusCodeFunction = + getStatusCodeFunction(); + + @Nullable + static Integer get(ClientResponse response) { + if (statusCodeFunction == null) { + return null; + } + return statusCodeFunction.apply(response); + } + + @Nullable + private static Function getStatusCodeFunction() { + Function statusCodeFunction = getStatusCodeFunction60(); + if (statusCodeFunction != null) { + return statusCodeFunction; + } + statusCodeFunction = getStatusCodeFunction51(); + if (statusCodeFunction != null) { + return statusCodeFunction; + } + return getStatusCodeFunction50(); + } + + // in webflux 6.0, HttpStatusCode class was introduced, and statusCode() was changed to return + // HttpStatusCode instead of HttpStatus + @Nullable + private static Function getStatusCodeFunction60() { + MethodHandle statusCode; + MethodHandle value; + try { + Class httpStatusCodeClass = Class.forName("org.springframework.http.HttpStatusCode"); + statusCode = + MethodHandles.publicLookup() + .findVirtual( + ClientResponse.class, "statusCode", MethodType.methodType(httpStatusCodeClass)); + value = + MethodHandles.publicLookup() + .findVirtual(httpStatusCodeClass, "value", MethodType.methodType(int.class)); + } catch (ClassNotFoundException | IllegalAccessException | NoSuchMethodException e) { + return null; + } + + return response -> { + try { + Object httpStatusCode = statusCode.invoke(response); + return (int) value.invoke(httpStatusCode); + } catch (Throwable e) { + logger.log(FINE, e.getMessage(), e); + return null; + } + }; + } + + // in webflux 5.1, rawStatusCode() was introduced to retrieve the exact status code + // note: rawStatusCode() was deprecated in 6.0 + @Nullable + private static Function getStatusCodeFunction51() { + MethodHandle rawStatusCode; + try { + rawStatusCode = + MethodHandles.publicLookup() + .findVirtual(ClientResponse.class, "rawStatusCode", MethodType.methodType(int.class)); + } catch (IllegalAccessException | NoSuchMethodException e) { + return null; + } + + return response -> { + try { + return (int) rawStatusCode.invoke(response); + } catch (Throwable e) { + logger.log(FINE, e.getMessage(), e); + return null; + } + }; + } + + // in webflux 5.0, statusCode() returns HttpStatus, which only represents standard status codes + // (there's no way to capture arbitrary status codes) + @Nullable + private static Function getStatusCodeFunction50() { + MethodHandle statusCode; + MethodHandle value; + try { + statusCode = + MethodHandles.publicLookup() + .findVirtual( + ClientResponse.class, "statusCode", MethodType.methodType(HttpStatus.class)); + value = + MethodHandles.publicLookup() + .findVirtual(HttpStatus.class, "value", MethodType.methodType(int.class)); + } catch (IllegalAccessException | NoSuchMethodException e) { + return null; + } + + return response -> { + try { + Object httpStatusCode = statusCode.invoke(response); + return (int) value.invoke(httpStatusCode); + } catch (Throwable e) { + logger.log(FINE, e.getMessage(), e); + return null; + } + }; + } + + private StatusCodes() {} +} From 949b222e7c94e01c6938a72a56865c4983e12196 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Wed, 11 Jan 2023 14:12:11 -0800 Subject: [PATCH 08/10] Update docs --- docs/supported-libraries.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/supported-libraries.md b/docs/supported-libraries.md index e4266c8049a8..61fd72a5cc87 100644 --- a/docs/supported-libraries.md +++ b/docs/supported-libraries.md @@ -118,7 +118,7 @@ These are the supported libraries and frameworks: | [Spring RestTemplate](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/client/package-summary.html) | 3.1+ | [opentelemetry-spring-web-3.1](../instrumentation/spring/spring-web/spring-web-3.1/library) | [HTTP Client Spans], [HTTP Client Metrics] | | [Spring Web MVC](https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/servlet/mvc/package-summary.html) | 3.1+ | [opentelemetry-spring-webmvc-5.3](../instrumentation/spring/spring-webmvc/spring-webmvc-5.3/library) | [HTTP Server Spans], [HTTP Server Metrics] | | [Spring Web Services](https://spring.io/projects/spring-ws) | 2.0+ | N/A | none | -| [Spring WebFlux](https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/reactive/package-summary.html) | 5.0+ (not including 6.0+ yet) | [opentelemetry-spring-webflux-5.0](../instrumentation/spring/spring-webflux-5.0/library) | [HTTP Client Spans], [HTTP Client Metrics], | +| [Spring WebFlux](https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/reactive/package-summary.html) | 5.0+ | [opentelemetry-spring-webflux-5.0](../instrumentation/spring/spring-webflux-5.0/library) | [HTTP Client Spans], [HTTP Client Metrics], | | [Spymemcached](https://github.com/couchbase/spymemcached) | 2.12+ | N/A | [Database Client Spans] | | [Tomcat JDBC Pool](https://tomcat.apache.org/tomcat-7.0-doc/jdbc-pool.html) | 8.5.0+ | N/A | [Database Pool Metrics] | | [Twilio](https://github.com/twilio/twilio-java) | 6.6+ (not including 8.x yet) | N/A | none | From c11683846dad62217b13edfde4a42d90750ad288 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Wed, 11 Jan 2023 15:51:49 -0800 Subject: [PATCH 09/10] Test against WebFlux 6 --- dependencyManagement/build.gradle.kts | 2 +- .../javaagent/build.gradle.kts | 28 ++++++++++++------- .../src/test/groovy/SpringWebfluxTest.groovy | 6 ++-- 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/dependencyManagement/build.gradle.kts b/dependencyManagement/build.gradle.kts index 6c278285ca53..e14f874fe56e 100644 --- a/dependencyManagement/build.gradle.kts +++ b/dependencyManagement/build.gradle.kts @@ -33,7 +33,7 @@ val DEPENDENCY_BOMS = listOf( "io.opentelemetry:opentelemetry-bom-alpha:1.22.0-alpha", "org.junit:junit-bom:5.9.2", "org.testcontainers:testcontainers-bom:1.17.6", - "org.spockframework:spock-bom:2.3-groovy-4.0" + "org.spockframework:spock-bom:2.4-M1-groovy-4.0" ) val autoServiceVersion = "1.0.1" diff --git a/instrumentation/spring/spring-webflux-5.0/javaagent/build.gradle.kts b/instrumentation/spring/spring-webflux-5.0/javaagent/build.gradle.kts index a01cf564f349..b07099e9fb3c 100644 --- a/instrumentation/spring/spring-webflux-5.0/javaagent/build.gradle.kts +++ b/instrumentation/spring/spring-webflux-5.0/javaagent/build.gradle.kts @@ -44,6 +44,9 @@ dependencies { compileOnly("org.springframework:spring-webflux:5.0.0.RELEASE") compileOnly("io.projectreactor.ipc:reactor-netty:0.7.0.RELEASE") + // this is needed to pick up SpringCoreIgnoredTypesConfigurer + testInstrumentation(project(":instrumentation:spring:spring-core-2.0:javaagent")) + testInstrumentation(project(":instrumentation:netty:netty-4.1:javaagent")) testInstrumentation(project(":instrumentation:reactor:reactor-3.1:javaagent")) testInstrumentation(project(":instrumentation:reactor:reactor-netty:reactor-netty-1.0:javaagent")) @@ -56,11 +59,7 @@ dependencies { testLibrary("org.springframework.boot:spring-boot-starter-webflux:2.0.0.RELEASE") testLibrary("org.springframework.boot:spring-boot-starter-test:2.0.0.RELEASE") testLibrary("org.springframework.boot:spring-boot-starter-reactor-netty:2.0.0.RELEASE") - testImplementation("org.spockframework:spock-spring:1.1-groovy-2.4") - - latestDepTestLibrary("org.springframework.boot:spring-boot-starter-webflux:2.+") - latestDepTestLibrary("org.springframework.boot:spring-boot-starter-test:2.+") - latestDepTestLibrary("org.springframework.boot:spring-boot-starter-reactor-netty:2.+") + testImplementation("org.spockframework:spock-spring:2.4-M1-groovy-4.0") } tasks.withType().configureEach { @@ -73,10 +72,19 @@ tasks.withType().configureEach { systemProperty("testLatestDeps", findProperty("testLatestDeps") as Boolean) } -configurations.testRuntimeClasspath { - resolutionStrategy { - // requires old logback (and therefore also old slf4j) - force("ch.qos.logback:logback-classic:1.2.11") - force("org.slf4j:slf4j-api:1.7.36") +val latestDepTest = findProperty("testLatestDeps") as Boolean + +if (latestDepTest) { + // spring 6 requires java 17 + otelJava { + minJavaVersionSupported.set(JavaVersion.VERSION_17) + } +} else { + // spring 5 requires old logback (and therefore also old slf4j) + configurations.testRuntimeClasspath { + resolutionStrategy { + force("ch.qos.logback:logback-classic:1.2.11") + force("org.slf4j:slf4j-api:1.7.36") + } } } diff --git a/instrumentation/spring/spring-webflux-5.0/javaagent/src/test/groovy/SpringWebfluxTest.groovy b/instrumentation/spring/spring-webflux-5.0/javaagent/src/test/groovy/SpringWebfluxTest.groovy index 7cb61ce83b1c..da8adf23a9c4 100644 --- a/instrumentation/spring/spring-webflux-5.0/javaagent/src/test/groovy/SpringWebfluxTest.groovy +++ b/instrumentation/spring/spring-webflux-5.0/javaagent/src/test/groovy/SpringWebfluxTest.groovy @@ -3,6 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ + import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification import io.opentelemetry.semconv.trace.attributes.SemanticAttributes import io.opentelemetry.testing.internal.armeria.client.ClientRequestContext @@ -12,10 +13,10 @@ import io.opentelemetry.testing.internal.armeria.client.WebClient import io.opentelemetry.testing.internal.armeria.common.HttpHeaderNames import io.opentelemetry.testing.internal.armeria.common.HttpRequest import io.opentelemetry.testing.internal.armeria.common.HttpResponse +import org.springframework.beans.factory.annotation.Value import org.springframework.boot.test.context.SpringBootTest import org.springframework.boot.test.context.TestConfiguration import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory -import org.springframework.boot.web.server.LocalServerPort import org.springframework.context.annotation.Bean import org.springframework.web.server.ResponseStatusException import server.EchoHandlerFunction @@ -43,7 +44,8 @@ class SpringWebfluxTest extends AgentInstrumentationSpecification { static final String INNER_HANDLER_FUNCTION_CLASS_TAG_PREFIX = SpringWebFluxTestApplication.getName() + "\$" static final String SPRING_APP_CLASS_ANON_NESTED_CLASS_PREFIX = SpringWebFluxTestApplication.getSimpleName() + "\$" - @LocalServerPort + // can't use @LocalServerPort annotation since it moved packages between Spring Boot 2 and 3 + @Value("\${local.server.port}") int port WebClient client From 9232dacc0354423707079bd96ca547d3f0cc3d53 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Wed, 11 Jan 2023 17:15:39 -0800 Subject: [PATCH 10/10] spotless --- .../javaagent/src/test/groovy/SpringWebfluxTest.groovy | 1 - 1 file changed, 1 deletion(-) diff --git a/instrumentation/spring/spring-webflux-5.0/javaagent/src/test/groovy/SpringWebfluxTest.groovy b/instrumentation/spring/spring-webflux-5.0/javaagent/src/test/groovy/SpringWebfluxTest.groovy index da8adf23a9c4..83746aae8e3e 100644 --- a/instrumentation/spring/spring-webflux-5.0/javaagent/src/test/groovy/SpringWebfluxTest.groovy +++ b/instrumentation/spring/spring-webflux-5.0/javaagent/src/test/groovy/SpringWebfluxTest.groovy @@ -3,7 +3,6 @@ * SPDX-License-Identifier: Apache-2.0 */ - import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification import io.opentelemetry.semconv.trace.attributes.SemanticAttributes import io.opentelemetry.testing.internal.armeria.client.ClientRequestContext