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.context.Context;
import io.opentelemetry.context.ContextKey;
import io.opentelemetry.instrumentation.api.instrumenter.ContextCustomizer;
import io.opentelemetry.instrumentation.api.tracer.ServerSpan;
import javax.annotation.Nullable;
@ -17,14 +18,13 @@ public final class ServerSpanNaming {
private static final ContextKey<ServerSpanNaming> CONTEXT_KEY =
ContextKey.named("opentelemetry-servlet-span-naming-key");
public static Context init(Context context, Source initialSource) {
ServerSpanNaming serverSpanNaming = context.get(CONTEXT_KEY);
if (serverSpanNaming != null) {
// TODO (trask) does this ever happen?
serverSpanNaming.updatedBySource = initialSource;
return context;
}
return context.with(CONTEXT_KEY, new ServerSpanNaming(initialSource));
public static <REQUEST> ContextCustomizer<REQUEST> get() {
return (context, request, startAttributes) -> {
if (context.get(CONTEXT_KEY) != null) {
return context;
}
return context.with(CONTEXT_KEY, new ServerSpanNaming(Source.CONTAINER));
};
}
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
* populate a {@code ServerSpanName} into the context, then this method will update the server
* span name using the provided {@link ServerSpanNameSupplier} if and only if the last {@link
* Source} to update the span name using this method has strictly lower priority than the provided
* {@link Source}, and the value returned from the {@link ServerSpanNameSupplier} is non-null.
* If there is a server span in the context, and the context has been customized with a {@code
* ServerSpanName}, then this method will update the server span name using the provided {@link
* ServerSpanNameSupplier} if and only if the last {@link Source} to update the span name using
* this method has strictly lower priority than the provided {@link Source}, and the value
* 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
* called to populate a {@code ServerSpanName} into the context, then this method will update the
* server span name using the provided {@link ServerSpanNameSupplier} if the value returned from
* it is non-null.
* <p>If there is a server span in the context, and the context has NOT been customized with a
* {@code ServerSpanName}, then this method will update the server span name using the provided
* {@link ServerSpanNameSupplier} if the value returned from it is non-null.
*/
public static <T> void updateServerSpanName(
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
* populate a {@code ServerSpanName} into the context, then this method will update the server
* span name using the provided {@link ServerSpanNameTwoArgSupplier} if and only if the last
* {@link Source} to update the span name using this method has strictly lower priority than the
* provided {@link Source}, and the value returned from the {@link ServerSpanNameTwoArgSupplier}
* is non-null.
* If there is a server span in the context, and the context has been customized with a {@code
* ServerSpanName}, then this method will update the server span name using the provided {@link
* ServerSpanNameTwoArgSupplier} if and only if the last {@link Source} to update the span name
* using this method has strictly lower priority than the provided {@link Source}, and the value
* returned from the {@link ServerSpanNameTwoArgSupplier} is non-null.
*
* <p>If there is a server span in the context, and {@link #init(Context, Source)} has NOT been
* called to populate a {@code ServerSpanName} into the context, then this method will update the
* server span name using the provided {@link ServerSpanNameTwoArgSupplier} if the value returned
* from it is non-null.
* <p>If there is a server span in the context, and the context has NOT been customized with a
* {@code ServerSpanName}, then this method will update the server span name using the provided
* {@link ServerSpanNameTwoArgSupplier} if the value returned from it is non-null.
*/
public static <T, U> void updateServerSpanName(
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.http.HttpServerMetrics;
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor;
import io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming;
import io.opentelemetry.javaagent.instrumentation.akkahttp.AkkaHttpUtil;
public class AkkaHttpServerSingletons {
@ -28,6 +29,7 @@ public class AkkaHttpServerSingletons {
.setSpanStatusExtractor(HttpSpanStatusExtractor.create(httpAttributesExtractor))
.addAttributesExtractor(httpAttributesExtractor)
.addRequestMetrics(HttpServerMetrics.get())
.addContextCustomizer(ServerSpanNaming.get())
.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.HttpSpanNameExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor;
import io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming;
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
import java.util.ArrayList;
import java.util.List;
@ -135,7 +136,8 @@ public final class ArmeriaTracingBuilder {
HttpSpanStatusExtractor.create(serverAttributesExtractor)))
.addAttributesExtractor(new ArmeriaNetServerAttributesExtractor())
.addAttributesExtractor(serverAttributesExtractor)
.addRequestMetrics(HttpServerMetrics.get());
.addRequestMetrics(HttpServerMetrics.get())
.addContextCustomizer(ServerSpanNaming.get());
if (peerService != null) {
clientInstrumenterBuilder.addAttributesExtractor(

View File

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

View File

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

View File

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

View File

@ -5,8 +5,6 @@
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.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor;
@ -39,8 +37,7 @@ public final class LibertyDispatcherSingletons {
.setSpanStatusExtractor(spanStatusExtractor)
.addAttributesExtractor(httpAttributesExtractor)
.addAttributesExtractor(netAttributesExtractor)
.addContextCustomizer(
(context, request, attributes) -> ServerSpanNaming.init(context, CONTAINER))
.addContextCustomizer(ServerSpanNaming.get())
.addRequestMetrics(HttpServerMetrics.get())
.newServerInstrumenter(LibertyDispatcherRequestGetter.INSTANCE);
}

View File

@ -5,10 +5,7 @@
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.servlet.ServerSpanNaming;
import io.opentelemetry.javaagent.bootstrap.servlet.AppServerBridge;
import io.opentelemetry.javaagent.instrumentation.servlet.ServletInstrumenterBuilder;
import io.opentelemetry.javaagent.instrumentation.servlet.ServletRequestContext;
@ -25,10 +22,8 @@ public final class LibertySingletons {
INSTRUMENTER =
ServletInstrumenterBuilder.<HttpServletRequest, HttpServletResponse>create()
.addContextCustomizer(
(context, request, attributes) -> {
context = ServerSpanNaming.init(context, CONTAINER);
return new AppServerBridge.Builder().recordException().init(context);
})
(context, request, attributes) ->
new AppServerBridge.Builder().recordException().init(context))
.build(INSTRUMENTATION_NAME, Servlet3Accessor.INSTANCE);
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.HttpSpanNameExtractor;
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.v3_8.HttpRequestAndChannel;
import org.jboss.netty.handler.codec.http.HttpResponse;
@ -33,6 +34,7 @@ final class NettyServerSingletons {
.addRequestMetrics(HttpServerMetrics.get())
.addContextCustomizer(
(context, requestAndChannel, startAttributes) -> NettyErrorHolder.init(context))
.addContextCustomizer(ServerSpanNaming.get())
.newServerInstrumenter(NettyHeadersGetter.INSTANCE);
}

View File

@ -31,12 +31,8 @@ public final class NettyServerInstrumenterFactory {
.addAttributesExtractor(httpAttributesExtractor)
.addAttributesExtractor(new NettyNetServerAttributesExtractor())
.addRequestMetrics(HttpServerMetrics.get())
.addContextCustomizer(
(context, request, attributes) -> {
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);
})
.addContextCustomizer((context, request, attributes) -> NettyErrorHolder.init(context))
.addContextCustomizer(ServerSpanNaming.get())
.newServerInstrumenter(HttpRequestHeadersGetter.INSTANCE);
}

View File

@ -5,12 +5,9 @@
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.SpanNameExtractor;
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.ServletRequestContext;
import io.opentelemetry.javaagent.instrumentation.servlet.ServletResponseContext;
@ -33,8 +30,6 @@ public final class Servlet2Singletons {
ServletRequestContext<HttpServletRequest>, ServletResponseContext<HttpServletResponse>>
instrumenter =
ServletInstrumenterBuilder.<HttpServletRequest, HttpServletResponse>create()
.addContextCustomizer(
(context, request, attributes) -> ServerSpanNaming.init(context, SERVLET))
.build(
INSTRUMENTATION_NAME,
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.Scope;
import io.opentelemetry.instrumentation.api.tracer.ServerSpan;
import io.opentelemetry.javaagent.bootstrap.servlet.AppServerBridge;
import io.opentelemetry.javaagent.bootstrap.servlet.MappingResolver;
import io.opentelemetry.javaagent.instrumentation.api.CallDepth;
@ -39,52 +38,41 @@ public class Servlet3Advice {
if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) {
return;
}
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
callDepth = CallDepth.forClass(AppServerBridge.getCallDepthKey());
callDepth.getAndIncrement();
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
Context currentContext = Java8BytecodeBridge.currentContext();
Context attachedContext = helper().getServerContext(httpServletRequest);
if (attachedContext != null && helper().needsRescoping(currentContext, attachedContext)) {
MappingResolver mappingResolver = Servlet3Singletons.getMappingResolver(servletOrFilter);
boolean servlet = servletOrFilter instanceof Servlet;
attachedContext =
helper().updateContext(attachedContext, httpServletRequest, mappingResolver, servlet);
scope = attachedContext.makeCurrent();
// We are inside nested servlet/filter/app-server span, don't create new span
return;
}
if (attachedContext != null || ServerSpan.fromContextOrNull(currentContext) != null) {
// Update context with info from current request to ensure that server span gets the best
// possible name.
// In case server span was created by app server instrumentations calling updateContext
// returns a new context that contains servlet context path that is used in other
// instrumentations for naming server span.
MappingResolver mappingResolver = Servlet3Singletons.getMappingResolver(servletOrFilter);
boolean servlet = servletOrFilter instanceof Servlet;
Context updatedContext =
helper().updateContext(currentContext, httpServletRequest, mappingResolver, servlet);
if (currentContext != updatedContext) {
// 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;
}
Context contextToUpdate;
requestContext = new ServletRequestContext<>(httpServletRequest, servletOrFilter);
if (attachedContext == null && helper().shouldStart(currentContext, requestContext)) {
context = helper().start(currentContext, requestContext);
helper().setAsyncListenerResponse(httpServletRequest, (HttpServletResponse) response);
if (!helper().shouldStart(currentContext, requestContext)) {
return;
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
contextToUpdate = currentContext;
}
context = helper().start(currentContext, requestContext);
scope = context.makeCurrent();
helper().setAsyncListenerResponse(httpServletRequest, (HttpServletResponse) response);
// Update context with info from current request to ensure that server span gets the best
// possible name.
// In case server span was created by app server instrumentations calling updateContext
// returns a new context that contains servlet context path that is used in other
// instrumentations for naming server span.
MappingResolver mappingResolver = Servlet3Singletons.getMappingResolver(servletOrFilter);
boolean servlet = servletOrFilter instanceof Servlet;
contextToUpdate =
helper().updateContext(contextToUpdate, httpServletRequest, mappingResolver, servlet);
scope = contextToUpdate.makeCurrent();
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)

View File

@ -5,12 +5,8 @@
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.instrumenter.Instrumenter;
import io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming;
import io.opentelemetry.javaagent.bootstrap.servlet.MappingResolver;
import io.opentelemetry.javaagent.instrumentation.servlet.ServletHelper;
import io.opentelemetry.javaagent.instrumentation.servlet.ServletInstrumenterBuilder;
@ -28,11 +24,6 @@ public final class Servlet3Singletons {
ServletRequestContext<HttpServletRequest>, ServletResponseContext<HttpServletResponse>>
INSTRUMENTER =
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);
private static final ServletHelper<HttpServletRequest, HttpServletResponse> HELPER =
@ -47,11 +38,6 @@ public final class Servlet3Singletons {
return HELPER;
}
private static MappingResolver getMappingResolver(
ServletRequestContext<?> servletRequestContext) {
return getMappingResolver(servletRequestContext.servletOrFilter());
}
public static MappingResolver getMappingResolver(Object servletOrFilter) {
MappingResolver.Factory factory = getMappingResolverFactory(servletOrFilter);
if (factory != null) {

View File

@ -5,12 +5,8 @@
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.instrumenter.Instrumenter;
import io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming;
import io.opentelemetry.javaagent.bootstrap.servlet.MappingResolver;
import io.opentelemetry.javaagent.instrumentation.servlet.ServletHelper;
import io.opentelemetry.javaagent.instrumentation.servlet.ServletInstrumenterBuilder;
@ -29,11 +25,6 @@ public final class Servlet5Singletons {
ServletRequestContext<HttpServletRequest>, ServletResponseContext<HttpServletResponse>>
INSTRUMENTER =
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);
private static final ServletHelper<HttpServletRequest, HttpServletResponse> HELPER =
@ -48,11 +39,6 @@ public final class Servlet5Singletons {
return HELPER;
}
private static MappingResolver getMappingResolver(
ServletRequestContext<?> servletRequestContext) {
return getMappingResolver(servletRequestContext.servletOrFilter());
}
public static MappingResolver getMappingResolver(Object servletOrFilter) {
MappingResolver.Factory factory = getMappingResolverFactory(servletOrFilter);
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.Scope;
import io.opentelemetry.instrumentation.api.tracer.ServerSpan;
import io.opentelemetry.javaagent.bootstrap.servlet.AppServerBridge;
import io.opentelemetry.javaagent.bootstrap.servlet.MappingResolver;
import io.opentelemetry.javaagent.instrumentation.api.CallDepth;
@ -40,52 +39,41 @@ public class JakartaServletServiceAdvice {
if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) {
return;
}
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
callDepth = CallDepth.forClass(AppServerBridge.getCallDepthKey());
callDepth.getAndIncrement();
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
Context currentContext = Java8BytecodeBridge.currentContext();
Context attachedContext = helper().getServerContext(httpServletRequest);
if (attachedContext != null && helper().needsRescoping(currentContext, attachedContext)) {
MappingResolver mappingResolver = Servlet5Singletons.getMappingResolver(servletOrFilter);
boolean servlet = servletOrFilter instanceof Servlet;
attachedContext =
helper().updateContext(attachedContext, httpServletRequest, mappingResolver, servlet);
scope = attachedContext.makeCurrent();
// We are inside nested servlet/filter/app-server span, don't create new span
return;
}
if (attachedContext != null || ServerSpan.fromContextOrNull(currentContext) != null) {
// Update context with info from current request to ensure that server span gets the best
// possible name.
// In case server span was created by app server instrumentations calling updateContext
// returns a new context that contains servlet context path that is used in other
// instrumentations for naming server span.
MappingResolver mappingResolver = Servlet5Singletons.getMappingResolver(servletOrFilter);
boolean servlet = servletOrFilter instanceof Servlet;
Context updatedContext =
helper().updateContext(currentContext, httpServletRequest, mappingResolver, servlet);
if (currentContext != updatedContext) {
// 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;
}
Context contextToUpdate;
requestContext = new ServletRequestContext<>(httpServletRequest, servletOrFilter);
if (attachedContext == null && helper().shouldStart(currentContext, requestContext)) {
context = helper().start(currentContext, requestContext);
helper().setAsyncListenerResponse(httpServletRequest, (HttpServletResponse) response);
if (!helper().shouldStart(currentContext, requestContext)) {
return;
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
contextToUpdate = currentContext;
}
context = helper().start(currentContext, requestContext);
scope = context.makeCurrent();
helper().setAsyncListenerResponse(httpServletRequest, (HttpServletResponse) response);
// Update context with info from current request to ensure that server span gets the best
// possible name.
// In case server span was created by app server instrumentations calling updateContext
// returns a new context that contains servlet context path that is used in other
// instrumentations for naming server span.
MappingResolver mappingResolver = Servlet5Singletons.getMappingResolver(servletOrFilter);
boolean servlet = servletOrFilter instanceof Servlet;
contextToUpdate =
helper().updateContext(contextToUpdate, httpServletRequest, mappingResolver, servlet);
scope = contextToUpdate.makeCurrent();
}
@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.http.HttpServerAttributesExtractor;
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.javaagent.bootstrap.servlet.MappingResolver;
import io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import javax.annotation.Nullable;
public final class ServletInstrumenterBuilder<REQUEST, RESPONSE> {
private ServletInstrumenterBuilder() {}
@Nullable
private Function<ServletRequestContext<REQUEST>, MappingResolver> mappingResolverFunction;
private final List<ContextCustomizer<? super ServletRequestContext<REQUEST>>> contextCustomizers =
new ArrayList<>();
@ -35,12 +31,6 @@ public final class ServletInstrumenterBuilder<REQUEST, RESPONSE> {
return new ServletInstrumenterBuilder<>();
}
public ServletInstrumenterBuilder<REQUEST, RESPONSE> setMappingResolverFunction(
Function<ServletRequestContext<REQUEST>, MappingResolver> mappingResolverFunction) {
this.mappingResolverFunction = mappingResolverFunction;
return this;
}
public ServletInstrumenterBuilder<REQUEST, RESPONSE> addContextCustomizer(
ContextCustomizer<? super ServletRequestContext<REQUEST>> contextCustomizer) {
contextCustomizers.add(contextCustomizer);
@ -72,7 +62,8 @@ public final class ServletInstrumenterBuilder<REQUEST, RESPONSE> {
.addAttributesExtractor(httpAttributesExtractor)
.addAttributesExtractor(netAttributesExtractor)
.addAttributesExtractor(additionalAttributesExtractor)
.addRequestMetrics(HttpServerMetrics.get());
.addRequestMetrics(HttpServerMetrics.get())
.addContextCustomizer(ServerSpanNaming.get());
if (ServletRequestParametersExtractor.enabled()) {
AttributesExtractor<ServletRequestContext<REQUEST>, ServletResponseContext<RESPONSE>>
requestParametersExtractor = new ServletRequestParametersExtractor<>(accessor);
@ -90,7 +81,7 @@ public final class ServletInstrumenterBuilder<REQUEST, RESPONSE> {
HttpServerAttributesExtractor<ServletRequestContext<REQUEST>, ServletResponseContext<RESPONSE>>
httpAttributesExtractor = new ServletHttpAttributesExtractor<>(accessor);
SpanNameExtractor<ServletRequestContext<REQUEST>> spanNameExtractor =
new ServletSpanNameExtractor<>(accessor, mappingResolverFunction);
HttpSpanNameExtractor.create(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.HttpSpanNameExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor;
import io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
@ -73,6 +74,7 @@ public final class SpringWebMvcTracingBuilder {
.addAttributesExtractor(new SpringWebMvcNetAttributesExtractor())
.addAttributesExtractors(additionalExtractors)
.addRequestMetrics(HttpServerMetrics.get())
.addContextCustomizer(ServerSpanNaming.get())
.newServerInstrumenter(JavaxHttpServletRequestGetter.INSTANCE);
return new SpringWebMvcTracing(instrumenter);

View File

@ -5,8 +5,6 @@
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.instrumentation.api.instrumenter.AttributesExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
@ -50,15 +48,13 @@ public final class TomcatInstrumenterFactory {
.addAttributesExtractor(httpAttributesExtractor)
.addAttributesExtractor(netAttributesExtractor)
.addAttributesExtractor(additionalAttributeExtractor)
.addContextCustomizer(ServerSpanNaming.get())
.addContextCustomizer(
(context, request, attributes) -> {
context = ServerSpanNaming.init(context, CONTAINER);
return new AppServerBridge.Builder()
.captureServletAttributes()
.recordException()
.init(context);
})
(context, request, attributes) ->
new AppServerBridge.Builder()
.captureServletAttributes()
.recordException()
.init(context))
.addRequestMetrics(HttpServerMetrics.get())
.newServerInstrumenter(TomcatRequestGetter.INSTANCE);
}

View File

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