Refactor `ServerSpanNaming` (in preparation for `http.route`) (#4852)

* Refactor ServerSpanNaming (in preparation for http.route)

* fix tests

* Add ServerSpanNaming to all HTTP server instrumentations

* fix tests
This commit is contained in:
Mateusz Rzeszutek 2021-12-14 10:11:20 +01:00 committed by GitHub
parent 331ce287d2
commit a65e9633ba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 102 additions and 263 deletions

View File

@ -8,6 +8,7 @@ package io.opentelemetry.instrumentation.api.servlet;
import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context; import io.opentelemetry.context.Context;
import io.opentelemetry.context.ContextKey; import io.opentelemetry.context.ContextKey;
import io.opentelemetry.instrumentation.api.instrumenter.ContextCustomizer;
import io.opentelemetry.instrumentation.api.tracer.ServerSpan; import io.opentelemetry.instrumentation.api.tracer.ServerSpan;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -17,14 +18,13 @@ public final class ServerSpanNaming {
private static final ContextKey<ServerSpanNaming> CONTEXT_KEY = private static final ContextKey<ServerSpanNaming> CONTEXT_KEY =
ContextKey.named("opentelemetry-servlet-span-naming-key"); ContextKey.named("opentelemetry-servlet-span-naming-key");
public static Context init(Context context, Source initialSource) { public static <REQUEST> ContextCustomizer<REQUEST> get() {
ServerSpanNaming serverSpanNaming = context.get(CONTEXT_KEY); return (context, request, startAttributes) -> {
if (serverSpanNaming != null) { if (context.get(CONTEXT_KEY) != null) {
// TODO (trask) does this ever happen?
serverSpanNaming.updatedBySource = initialSource;
return context; return context;
} }
return context.with(CONTEXT_KEY, new ServerSpanNaming(initialSource)); return context.with(CONTEXT_KEY, new ServerSpanNaming(Source.CONTAINER));
};
} }
private volatile Source updatedBySource; private volatile Source updatedBySource;
@ -37,16 +37,15 @@ public final class ServerSpanNaming {
} }
/** /**
* If there is a server span in the context, and {@link #init(Context, Source)} has been called to * If there is a server span in the context, and the context has been customized with a {@code
* populate a {@code ServerSpanName} into the context, then this method will update the server * ServerSpanName}, then this method will update the server span name using the provided {@link
* span name using the provided {@link ServerSpanNameSupplier} if and only if the last {@link * ServerSpanNameSupplier} if and only if the last {@link Source} to update the span name using
* Source} to update the span name using this method has strictly lower priority than the provided * this method has strictly lower priority than the provided {@link Source}, and the value
* {@link Source}, and the value returned from the {@link ServerSpanNameSupplier} is non-null. * returned from the {@link ServerSpanNameSupplier} is non-null.
* *
* <p>If there is a server span in the context, and {@link #init(Context, Source)} has NOT been * <p>If there is a server span in the context, and the context has NOT been customized with a
* called to populate a {@code ServerSpanName} into the context, then this method will update the * {@code ServerSpanName}, then this method will update the server span name using the provided
* server span name using the provided {@link ServerSpanNameSupplier} if the value returned from * {@link ServerSpanNameSupplier} if the value returned from it is non-null.
* it is non-null.
*/ */
public static <T> void updateServerSpanName( public static <T> void updateServerSpanName(
Context context, Source source, ServerSpanNameSupplier<T> serverSpanName, T arg1) { Context context, Source source, ServerSpanNameSupplier<T> serverSpanName, T arg1) {
@ -54,17 +53,15 @@ public final class ServerSpanNaming {
} }
/** /**
* If there is a server span in the context, and {@link #init(Context, Source)} has been called to * If there is a server span in the context, and the context has been customized with a {@code
* populate a {@code ServerSpanName} into the context, then this method will update the server * ServerSpanName}, then this method will update the server span name using the provided {@link
* span name using the provided {@link ServerSpanNameTwoArgSupplier} if and only if the last * ServerSpanNameTwoArgSupplier} if and only if the last {@link Source} to update the span name
* {@link Source} to update the span name using this method has strictly lower priority than the * using this method has strictly lower priority than the provided {@link Source}, and the value
* provided {@link Source}, and the value returned from the {@link ServerSpanNameTwoArgSupplier} * returned from the {@link ServerSpanNameTwoArgSupplier} is non-null.
* is non-null.
* *
* <p>If there is a server span in the context, and {@link #init(Context, Source)} has NOT been * <p>If there is a server span in the context, and the context has NOT been customized with a
* called to populate a {@code ServerSpanName} into the context, then this method will update the * {@code ServerSpanName}, then this method will update the server span name using the provided
* server span name using the provided {@link ServerSpanNameTwoArgSupplier} if the value returned * {@link ServerSpanNameTwoArgSupplier} if the value returned from it is non-null.
* from it is non-null.
*/ */
public static <T, U> void updateServerSpanName( public static <T, U> void updateServerSpanName(
Context context, Context context,

View File

@ -11,6 +11,7 @@ import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerMetrics; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerMetrics;
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor;
import io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming;
import io.opentelemetry.javaagent.instrumentation.akkahttp.AkkaHttpUtil; import io.opentelemetry.javaagent.instrumentation.akkahttp.AkkaHttpUtil;
public class AkkaHttpServerSingletons { public class AkkaHttpServerSingletons {
@ -28,6 +29,7 @@ public class AkkaHttpServerSingletons {
.setSpanStatusExtractor(HttpSpanStatusExtractor.create(httpAttributesExtractor)) .setSpanStatusExtractor(HttpSpanStatusExtractor.create(httpAttributesExtractor))
.addAttributesExtractor(httpAttributesExtractor) .addAttributesExtractor(httpAttributesExtractor)
.addRequestMetrics(HttpServerMetrics.get()) .addRequestMetrics(HttpServerMetrics.get())
.addContextCustomizer(ServerSpanNaming.get())
.newServerInstrumenter(AkkaHttpServerHeaders.INSTANCE); .newServerInstrumenter(AkkaHttpServerHeaders.INSTANCE);
} }

View File

@ -21,6 +21,7 @@ import io.opentelemetry.instrumentation.api.instrumenter.http.HttpClientMetrics;
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerMetrics; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerMetrics;
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor;
import io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming;
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -135,7 +136,8 @@ public final class ArmeriaTracingBuilder {
HttpSpanStatusExtractor.create(serverAttributesExtractor))) HttpSpanStatusExtractor.create(serverAttributesExtractor)))
.addAttributesExtractor(new ArmeriaNetServerAttributesExtractor()) .addAttributesExtractor(new ArmeriaNetServerAttributesExtractor())
.addAttributesExtractor(serverAttributesExtractor) .addAttributesExtractor(serverAttributesExtractor)
.addRequestMetrics(HttpServerMetrics.get()); .addRequestMetrics(HttpServerMetrics.get())
.addContextCustomizer(ServerSpanNaming.get());
if (peerService != null) { if (peerService != null) {
clientInstrumenterBuilder.addAttributesExtractor( clientInstrumenterBuilder.addAttributesExtractor(

View File

@ -32,10 +32,8 @@ public final class GrizzlySingletons {
.addAttributesExtractor(netAttributesExtractor) .addAttributesExtractor(netAttributesExtractor)
.addRequestMetrics(HttpServerMetrics.get()) .addRequestMetrics(HttpServerMetrics.get())
.addContextCustomizer( .addContextCustomizer(
(context, httpRequestPacket, startAttributes) -> { (context, httpRequestPacket, startAttributes) -> GrizzlyErrorHolder.init(context))
context = GrizzlyErrorHolder.init(context); .addContextCustomizer(ServerSpanNaming.get())
return ServerSpanNaming.init(context, ServerSpanNaming.Source.CONTAINER);
})
.newServerInstrumenter(HttpRequestHeadersGetter.INSTANCE); .newServerInstrumenter(HttpRequestHeadersGetter.INSTANCE);
} }

View File

@ -5,10 +5,7 @@
package io.opentelemetry.javaagent.instrumentation.jetty.v11_0; package io.opentelemetry.javaagent.instrumentation.jetty.v11_0;
import static io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming.Source.CONTAINER;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming;
import io.opentelemetry.javaagent.bootstrap.servlet.AppServerBridge; import io.opentelemetry.javaagent.bootstrap.servlet.AppServerBridge;
import io.opentelemetry.javaagent.instrumentation.jetty.common.JettyHelper; import io.opentelemetry.javaagent.instrumentation.jetty.common.JettyHelper;
import io.opentelemetry.javaagent.instrumentation.servlet.ServletInstrumenterBuilder; import io.opentelemetry.javaagent.instrumentation.servlet.ServletInstrumenterBuilder;
@ -26,10 +23,7 @@ public final class Jetty11Singletons {
INSTRUMENTER = INSTRUMENTER =
ServletInstrumenterBuilder.<HttpServletRequest, HttpServletResponse>create() ServletInstrumenterBuilder.<HttpServletRequest, HttpServletResponse>create()
.addContextCustomizer( .addContextCustomizer(
(context, request, attributes) -> { (context, request, attributes) -> new AppServerBridge.Builder().init(context))
context = ServerSpanNaming.init(context, CONTAINER);
return new AppServerBridge.Builder().init(context);
})
.build(INSTRUMENTATION_NAME, Servlet5Accessor.INSTANCE); .build(INSTRUMENTATION_NAME, Servlet5Accessor.INSTANCE);
private static final JettyHelper<HttpServletRequest, HttpServletResponse> HELPER = private static final JettyHelper<HttpServletRequest, HttpServletResponse> HELPER =

View File

@ -5,10 +5,7 @@
package io.opentelemetry.javaagent.instrumentation.jetty.v8_0; package io.opentelemetry.javaagent.instrumentation.jetty.v8_0;
import static io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming.Source.CONTAINER;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming;
import io.opentelemetry.javaagent.bootstrap.servlet.AppServerBridge; import io.opentelemetry.javaagent.bootstrap.servlet.AppServerBridge;
import io.opentelemetry.javaagent.instrumentation.jetty.common.JettyHelper; import io.opentelemetry.javaagent.instrumentation.jetty.common.JettyHelper;
import io.opentelemetry.javaagent.instrumentation.servlet.ServletInstrumenterBuilder; import io.opentelemetry.javaagent.instrumentation.servlet.ServletInstrumenterBuilder;
@ -26,10 +23,7 @@ public final class Jetty8Singletons {
INSTRUMENTER = INSTRUMENTER =
ServletInstrumenterBuilder.<HttpServletRequest, HttpServletResponse>create() ServletInstrumenterBuilder.<HttpServletRequest, HttpServletResponse>create()
.addContextCustomizer( .addContextCustomizer(
(context, request, attributes) -> { (context, request, attributes) -> new AppServerBridge.Builder().init(context))
context = ServerSpanNaming.init(context, CONTAINER);
return new AppServerBridge.Builder().init(context);
})
.build(INSTRUMENTATION_NAME, Servlet3Accessor.INSTANCE); .build(INSTRUMENTATION_NAME, Servlet3Accessor.INSTANCE);
private static final JettyHelper<HttpServletRequest, HttpServletResponse> HELPER = private static final JettyHelper<HttpServletRequest, HttpServletResponse> HELPER =

View File

@ -5,8 +5,6 @@
package io.opentelemetry.javaagent.instrumentation.liberty.dispatcher; package io.opentelemetry.javaagent.instrumentation.liberty.dispatcher;
import static io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming.Source.CONTAINER;
import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor;
@ -39,8 +37,7 @@ public final class LibertyDispatcherSingletons {
.setSpanStatusExtractor(spanStatusExtractor) .setSpanStatusExtractor(spanStatusExtractor)
.addAttributesExtractor(httpAttributesExtractor) .addAttributesExtractor(httpAttributesExtractor)
.addAttributesExtractor(netAttributesExtractor) .addAttributesExtractor(netAttributesExtractor)
.addContextCustomizer( .addContextCustomizer(ServerSpanNaming.get())
(context, request, attributes) -> ServerSpanNaming.init(context, CONTAINER))
.addRequestMetrics(HttpServerMetrics.get()) .addRequestMetrics(HttpServerMetrics.get())
.newServerInstrumenter(LibertyDispatcherRequestGetter.INSTANCE); .newServerInstrumenter(LibertyDispatcherRequestGetter.INSTANCE);
} }

View File

@ -5,10 +5,7 @@
package io.opentelemetry.javaagent.instrumentation.liberty; package io.opentelemetry.javaagent.instrumentation.liberty;
import static io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming.Source.CONTAINER;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming;
import io.opentelemetry.javaagent.bootstrap.servlet.AppServerBridge; import io.opentelemetry.javaagent.bootstrap.servlet.AppServerBridge;
import io.opentelemetry.javaagent.instrumentation.servlet.ServletInstrumenterBuilder; import io.opentelemetry.javaagent.instrumentation.servlet.ServletInstrumenterBuilder;
import io.opentelemetry.javaagent.instrumentation.servlet.ServletRequestContext; import io.opentelemetry.javaagent.instrumentation.servlet.ServletRequestContext;
@ -25,10 +22,8 @@ public final class LibertySingletons {
INSTRUMENTER = INSTRUMENTER =
ServletInstrumenterBuilder.<HttpServletRequest, HttpServletResponse>create() ServletInstrumenterBuilder.<HttpServletRequest, HttpServletResponse>create()
.addContextCustomizer( .addContextCustomizer(
(context, request, attributes) -> { (context, request, attributes) ->
context = ServerSpanNaming.init(context, CONTAINER); new AppServerBridge.Builder().recordException().init(context))
return new AppServerBridge.Builder().recordException().init(context);
})
.build(INSTRUMENTATION_NAME, Servlet3Accessor.INSTANCE); .build(INSTRUMENTATION_NAME, Servlet3Accessor.INSTANCE);
private static final LibertyHelper<HttpServletRequest, HttpServletResponse> HELPER = private static final LibertyHelper<HttpServletRequest, HttpServletResponse> HELPER =

View File

@ -10,6 +10,7 @@ import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerMetrics; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerMetrics;
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor;
import io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming;
import io.opentelemetry.javaagent.instrumentation.netty.common.NettyErrorHolder; import io.opentelemetry.javaagent.instrumentation.netty.common.NettyErrorHolder;
import io.opentelemetry.javaagent.instrumentation.netty.v3_8.HttpRequestAndChannel; import io.opentelemetry.javaagent.instrumentation.netty.v3_8.HttpRequestAndChannel;
import org.jboss.netty.handler.codec.http.HttpResponse; import org.jboss.netty.handler.codec.http.HttpResponse;
@ -33,6 +34,7 @@ final class NettyServerSingletons {
.addRequestMetrics(HttpServerMetrics.get()) .addRequestMetrics(HttpServerMetrics.get())
.addContextCustomizer( .addContextCustomizer(
(context, requestAndChannel, startAttributes) -> NettyErrorHolder.init(context)) (context, requestAndChannel, startAttributes) -> NettyErrorHolder.init(context))
.addContextCustomizer(ServerSpanNaming.get())
.newServerInstrumenter(NettyHeadersGetter.INSTANCE); .newServerInstrumenter(NettyHeadersGetter.INSTANCE);
} }

View File

@ -31,12 +31,8 @@ public final class NettyServerInstrumenterFactory {
.addAttributesExtractor(httpAttributesExtractor) .addAttributesExtractor(httpAttributesExtractor)
.addAttributesExtractor(new NettyNetServerAttributesExtractor()) .addAttributesExtractor(new NettyNetServerAttributesExtractor())
.addRequestMetrics(HttpServerMetrics.get()) .addRequestMetrics(HttpServerMetrics.get())
.addContextCustomizer( .addContextCustomizer((context, request, attributes) -> NettyErrorHolder.init(context))
(context, request, attributes) -> { .addContextCustomizer(ServerSpanNaming.get())
context = NettyErrorHolder.init(context);
// netty is not exactly a "container", but it's the best match out of these
return ServerSpanNaming.init(context, ServerSpanNaming.Source.CONTAINER);
})
.newServerInstrumenter(HttpRequestHeadersGetter.INSTANCE); .newServerInstrumenter(HttpRequestHeadersGetter.INSTANCE);
} }

View File

@ -5,12 +5,9 @@
package io.opentelemetry.javaagent.instrumentation.servlet.v2_2; package io.opentelemetry.javaagent.instrumentation.servlet.v2_2;
import static io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming.Source.SERVLET;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerAttributesExtractor;
import io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming;
import io.opentelemetry.javaagent.instrumentation.servlet.ServletInstrumenterBuilder; import io.opentelemetry.javaagent.instrumentation.servlet.ServletInstrumenterBuilder;
import io.opentelemetry.javaagent.instrumentation.servlet.ServletRequestContext; import io.opentelemetry.javaagent.instrumentation.servlet.ServletRequestContext;
import io.opentelemetry.javaagent.instrumentation.servlet.ServletResponseContext; import io.opentelemetry.javaagent.instrumentation.servlet.ServletResponseContext;
@ -33,8 +30,6 @@ public final class Servlet2Singletons {
ServletRequestContext<HttpServletRequest>, ServletResponseContext<HttpServletResponse>> ServletRequestContext<HttpServletRequest>, ServletResponseContext<HttpServletResponse>>
instrumenter = instrumenter =
ServletInstrumenterBuilder.<HttpServletRequest, HttpServletResponse>create() ServletInstrumenterBuilder.<HttpServletRequest, HttpServletResponse>create()
.addContextCustomizer(
(context, request, attributes) -> ServerSpanNaming.init(context, SERVLET))
.build( .build(
INSTRUMENTATION_NAME, INSTRUMENTATION_NAME,
Servlet2Accessor.INSTANCE, Servlet2Accessor.INSTANCE,

View File

@ -9,7 +9,6 @@ import static io.opentelemetry.javaagent.instrumentation.servlet.v3_0.Servlet3Si
import io.opentelemetry.context.Context; import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope; import io.opentelemetry.context.Scope;
import io.opentelemetry.instrumentation.api.tracer.ServerSpan;
import io.opentelemetry.javaagent.bootstrap.servlet.AppServerBridge; import io.opentelemetry.javaagent.bootstrap.servlet.AppServerBridge;
import io.opentelemetry.javaagent.bootstrap.servlet.MappingResolver; import io.opentelemetry.javaagent.bootstrap.servlet.MappingResolver;
import io.opentelemetry.javaagent.instrumentation.api.CallDepth; import io.opentelemetry.javaagent.instrumentation.api.CallDepth;
@ -39,25 +38,31 @@ public class Servlet3Advice {
if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) { if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) {
return; return;
} }
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
callDepth = CallDepth.forClass(AppServerBridge.getCallDepthKey()); callDepth = CallDepth.forClass(AppServerBridge.getCallDepthKey());
callDepth.getAndIncrement(); callDepth.getAndIncrement();
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
Context currentContext = Java8BytecodeBridge.currentContext(); Context currentContext = Java8BytecodeBridge.currentContext();
Context attachedContext = helper().getServerContext(httpServletRequest); Context attachedContext = helper().getServerContext(httpServletRequest);
if (attachedContext != null && helper().needsRescoping(currentContext, attachedContext)) { Context contextToUpdate;
MappingResolver mappingResolver = Servlet3Singletons.getMappingResolver(servletOrFilter);
boolean servlet = servletOrFilter instanceof Servlet; requestContext = new ServletRequestContext<>(httpServletRequest, servletOrFilter);
attachedContext = if (attachedContext == null && helper().shouldStart(currentContext, requestContext)) {
helper().updateContext(attachedContext, httpServletRequest, mappingResolver, servlet); context = helper().start(currentContext, requestContext);
scope = attachedContext.makeCurrent(); helper().setAsyncListenerResponse(httpServletRequest, (HttpServletResponse) response);
contextToUpdate = context;
} else if (attachedContext != null
&& helper().needsRescoping(currentContext, attachedContext)) {
// Given request already has a context associated with it.
// see the needsRescoping() javadoc for more explanation
contextToUpdate = attachedContext;
} else {
// We are inside nested servlet/filter/app-server span, don't create new span // We are inside nested servlet/filter/app-server span, don't create new span
return; contextToUpdate = currentContext;
} }
if (attachedContext != null || ServerSpan.fromContextOrNull(currentContext) != null) {
// Update context with info from current request to ensure that server span gets the best // Update context with info from current request to ensure that server span gets the best
// possible name. // possible name.
// In case server span was created by app server instrumentations calling updateContext // In case server span was created by app server instrumentations calling updateContext
@ -65,26 +70,9 @@ public class Servlet3Advice {
// instrumentations for naming server span. // instrumentations for naming server span.
MappingResolver mappingResolver = Servlet3Singletons.getMappingResolver(servletOrFilter); MappingResolver mappingResolver = Servlet3Singletons.getMappingResolver(servletOrFilter);
boolean servlet = servletOrFilter instanceof Servlet; boolean servlet = servletOrFilter instanceof Servlet;
Context updatedContext = contextToUpdate =
helper().updateContext(currentContext, httpServletRequest, mappingResolver, servlet); helper().updateContext(contextToUpdate, httpServletRequest, mappingResolver, servlet);
if (currentContext != updatedContext) { scope = contextToUpdate.makeCurrent();
// updateContext updated context, need to re-scope
scope = updatedContext.makeCurrent();
}
// We are inside nested servlet/filter/app-server span, don't create new span
return;
}
requestContext = new ServletRequestContext<>(httpServletRequest, servletOrFilter);
if (!helper().shouldStart(currentContext, requestContext)) {
return;
}
context = helper().start(currentContext, requestContext);
scope = context.makeCurrent();
helper().setAsyncListenerResponse(httpServletRequest, (HttpServletResponse) response);
} }
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)

View File

@ -5,12 +5,8 @@
package io.opentelemetry.javaagent.instrumentation.servlet.v3_0; package io.opentelemetry.javaagent.instrumentation.servlet.v3_0;
import static io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming.Source.FILTER;
import static io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming.Source.SERVLET;
import io.opentelemetry.instrumentation.api.field.VirtualField; import io.opentelemetry.instrumentation.api.field.VirtualField;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming;
import io.opentelemetry.javaagent.bootstrap.servlet.MappingResolver; import io.opentelemetry.javaagent.bootstrap.servlet.MappingResolver;
import io.opentelemetry.javaagent.instrumentation.servlet.ServletHelper; import io.opentelemetry.javaagent.instrumentation.servlet.ServletHelper;
import io.opentelemetry.javaagent.instrumentation.servlet.ServletInstrumenterBuilder; import io.opentelemetry.javaagent.instrumentation.servlet.ServletInstrumenterBuilder;
@ -28,11 +24,6 @@ public final class Servlet3Singletons {
ServletRequestContext<HttpServletRequest>, ServletResponseContext<HttpServletResponse>> ServletRequestContext<HttpServletRequest>, ServletResponseContext<HttpServletResponse>>
INSTRUMENTER = INSTRUMENTER =
ServletInstrumenterBuilder.<HttpServletRequest, HttpServletResponse>create() ServletInstrumenterBuilder.<HttpServletRequest, HttpServletResponse>create()
.setMappingResolverFunction(Servlet3Singletons::getMappingResolver)
.addContextCustomizer(
(context, request, attributes) ->
ServerSpanNaming.init(
context, request.servletOrFilter() instanceof Servlet ? SERVLET : FILTER))
.build(INSTRUMENTATION_NAME, Servlet3Accessor.INSTANCE); .build(INSTRUMENTATION_NAME, Servlet3Accessor.INSTANCE);
private static final ServletHelper<HttpServletRequest, HttpServletResponse> HELPER = private static final ServletHelper<HttpServletRequest, HttpServletResponse> HELPER =
@ -47,11 +38,6 @@ public final class Servlet3Singletons {
return HELPER; return HELPER;
} }
private static MappingResolver getMappingResolver(
ServletRequestContext<?> servletRequestContext) {
return getMappingResolver(servletRequestContext.servletOrFilter());
}
public static MappingResolver getMappingResolver(Object servletOrFilter) { public static MappingResolver getMappingResolver(Object servletOrFilter) {
MappingResolver.Factory factory = getMappingResolverFactory(servletOrFilter); MappingResolver.Factory factory = getMappingResolverFactory(servletOrFilter);
if (factory != null) { if (factory != null) {

View File

@ -5,12 +5,8 @@
package io.opentelemetry.javaagent.instrumentation.servlet.v5_0; package io.opentelemetry.javaagent.instrumentation.servlet.v5_0;
import static io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming.Source.FILTER;
import static io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming.Source.SERVLET;
import io.opentelemetry.instrumentation.api.field.VirtualField; import io.opentelemetry.instrumentation.api.field.VirtualField;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming;
import io.opentelemetry.javaagent.bootstrap.servlet.MappingResolver; import io.opentelemetry.javaagent.bootstrap.servlet.MappingResolver;
import io.opentelemetry.javaagent.instrumentation.servlet.ServletHelper; import io.opentelemetry.javaagent.instrumentation.servlet.ServletHelper;
import io.opentelemetry.javaagent.instrumentation.servlet.ServletInstrumenterBuilder; import io.opentelemetry.javaagent.instrumentation.servlet.ServletInstrumenterBuilder;
@ -29,11 +25,6 @@ public final class Servlet5Singletons {
ServletRequestContext<HttpServletRequest>, ServletResponseContext<HttpServletResponse>> ServletRequestContext<HttpServletRequest>, ServletResponseContext<HttpServletResponse>>
INSTRUMENTER = INSTRUMENTER =
ServletInstrumenterBuilder.<HttpServletRequest, HttpServletResponse>create() ServletInstrumenterBuilder.<HttpServletRequest, HttpServletResponse>create()
.setMappingResolverFunction(Servlet5Singletons::getMappingResolver)
.addContextCustomizer(
(context, request, attributes) ->
ServerSpanNaming.init(
context, request.servletOrFilter() instanceof Servlet ? SERVLET : FILTER))
.build(INSTRUMENTATION_NAME, Servlet5Accessor.INSTANCE); .build(INSTRUMENTATION_NAME, Servlet5Accessor.INSTANCE);
private static final ServletHelper<HttpServletRequest, HttpServletResponse> HELPER = private static final ServletHelper<HttpServletRequest, HttpServletResponse> HELPER =
@ -48,11 +39,6 @@ public final class Servlet5Singletons {
return HELPER; return HELPER;
} }
private static MappingResolver getMappingResolver(
ServletRequestContext<?> servletRequestContext) {
return getMappingResolver(servletRequestContext.servletOrFilter());
}
public static MappingResolver getMappingResolver(Object servletOrFilter) { public static MappingResolver getMappingResolver(Object servletOrFilter) {
MappingResolver.Factory factory = getMappingResolverFactory(servletOrFilter); MappingResolver.Factory factory = getMappingResolverFactory(servletOrFilter);
if (factory != null) { if (factory != null) {

View File

@ -9,7 +9,6 @@ import static io.opentelemetry.javaagent.instrumentation.servlet.v5_0.Servlet5Si
import io.opentelemetry.context.Context; import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope; import io.opentelemetry.context.Scope;
import io.opentelemetry.instrumentation.api.tracer.ServerSpan;
import io.opentelemetry.javaagent.bootstrap.servlet.AppServerBridge; import io.opentelemetry.javaagent.bootstrap.servlet.AppServerBridge;
import io.opentelemetry.javaagent.bootstrap.servlet.MappingResolver; import io.opentelemetry.javaagent.bootstrap.servlet.MappingResolver;
import io.opentelemetry.javaagent.instrumentation.api.CallDepth; import io.opentelemetry.javaagent.instrumentation.api.CallDepth;
@ -40,25 +39,31 @@ public class JakartaServletServiceAdvice {
if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) { if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) {
return; return;
} }
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
callDepth = CallDepth.forClass(AppServerBridge.getCallDepthKey()); callDepth = CallDepth.forClass(AppServerBridge.getCallDepthKey());
callDepth.getAndIncrement(); callDepth.getAndIncrement();
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
Context currentContext = Java8BytecodeBridge.currentContext(); Context currentContext = Java8BytecodeBridge.currentContext();
Context attachedContext = helper().getServerContext(httpServletRequest); Context attachedContext = helper().getServerContext(httpServletRequest);
if (attachedContext != null && helper().needsRescoping(currentContext, attachedContext)) { Context contextToUpdate;
MappingResolver mappingResolver = Servlet5Singletons.getMappingResolver(servletOrFilter);
boolean servlet = servletOrFilter instanceof Servlet; requestContext = new ServletRequestContext<>(httpServletRequest, servletOrFilter);
attachedContext = if (attachedContext == null && helper().shouldStart(currentContext, requestContext)) {
helper().updateContext(attachedContext, httpServletRequest, mappingResolver, servlet); context = helper().start(currentContext, requestContext);
scope = attachedContext.makeCurrent(); helper().setAsyncListenerResponse(httpServletRequest, (HttpServletResponse) response);
contextToUpdate = context;
} else if (attachedContext != null
&& helper().needsRescoping(currentContext, attachedContext)) {
// Given request already has a context associated with it.
// see the needsRescoping() javadoc for more explanation
contextToUpdate = attachedContext;
} else {
// We are inside nested servlet/filter/app-server span, don't create new span // We are inside nested servlet/filter/app-server span, don't create new span
return; contextToUpdate = currentContext;
} }
if (attachedContext != null || ServerSpan.fromContextOrNull(currentContext) != null) {
// Update context with info from current request to ensure that server span gets the best // Update context with info from current request to ensure that server span gets the best
// possible name. // possible name.
// In case server span was created by app server instrumentations calling updateContext // In case server span was created by app server instrumentations calling updateContext
@ -66,26 +71,9 @@ public class JakartaServletServiceAdvice {
// instrumentations for naming server span. // instrumentations for naming server span.
MappingResolver mappingResolver = Servlet5Singletons.getMappingResolver(servletOrFilter); MappingResolver mappingResolver = Servlet5Singletons.getMappingResolver(servletOrFilter);
boolean servlet = servletOrFilter instanceof Servlet; boolean servlet = servletOrFilter instanceof Servlet;
Context updatedContext = contextToUpdate =
helper().updateContext(currentContext, httpServletRequest, mappingResolver, servlet); helper().updateContext(contextToUpdate, httpServletRequest, mappingResolver, servlet);
if (currentContext != updatedContext) { scope = contextToUpdate.makeCurrent();
// updateContext updated context, need to re-scope
scope = updatedContext.makeCurrent();
}
// We are inside nested servlet/filter/app-server span, don't create new span
return;
}
requestContext = new ServletRequestContext<>(httpServletRequest, servletOrFilter);
if (!helper().shouldStart(currentContext, requestContext)) {
return;
}
context = helper().start(currentContext, requestContext);
scope = context.makeCurrent();
helper().setAsyncListenerResponse(httpServletRequest, (HttpServletResponse) response);
} }
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)

View File

@ -14,20 +14,16 @@ import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.SpanStatusExtractor; import io.opentelemetry.instrumentation.api.instrumenter.SpanStatusExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerAttributesExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerMetrics; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerMetrics;
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor;
import io.opentelemetry.javaagent.bootstrap.servlet.MappingResolver; import io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.function.Function;
import javax.annotation.Nullable;
public final class ServletInstrumenterBuilder<REQUEST, RESPONSE> { public final class ServletInstrumenterBuilder<REQUEST, RESPONSE> {
private ServletInstrumenterBuilder() {} private ServletInstrumenterBuilder() {}
@Nullable
private Function<ServletRequestContext<REQUEST>, MappingResolver> mappingResolverFunction;
private final List<ContextCustomizer<? super ServletRequestContext<REQUEST>>> contextCustomizers = private final List<ContextCustomizer<? super ServletRequestContext<REQUEST>>> contextCustomizers =
new ArrayList<>(); new ArrayList<>();
@ -35,12 +31,6 @@ public final class ServletInstrumenterBuilder<REQUEST, RESPONSE> {
return new ServletInstrumenterBuilder<>(); return new ServletInstrumenterBuilder<>();
} }
public ServletInstrumenterBuilder<REQUEST, RESPONSE> setMappingResolverFunction(
Function<ServletRequestContext<REQUEST>, MappingResolver> mappingResolverFunction) {
this.mappingResolverFunction = mappingResolverFunction;
return this;
}
public ServletInstrumenterBuilder<REQUEST, RESPONSE> addContextCustomizer( public ServletInstrumenterBuilder<REQUEST, RESPONSE> addContextCustomizer(
ContextCustomizer<? super ServletRequestContext<REQUEST>> contextCustomizer) { ContextCustomizer<? super ServletRequestContext<REQUEST>> contextCustomizer) {
contextCustomizers.add(contextCustomizer); contextCustomizers.add(contextCustomizer);
@ -72,7 +62,8 @@ public final class ServletInstrumenterBuilder<REQUEST, RESPONSE> {
.addAttributesExtractor(httpAttributesExtractor) .addAttributesExtractor(httpAttributesExtractor)
.addAttributesExtractor(netAttributesExtractor) .addAttributesExtractor(netAttributesExtractor)
.addAttributesExtractor(additionalAttributesExtractor) .addAttributesExtractor(additionalAttributesExtractor)
.addRequestMetrics(HttpServerMetrics.get()); .addRequestMetrics(HttpServerMetrics.get())
.addContextCustomizer(ServerSpanNaming.get());
if (ServletRequestParametersExtractor.enabled()) { if (ServletRequestParametersExtractor.enabled()) {
AttributesExtractor<ServletRequestContext<REQUEST>, ServletResponseContext<RESPONSE>> AttributesExtractor<ServletRequestContext<REQUEST>, ServletResponseContext<RESPONSE>>
requestParametersExtractor = new ServletRequestParametersExtractor<>(accessor); requestParametersExtractor = new ServletRequestParametersExtractor<>(accessor);
@ -90,7 +81,7 @@ public final class ServletInstrumenterBuilder<REQUEST, RESPONSE> {
HttpServerAttributesExtractor<ServletRequestContext<REQUEST>, ServletResponseContext<RESPONSE>> HttpServerAttributesExtractor<ServletRequestContext<REQUEST>, ServletResponseContext<RESPONSE>>
httpAttributesExtractor = new ServletHttpAttributesExtractor<>(accessor); httpAttributesExtractor = new ServletHttpAttributesExtractor<>(accessor);
SpanNameExtractor<ServletRequestContext<REQUEST>> spanNameExtractor = SpanNameExtractor<ServletRequestContext<REQUEST>> spanNameExtractor =
new ServletSpanNameExtractor<>(accessor, mappingResolverFunction); HttpSpanNameExtractor.create(httpAttributesExtractor);
return build(instrumentationName, accessor, spanNameExtractor, httpAttributesExtractor); return build(instrumentationName, accessor, spanNameExtractor, httpAttributesExtractor);
} }

View File

@ -1,68 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.servlet;
import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor;
import io.opentelemetry.javaagent.bootstrap.servlet.MappingResolver;
import java.util.function.Function;
import javax.annotation.Nullable;
public class ServletSpanNameExtractor<REQUEST, RESPONSE>
implements SpanNameExtractor<ServletRequestContext<REQUEST>> {
private final ServletAccessor<REQUEST, RESPONSE> accessor;
@Nullable
private final Function<ServletRequestContext<REQUEST>, MappingResolver> mappingResolverFunction;
public ServletSpanNameExtractor(
ServletAccessor<REQUEST, RESPONSE> accessor,
@Nullable Function<ServletRequestContext<REQUEST>, MappingResolver> mappingResolverFunction) {
this.accessor = accessor;
this.mappingResolverFunction = mappingResolverFunction;
}
@Nullable
private String route(ServletRequestContext<REQUEST> requestContext) {
if (mappingResolverFunction == null) {
return null;
}
MappingResolver mappingResolver = mappingResolverFunction.apply(requestContext);
if (mappingResolver == null) {
return null;
}
REQUEST request = requestContext.request();
String servletPath = accessor.getRequestServletPath(request);
String pathInfo = accessor.getRequestPathInfo(request);
String contextPath = accessor.getRequestContextPath(request);
boolean hasContextPath =
contextPath != null && !contextPath.isEmpty() && !contextPath.equals("/");
String route = mappingResolver.resolve(servletPath, pathInfo);
if (route == null) {
if (hasContextPath) {
return contextPath + "/*";
}
return null;
}
// prepend context path
return contextPath + route;
}
@Override
public String extract(ServletRequestContext<REQUEST> requestContext) {
String route = route(requestContext);
if (route != null) {
return route;
}
REQUEST request = requestContext.request();
String method = accessor.getRequestMethod(request);
if (method != null) {
return "HTTP " + method;
}
return "HTTP request";
}
}

View File

@ -13,6 +13,7 @@ import io.opentelemetry.instrumentation.api.instrumenter.http.CapturedHttpHeader
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerMetrics; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerMetrics;
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor;
import io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -73,6 +74,7 @@ public final class SpringWebMvcTracingBuilder {
.addAttributesExtractor(new SpringWebMvcNetAttributesExtractor()) .addAttributesExtractor(new SpringWebMvcNetAttributesExtractor())
.addAttributesExtractors(additionalExtractors) .addAttributesExtractors(additionalExtractors)
.addRequestMetrics(HttpServerMetrics.get()) .addRequestMetrics(HttpServerMetrics.get())
.addContextCustomizer(ServerSpanNaming.get())
.newServerInstrumenter(JavaxHttpServletRequestGetter.INSTANCE); .newServerInstrumenter(JavaxHttpServletRequestGetter.INSTANCE);
return new SpringWebMvcTracing(instrumenter); return new SpringWebMvcTracing(instrumenter);

View File

@ -5,8 +5,6 @@
package io.opentelemetry.javaagent.instrumentation.tomcat.common; package io.opentelemetry.javaagent.instrumentation.tomcat.common;
import static io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming.Source.CONTAINER;
import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
@ -50,15 +48,13 @@ public final class TomcatInstrumenterFactory {
.addAttributesExtractor(httpAttributesExtractor) .addAttributesExtractor(httpAttributesExtractor)
.addAttributesExtractor(netAttributesExtractor) .addAttributesExtractor(netAttributesExtractor)
.addAttributesExtractor(additionalAttributeExtractor) .addAttributesExtractor(additionalAttributeExtractor)
.addContextCustomizer(ServerSpanNaming.get())
.addContextCustomizer( .addContextCustomizer(
(context, request, attributes) -> { (context, request, attributes) ->
context = ServerSpanNaming.init(context, CONTAINER); new AppServerBridge.Builder()
return new AppServerBridge.Builder()
.captureServletAttributes() .captureServletAttributes()
.recordException() .recordException()
.init(context); .init(context))
})
.addRequestMetrics(HttpServerMetrics.get()) .addRequestMetrics(HttpServerMetrics.get())
.newServerInstrumenter(TomcatRequestGetter.INSTANCE); .newServerInstrumenter(TomcatRequestGetter.INSTANCE);
} }

View File

@ -5,8 +5,6 @@
package io.opentelemetry.javaagent.instrumentation.undertow; package io.opentelemetry.javaagent.instrumentation.undertow;
import static io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming.Source.CONTAINER;
import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor;
@ -42,9 +40,9 @@ public final class UndertowSingletons {
.setSpanStatusExtractor(spanStatusExtractor) .setSpanStatusExtractor(spanStatusExtractor)
.addAttributesExtractor(httpAttributesExtractor) .addAttributesExtractor(httpAttributesExtractor)
.addAttributesExtractor(netAttributesExtractor) .addAttributesExtractor(netAttributesExtractor)
.addContextCustomizer(ServerSpanNaming.get())
.addContextCustomizer( .addContextCustomizer(
(context, request, attributes) -> { (context, request, attributes) -> {
context = ServerSpanNaming.init(context, CONTAINER);
// span is ended when counter reaches 0, we start from 2 which accounts for the // span is ended when counter reaches 0, we start from 2 which accounts for the
// handler that started the span and exchange completion listener // handler that started the span and exchange completion listener
context = UndertowActiveHandlers.init(context, 2); context = UndertowActiveHandlers.init(context, 2);