-
Notifications
You must be signed in to change notification settings - Fork 873
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Update webflux to Instrumenter API (and improvements/simplifications) #3798
Changes from 8 commits
c5ebca5
6695abc
bc9f077
e56b099
d3c4a05
9df4a14
e95e964
c152c6d
26f36fb
d5a9bd8
72a27e3
df2cfa8
8f6cd24
111a885
04e4bf3
821b1b5
20db675
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,16 +5,12 @@ | |
|
||
package io.opentelemetry.javaagent.instrumentation.spring.webflux.server; | ||
|
||
import static io.opentelemetry.javaagent.instrumentation.spring.webflux.server.SpringWebfluxHttpServerTracer.tracer; | ||
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.takesArgument; | ||
import static net.bytebuddy.matcher.ElementMatchers.takesArguments; | ||
|
||
import io.opentelemetry.api.trace.SpanKind; | ||
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; | ||
|
@@ -41,41 +37,20 @@ public void transform(TypeTransformer transformer) { | |
this.getClass().getName() + "$HandleAdvice"); | ||
} | ||
|
||
/** | ||
* This is 'top level' advice for Webflux instrumentation. This handles creating and finishing | ||
* Webflux span. | ||
*/ | ||
@SuppressWarnings("unused") | ||
public static class HandleAdvice { | ||
|
||
@Advice.OnMethodEnter(suppress = Throwable.class) | ||
public static void methodEnter( | ||
@Advice.Argument(0) ServerWebExchange exchange, | ||
@Advice.Local("otelScope") Scope otelScope, | ||
@Advice.Local("otelContext") Context otelContext) { | ||
|
||
otelContext = tracer().startSpan("DispatcherHandler.handle", SpanKind.INTERNAL); | ||
// Unfortunately Netty EventLoop is not instrumented well enough to attribute all work to the | ||
// right things so we have to store the context in request itself. | ||
exchange.getAttributes().put(AdviceUtils.CONTEXT_ATTRIBUTE, otelContext); | ||
|
||
otelScope = otelContext.makeCurrent(); | ||
} | ||
|
||
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) | ||
public static void methodExit( | ||
@Advice.Thrown Throwable throwable, | ||
@Advice.Argument(0) ServerWebExchange exchange, | ||
@Advice.Return(readOnly = false) Mono<Void> mono, | ||
@Advice.Local("otelScope") Scope otelScope, | ||
@Advice.Local("otelContext") Context otelContext) { | ||
if (throwable == null && mono != null) { | ||
mono = AdviceUtils.setPublisherSpan(mono, otelContext); | ||
} else if (throwable != null) { | ||
AdviceUtils.finishSpanIfPresent(exchange, throwable); | ||
@Advice.Return(readOnly = false) Mono<Void> mono) { | ||
if (mono != null) { | ||
// note: it seems like this code should go in HandleAdvice @OnMethodExit | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you mean Enter? This code seems to be in HandleAdvice.OnMethodExit right now There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. oh no, two different |
||
// but for some reason "GET to bad endpoint annotation API fail Mono" test fails | ||
// with that placement | ||
mono = AdviceUtils.end(mono, exchange); | ||
} | ||
otelScope.close(); | ||
// span finished in SpanFinishingSubscriber | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,7 @@ | |
|
||
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; | ||
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface; | ||
import static io.opentelemetry.javaagent.instrumentation.spring.webflux.server.WebfluxSingletons.instrumenter; | ||
import static net.bytebuddy.matcher.ElementMatchers.isAbstract; | ||
import static net.bytebuddy.matcher.ElementMatchers.isMethod; | ||
import static net.bytebuddy.matcher.ElementMatchers.isPublic; | ||
|
@@ -64,55 +65,70 @@ public static class HandleAdvice { | |
public static void methodEnter( | ||
@Advice.Argument(0) ServerWebExchange exchange, | ||
@Advice.Argument(1) Object handler, | ||
@Advice.Local("otelContext") Context context, | ||
@Advice.Local("otelScope") Scope scope) { | ||
|
||
Context context = exchange.getAttribute(AdviceUtils.CONTEXT_ATTRIBUTE); | ||
if (handler != null && context != null) { | ||
Span span = Span.fromContext(context); | ||
String handlerType; | ||
String spanName; | ||
|
||
if (handler instanceof HandlerMethod) { | ||
// Special case for requests mapped with annotations | ||
HandlerMethod handlerMethod = (HandlerMethod) handler; | ||
spanName = SpanNames.fromMethod(handlerMethod.getMethod()); | ||
handlerType = handlerMethod.getMethod().getDeclaringClass().getName(); | ||
} else { | ||
spanName = AdviceUtils.spanNameForHandler(handler); | ||
handlerType = handler.getClass().getName(); | ||
} | ||
|
||
span.updateName(spanName); | ||
if (SpringWebfluxConfig.captureExperimentalSpanAttributes()) { | ||
span.setAttribute("spring-webflux.handler.type", handlerType); | ||
} | ||
|
||
scope = context.makeCurrent(); | ||
Context parentContext = Context.current(); | ||
|
||
Span serverSpan = ServerSpan.fromContextOrNull(parentContext); | ||
|
||
PathPattern bestPattern = | ||
exchange.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE); | ||
if (serverSpan != null && bestPattern != null) { | ||
serverSpan.updateName( | ||
ServletContextPath.prepend(Context.current(), bestPattern.toString())); | ||
} | ||
|
||
if (handler == null) { | ||
return; | ||
} | ||
|
||
if (context != null) { | ||
Span serverSpan = ServerSpan.fromContextOrNull(context); | ||
if (!instrumenter().shouldStart(parentContext, null)) { | ||
return; | ||
} | ||
|
||
context = instrumenter().start(parentContext, null); | ||
|
||
Span span = Span.fromContext(context); | ||
String handlerType; | ||
String spanName; | ||
|
||
PathPattern bestPattern = | ||
exchange.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE); | ||
if (serverSpan != null && bestPattern != null) { | ||
serverSpan.updateName( | ||
ServletContextPath.prepend(Context.current(), bestPattern.toString())); | ||
} | ||
if (handler instanceof HandlerMethod) { | ||
// Special case for requests mapped with annotations | ||
HandlerMethod handlerMethod = (HandlerMethod) handler; | ||
spanName = SpanNames.fromMethod(handlerMethod.getMethod()); | ||
handlerType = handlerMethod.getMethod().getDeclaringClass().getName(); | ||
} else { | ||
spanName = AdviceUtils.spanNameForHandler(handler); | ||
handlerType = handler.getClass().getName(); | ||
} | ||
|
||
span.updateName(spanName); | ||
if (SpringWebfluxConfig.captureExperimentalSpanAttributes()) { | ||
span.setAttribute("spring-webflux.handler.type", handlerType); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could we move that code inside |
||
|
||
scope = context.makeCurrent(); | ||
} | ||
|
||
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) | ||
public static void methodExit( | ||
@Advice.Argument(0) ServerWebExchange exchange, | ||
@Advice.Thrown Throwable throwable, | ||
@Advice.Local("otelContext") Context context, | ||
@Advice.Local("otelScope") Scope scope) { | ||
if (throwable != null) { | ||
AdviceUtils.finishSpanIfPresent(exchange, throwable); | ||
if (scope == null) { | ||
return; | ||
} | ||
if (scope != null) { | ||
scope.close(); | ||
// span finished in SpanFinishingSubscriber | ||
scope.close(); | ||
|
||
if (throwable != null) { | ||
instrumenter().end(context, null, null, throwable); | ||
} else { | ||
exchange.getAttributes().put(AdviceUtils.CONTEXT_ATTRIBUTE, context); | ||
// span finished by wrapped Mono in DispatcherHandlerInstrumentation | ||
// the Mono is already wrapped at this point, but doesn't read the CONTEXT_ATTRIBUTE until | ||
// the Mono is resolved, which is after this point | ||
} | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tried it, strict context check still fails. I believe we need to wait for netty request processing to complete before doing context leaks check. I'll create a pr for this.