Pass around context more instead of span (HttpServerTracer) (#1810)

This commit is contained in:
Trask Stalnaker 2020-12-01 14:49:01 -08:00 committed by GitHub
parent 01b4345742
commit 0bd85d7b7f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 131 additions and 174 deletions

View File

@ -12,7 +12,6 @@ import io.opentelemetry.api.trace.SpanBuilder;
import io.opentelemetry.api.trace.Tracer; import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.api.trace.attributes.SemanticAttributes; import io.opentelemetry.api.trace.attributes.SemanticAttributes;
import io.opentelemetry.context.Context; import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.context.propagation.TextMapPropagator; import io.opentelemetry.context.propagation.TextMapPropagator;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -35,17 +34,29 @@ public abstract class HttpServerTracer<REQUEST, RESPONSE, CONNECTION, STORAGE> e
super(tracer); super(tracer);
} }
public Context startSpan(REQUEST request, CONNECTION connection, Method origin) { public Context startSpan(REQUEST request, CONNECTION connection, STORAGE storage, Method origin) {
String spanName = spanNameForMethod(origin); String spanName = spanNameForMethod(origin);
return startSpan(request, connection, spanName); return startSpan(request, connection, storage, spanName);
}
public Context startSpan(REQUEST request, CONNECTION connection, String spanName) {
return startSpan(request, connection, spanName, -1);
} }
public Context startSpan( public Context startSpan(
REQUEST request, CONNECTION connection, String spanName, long startTimestamp) { REQUEST request, CONNECTION connection, STORAGE storage, String spanName) {
return startSpan(request, connection, storage, spanName, -1);
}
public Context startSpan(
REQUEST request,
CONNECTION connection,
@Nullable STORAGE storage,
String spanName,
long startTimestamp) {
// not checking if inside of nested SERVER span because of concerns about context leaking
// and so always starting with a clean context here
// also we can't conditionally start a span in this method, because the caller won't know
// whether to call end() or not on the Span in the returned Context
Context parentContext = extract(request, getGetter()); Context parentContext = extract(request, getGetter());
SpanBuilder builder = tracer.spanBuilder(spanName).setSpanKind(SERVER).setParent(parentContext); SpanBuilder builder = tracer.spanBuilder(spanName).setSpanKind(SERVER).setParent(parentContext);
@ -58,67 +69,51 @@ public abstract class HttpServerTracer<REQUEST, RESPONSE, CONNECTION, STORAGE> e
onRequest(span, request); onRequest(span, request);
onConnectionAndRequest(span, connection, request); onConnectionAndRequest(span, connection, request);
return parentContext.with(span); Context context = parentContext.with(CONTEXT_SERVER_SPAN_KEY, span).with(span);
attachServerContext(context, storage);
return context;
} }
/** /**
* Creates new scoped context, based on the current context, with the given span. * Convenience method. Delegates to {@link #end(Context, Object, long)}, passing {@code timestamp}
*
* <p>Attaches new context to the request to avoid creating duplicate server spans.
*/
public Scope startScope(Span span, STORAGE storage) {
return startScope(span, storage, Context.current());
}
/**
* Creates new scoped context, based on the given context, with the given span.
*
* <p>Attaches new context to the request to avoid creating duplicate server spans.
*/
public Scope startScope(Span span, STORAGE storage, Context context) {
// TODO we could do this in one go, but TracingContextUtils.CONTEXT_SPAN_KEY is private
Context newContext = context.with(CONTEXT_SERVER_SPAN_KEY, span).with(span);
attachServerContext(newContext, storage);
return newContext.makeCurrent();
}
/**
* Convenience method. Delegates to {@link #end(Span, Object, long)}, passing {@code timestamp}
* value of {@code -1}. * value of {@code -1}.
*/ */
// TODO should end methods remove SPAN attribute from request as well? // TODO should end methods remove SPAN attribute from request as well?
public void end(Span span, RESPONSE response) { public void end(Context context, RESPONSE response) {
end(span, response, -1); end(context, response, -1);
} }
// TODO should end methods remove SPAN attribute from request as well? // TODO should end methods remove SPAN attribute from request as well?
public void end(Span span, RESPONSE response, long timestamp) { public void end(Context context, RESPONSE response, long timestamp) {
Span span = Span.fromContext(context);
setStatus(span, responseStatus(response)); setStatus(span, responseStatus(response));
endSpan(span, timestamp); endSpan(span, timestamp);
} }
/** /**
* Convenience method. Delegates to {@link #endExceptionally(Span, Throwable, Object)}, passing * Convenience method. Delegates to {@link #endExceptionally(Context, Throwable, Object)}, passing
* {@code response} value of {@code null}. * {@code response} value of {@code null}.
*/ */
@Override public void endExceptionally(Context context, Throwable throwable) {
public void endExceptionally(Span span, Throwable throwable) { endExceptionally(context, throwable, null);
endExceptionally(span, throwable, null);
} }
/** /**
* Convenience method. Delegates to {@link #endExceptionally(Span, Throwable, Object, long)}, * Convenience method. Delegates to {@link #endExceptionally(Context, Throwable, Object, long)},
* passing {@code timestamp} value of {@code -1}. * passing {@code timestamp} value of {@code -1}.
*/ */
public void endExceptionally(Span span, Throwable throwable, RESPONSE response) { public void endExceptionally(Context context, Throwable throwable, RESPONSE response) {
endExceptionally(span, throwable, response, -1); endExceptionally(context, throwable, response, -1);
} }
/** /**
* If {@code response} is {@code null}, the {@code http.status_code} will be set to {@code 500} * If {@code response} is {@code null}, the {@code http.status_code} will be set to {@code 500}
* and the {@link Span} status will be set to {@link io.opentelemetry.api.trace.StatusCode#ERROR}. * and the {@link Span} status will be set to {@link io.opentelemetry.api.trace.StatusCode#ERROR}.
*/ */
public void endExceptionally(Span span, Throwable throwable, RESPONSE response, long timestamp) { public void endExceptionally(
Context context, Throwable throwable, RESPONSE response, long timestamp) {
Span span = Span.fromContext(context);
onError(span, unwrapThrowable(throwable)); onError(span, unwrapThrowable(throwable));
if (response == null) { if (response == null) {
setStatus(span, 500); setStatus(span, 500);

View File

@ -8,7 +8,6 @@ package io.opentelemetry.instrumentation.servlet;
import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.attributes.SemanticAttributes; import io.opentelemetry.api.trace.attributes.SemanticAttributes;
import io.opentelemetry.context.Context; import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.context.propagation.TextMapPropagator.Getter; import io.opentelemetry.context.propagation.TextMapPropagator.Getter;
import io.opentelemetry.instrumentation.api.servlet.ServletContextPath; import io.opentelemetry.instrumentation.api.servlet.ServletContextPath;
import io.opentelemetry.instrumentation.api.tracer.HttpServerTracer; import io.opentelemetry.instrumentation.api.tracer.HttpServerTracer;
@ -26,19 +25,12 @@ public abstract class ServletHttpServerTracer<RESPONSE>
private static final Logger log = LoggerFactory.getLogger(ServletHttpServerTracer.class); private static final Logger log = LoggerFactory.getLogger(ServletHttpServerTracer.class);
public Context startSpan(HttpServletRequest request) { public Context startSpan(HttpServletRequest request) {
return startSpan(request, request, getSpanName(request)); Context context = startSpan(request, request, request, getSpanName(request));
}
@Override
public Scope startScope(Span span, HttpServletRequest request) {
Context context = Context.current();
// if we start returning Context from startSpan() then we can add ServletContextPath there
// (https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/1481)
String contextPath = request.getContextPath(); String contextPath = request.getContextPath();
if (contextPath != null && !contextPath.isEmpty() && !contextPath.equals("/")) { if (contextPath != null && !contextPath.isEmpty() && !contextPath.equals("/")) {
context = context.with(ServletContextPath.CONTEXT_KEY, contextPath); context = context.with(ServletContextPath.CONTEXT_KEY, contextPath);
} }
return super.startScope(span, request, context); return context;
} }
@Override @Override
@ -109,10 +101,10 @@ public abstract class ServletHttpServerTracer<RESPONSE>
return super.unwrapThrowable(result); return super.unwrapThrowable(result);
} }
public void setPrincipal(Span span, HttpServletRequest request) { public void setPrincipal(Context context, HttpServletRequest request) {
Principal principal = request.getUserPrincipal(); Principal principal = request.getUserPrincipal();
if (principal != null) { if (principal != null) {
span.setAttribute(SemanticAttributes.ENDUSER_ID, principal.getName()); Span.fromContext(context).setAttribute(SemanticAttributes.ENDUSER_ID, principal.getName());
} }
} }

View File

@ -14,10 +14,8 @@ import akka.http.scaladsl.model.HttpRequest;
import akka.http.scaladsl.model.HttpResponse; import akka.http.scaladsl.model.HttpResponse;
import akka.stream.Materializer; import akka.stream.Materializer;
import com.google.auto.service.AutoService; import com.google.auto.service.AutoService;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context; import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope; import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
import io.opentelemetry.javaagent.tooling.InstrumentationModule; import io.opentelemetry.javaagent.tooling.InstrumentationModule;
import io.opentelemetry.javaagent.tooling.TypeInstrumentation; import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
import java.util.HashMap; import java.util.HashMap;
@ -96,14 +94,13 @@ public class AkkaHttpServerInstrumentationModule extends InstrumentationModule {
@Override @Override
public HttpResponse apply(HttpRequest request) { public HttpResponse apply(HttpRequest request) {
Context ctx = tracer().startSpan(request, request, "akka.request"); Context ctx = tracer().startSpan(request, request, null, "akka.request");
Span span = Java8BytecodeBridge.spanFromContext(ctx); try (Scope ignored = ctx.makeCurrent()) {
try (Scope ignored = tracer().startScope(span, null)) {
HttpResponse response = userHandler.apply(request); HttpResponse response = userHandler.apply(request);
tracer().end(span, response); tracer().end(ctx, response);
return response; return response;
} catch (Throwable t) { } catch (Throwable t) {
tracer().endExceptionally(span, t); tracer().endExceptionally(ctx, t);
throw t; throw t;
} }
} }
@ -122,29 +119,28 @@ public class AkkaHttpServerInstrumentationModule extends InstrumentationModule {
@Override @Override
public Future<HttpResponse> apply(HttpRequest request) { public Future<HttpResponse> apply(HttpRequest request) {
Context ctx = tracer().startSpan(request, request, "akka.request"); Context ctx = tracer().startSpan(request, request, null, "akka.request");
Span span = Java8BytecodeBridge.spanFromContext(ctx); try (Scope ignored = ctx.makeCurrent()) {
try (Scope ignored = tracer().startScope(span, null)) {
return userHandler return userHandler
.apply(request) .apply(request)
.transform( .transform(
new AbstractFunction1<HttpResponse, HttpResponse>() { new AbstractFunction1<HttpResponse, HttpResponse>() {
@Override @Override
public HttpResponse apply(HttpResponse response) { public HttpResponse apply(HttpResponse response) {
tracer().end(span, response); tracer().end(ctx, response);
return response; return response;
} }
}, },
new AbstractFunction1<Throwable, Throwable>() { new AbstractFunction1<Throwable, Throwable>() {
@Override @Override
public Throwable apply(Throwable t) { public Throwable apply(Throwable t) {
tracer().endExceptionally(span, t); tracer().endExceptionally(ctx, t);
return t; return t;
} }
}, },
executionContext); executionContext);
} catch (Throwable t) { } catch (Throwable t) {
tracer().endExceptionally(span, t); tracer().endExceptionally(ctx, t);
throw t; throw t;
} }
} }

View File

@ -59,10 +59,9 @@ public class OpenTelemetryService extends SimpleDecoratingHttpService {
long requestStartTimeMicros = long requestStartTimeMicros =
ctx.log().ensureAvailable(RequestLogProperty.REQUEST_START_TIME).requestStartTimeMicros(); ctx.log().ensureAvailable(RequestLogProperty.REQUEST_START_TIME).requestStartTimeMicros();
long requestStartTimeNanos = TimeUnit.MICROSECONDS.toNanos(requestStartTimeMicros); long requestStartTimeNanos = TimeUnit.MICROSECONDS.toNanos(requestStartTimeMicros);
Context context = serverTracer.startSpan(req, ctx, spanName, requestStartTimeNanos); Context context = serverTracer.startSpan(req, ctx, null, spanName, requestStartTimeNanos);
Span span = Span.fromContext(context);
if (span.isRecording()) { if (Span.fromContext(context).isRecording()) {
ctx.log() ctx.log()
.whenComplete() .whenComplete()
.thenAccept( .thenAccept(
@ -70,14 +69,14 @@ public class OpenTelemetryService extends SimpleDecoratingHttpService {
long requestEndTimeNanos = requestStartTimeNanos + log.responseDurationNanos(); long requestEndTimeNanos = requestStartTimeNanos + log.responseDurationNanos();
if (log.responseCause() != null) { if (log.responseCause() != null) {
serverTracer.endExceptionally( serverTracer.endExceptionally(
span, log.responseCause(), log, requestEndTimeNanos); context, log.responseCause(), log, requestEndTimeNanos);
} else { } else {
serverTracer.end(span, log, requestEndTimeNanos); serverTracer.end(context, log, requestEndTimeNanos);
} }
}); });
} }
try (Scope ignored = span.makeCurrent()) { try (Scope ignored = context.makeCurrent()) {
return unwrap().serve(ctx, req); return unwrap().serve(ctx, req);
} }
} }

View File

@ -7,9 +7,7 @@ package io.opentelemetry.javaagent.instrumentation.grizzly;
import static io.opentelemetry.javaagent.instrumentation.grizzly.GrizzlyHttpServerTracer.tracer; import static io.opentelemetry.javaagent.instrumentation.grizzly.GrizzlyHttpServerTracer.tracer;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context; import io.opentelemetry.context.Context;
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
import org.glassfish.grizzly.filterchain.FilterChainContext; import org.glassfish.grizzly.filterchain.FilterChainContext;
@ -31,11 +29,9 @@ public class HttpCodecFilterAdvice {
return; return;
} }
HttpRequestPacket httpRequest = (HttpRequestPacket) httpHeader; HttpRequestPacket httpRequest = (HttpRequestPacket) httpHeader;
Context extractedContext = tracer().startSpan(httpRequest, httpRequest, method);
Span span = Java8BytecodeBridge.spanFromContext(extractedContext);
// We don't actually want to attach new context to this thread, as actual request will continue // We don't want to attach the new context to this thread, as actual request will continue on
// on some other thread. But we do want to attach that new context to FilterChainContext // some other thread where we will read and attach it via tracer().getServerContext(ctx).
tracer().startScope(span, ctx).close(); tracer().startSpan(httpRequest, httpRequest, ctx, method);
} }
} }

View File

@ -7,9 +7,7 @@ package io.opentelemetry.javaagent.instrumentation.grizzly;
import static io.opentelemetry.javaagent.instrumentation.grizzly.GrizzlyHttpServerTracer.tracer; import static io.opentelemetry.javaagent.instrumentation.grizzly.GrizzlyHttpServerTracer.tracer;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context; import io.opentelemetry.context.Context;
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
import org.glassfish.grizzly.filterchain.FilterChainContext; import org.glassfish.grizzly.filterchain.FilterChainContext;
@ -31,11 +29,9 @@ public class HttpCodecFilterOldAdvice {
return; return;
} }
HttpRequestPacket httpRequest = (HttpRequestPacket) httpHeader; HttpRequestPacket httpRequest = (HttpRequestPacket) httpHeader;
Context extractedContext = tracer().startSpan(httpRequest, httpRequest, method);
Span span = Java8BytecodeBridge.spanFromContext(extractedContext);
// We don't actually want to attach new context to this thread, as actual request will continue // We don't want to attach the new context to this thread, as actual request will continue on
// on some other thread. But we do want to attach that new context to FilterChainContext // some other thread where we will read and attach it via tracer().getServerContext(ctx).
tracer().startScope(span, ctx).close(); tracer().startSpan(httpRequest, httpRequest, ctx, method);
} }
} }

View File

@ -7,7 +7,7 @@ package io.opentelemetry.javaagent.instrumentation.grizzly;
import static io.opentelemetry.javaagent.instrumentation.grizzly.GrizzlyHttpServerTracer.tracer; import static io.opentelemetry.javaagent.instrumentation.grizzly.GrizzlyHttpServerTracer.tracer;
import io.opentelemetry.api.trace.Span; import io.opentelemetry.context.Context;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
import org.glassfish.grizzly.filterchain.FilterChainContext; import org.glassfish.grizzly.filterchain.FilterChainContext;
import org.glassfish.grizzly.http.HttpResponsePacket; import org.glassfish.grizzly.http.HttpResponsePacket;
@ -16,9 +16,9 @@ public class HttpServerFilterAdvice {
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void onExit( public static void onExit(
@Advice.Argument(0) FilterChainContext ctx, @Advice.Argument(2) HttpResponsePacket response) { @Advice.Argument(0) FilterChainContext ctx, @Advice.Argument(2) HttpResponsePacket response) {
Span span = tracer().getServerSpan(ctx); Context context = tracer().getServerContext(ctx);
if (span != null) { if (context != null) {
tracer().end(span, response); tracer().end(context, response);
} }
} }
} }

View File

@ -7,10 +7,8 @@ package io.opentelemetry.javaagent.instrumentation.jetty;
import static io.opentelemetry.javaagent.instrumentation.jetty.JettyHttpServerTracer.tracer; import static io.opentelemetry.javaagent.instrumentation.jetty.JettyHttpServerTracer.tracer;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context; import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope; import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
import io.opentelemetry.javaagent.instrumentation.servlet.v3_0.TagSettingAsyncListener; import io.opentelemetry.javaagent.instrumentation.servlet.v3_0.TagSettingAsyncListener;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
@ -25,7 +23,7 @@ public class JettyHandlerAdvice {
@Advice.Origin Method method, @Advice.Origin Method method,
@Advice.This Object source, @Advice.This Object source,
@Advice.Argument(value = 2, readOnly = false) HttpServletRequest request, @Advice.Argument(value = 2, readOnly = false) HttpServletRequest request,
@Advice.Local("otelSpan") Span span, @Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) { @Advice.Local("otelScope") Scope scope) {
Context attachedContext = tracer().getServerContext(request); Context attachedContext = tracer().getServerContext(request);
@ -34,9 +32,8 @@ public class JettyHandlerAdvice {
return; return;
} }
Context ctx = tracer().startSpan(request, request, method); context = tracer().startSpan(request, request, request, method);
span = Java8BytecodeBridge.spanFromContext(ctx); scope = context.makeCurrent();
scope = tracer().startScope(span, request);
} }
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
@ -44,22 +41,22 @@ public class JettyHandlerAdvice {
@Advice.Argument(2) HttpServletRequest request, @Advice.Argument(2) HttpServletRequest request,
@Advice.Argument(3) HttpServletResponse response, @Advice.Argument(3) HttpServletResponse response,
@Advice.Thrown Throwable throwable, @Advice.Thrown Throwable throwable,
@Advice.Local("otelSpan") Span span, @Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) { @Advice.Local("otelScope") Scope scope) {
if (scope == null) { if (scope == null) {
return; return;
} }
scope.close(); scope.close();
if (span == null) { if (context == null) {
// an existing span was found // an existing span was found
return; return;
} }
tracer().setPrincipal(span, request); tracer().setPrincipal(context, request);
if (throwable != null) { if (throwable != null) {
tracer().endExceptionally(span, throwable, response); tracer().endExceptionally(context, throwable, response);
return; return;
} }
@ -68,7 +65,9 @@ public class JettyHandlerAdvice {
// In case of async servlets wait for the actual response to be ready // In case of async servlets wait for the actual response to be ready
if (request.isAsyncStarted()) { if (request.isAsyncStarted()) {
try { try {
request.getAsyncContext().addListener(new TagSettingAsyncListener(responseHandled, span)); request
.getAsyncContext()
.addListener(new TagSettingAsyncListener(responseHandled, context));
} catch (IllegalStateException e) { } catch (IllegalStateException e) {
// org.eclipse.jetty.server.Request may throw an exception here if request became // org.eclipse.jetty.server.Request may throw an exception here if request became
// finished after check above. We just ignore that exception and move on. // finished after check above. We just ignore that exception and move on.
@ -77,7 +76,7 @@ public class JettyHandlerAdvice {
// Check again in case the request finished before adding the listener. // Check again in case the request finished before adding the listener.
if (!request.isAsyncStarted() && responseHandled.compareAndSet(false, true)) { if (!request.isAsyncStarted() && responseHandled.compareAndSet(false, true)) {
tracer().end(span, response); tracer().end(context, response);
} }
} }
} }

View File

@ -7,11 +7,9 @@ package io.opentelemetry.javaagent.instrumentation.netty.v3_8.server;
import static io.opentelemetry.javaagent.instrumentation.netty.v3_8.server.NettyHttpServerTracer.tracer; import static io.opentelemetry.javaagent.instrumentation.netty.v3_8.server.NettyHttpServerTracer.tracer;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context; import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope; import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.instrumentation.api.ContextStore; import io.opentelemetry.javaagent.instrumentation.api.ContextStore;
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
import io.opentelemetry.javaagent.instrumentation.netty.v3_8.ChannelTraceContext; import io.opentelemetry.javaagent.instrumentation.netty.v3_8.ChannelTraceContext;
import org.jboss.netty.channel.Channel; import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext; import org.jboss.netty.channel.ChannelHandlerContext;
@ -46,12 +44,13 @@ public class HttpServerRequestTracingHandler extends SimpleChannelUpstreamHandle
HttpRequest request = (HttpRequest) msg.getMessage(); HttpRequest request = (HttpRequest) msg.getMessage();
Context context = tracer().startSpan(request, ctx.getChannel(), "netty.request"); Context context =
Span span = Java8BytecodeBridge.spanFromContext(context); tracer().startSpan(request, ctx.getChannel(), channelTraceContext, "netty.request");
try (Scope ignored = tracer().startScope(span, channelTraceContext)) { try (Scope ignored = context.makeCurrent()) {
ctx.sendUpstream(msg); ctx.sendUpstream(msg);
// the span is ended normally in HttpServerResponseTracingHandler
} catch (Throwable throwable) { } catch (Throwable throwable) {
tracer().endExceptionally(span, throwable); tracer().endExceptionally(context, throwable);
throw throwable; throw throwable;
} }
} }

View File

@ -7,7 +7,6 @@ package io.opentelemetry.javaagent.instrumentation.netty.v3_8.server;
import static io.opentelemetry.javaagent.instrumentation.netty.v3_8.server.NettyHttpServerTracer.tracer; import static io.opentelemetry.javaagent.instrumentation.netty.v3_8.server.NettyHttpServerTracer.tracer;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context; import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope; import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.instrumentation.api.ContextStore; import io.opentelemetry.javaagent.instrumentation.api.ContextStore;
@ -37,13 +36,12 @@ public class HttpServerResponseTracingHandler extends SimpleChannelDownstreamHan
return; return;
} }
Span span = Span.fromContext(context);
try (Scope ignored = context.makeCurrent()) { try (Scope ignored = context.makeCurrent()) {
ctx.sendDownstream(msg); ctx.sendDownstream(msg);
} catch (Throwable throwable) { } catch (Throwable throwable) {
tracer().endExceptionally(span, throwable); tracer().endExceptionally(context, throwable);
throw throwable; throw throwable;
} }
tracer().end(span, (HttpResponse) msg.getMessage()); tracer().end(context, (HttpResponse) msg.getMessage());
} }
} }

View File

@ -11,10 +11,8 @@ import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpRequest;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context; import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope; import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
public class HttpServerRequestTracingHandler extends ChannelInboundHandlerAdapter { public class HttpServerRequestTracingHandler extends ChannelInboundHandlerAdapter {
@ -34,12 +32,12 @@ public class HttpServerRequestTracingHandler extends ChannelInboundHandlerAdapte
return; return;
} }
Context context = tracer().startSpan((HttpRequest) msg, channel, "netty.request"); Context context = tracer().startSpan((HttpRequest) msg, channel, channel, "netty.request");
Span span = Java8BytecodeBridge.spanFromContext(context); try (Scope ignored = context.makeCurrent()) {
try (Scope ignored = tracer().startScope(span, channel)) {
ctx.fireChannelRead(msg); ctx.fireChannelRead(msg);
// the span is ended normally in HttpServerResponseTracingHandler
} catch (Throwable throwable) { } catch (Throwable throwable) {
tracer().endExceptionally(span, throwable); tracer().endExceptionally(context, throwable);
throw throwable; throw throwable;
} }
} }

View File

@ -11,7 +11,6 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOutboundHandlerAdapter; import io.netty.channel.ChannelOutboundHandlerAdapter;
import io.netty.channel.ChannelPromise; import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.http.HttpResponse; import io.netty.handler.codec.http.HttpResponse;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context; import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope; import io.opentelemetry.context.Scope;
@ -25,13 +24,12 @@ public class HttpServerResponseTracingHandler extends ChannelOutboundHandlerAdap
return; return;
} }
Span span = Span.fromContext(context);
try (Scope ignored = context.makeCurrent()) { try (Scope ignored = context.makeCurrent()) {
ctx.write(msg, prm); ctx.write(msg, prm);
} catch (Throwable throwable) { } catch (Throwable throwable) {
tracer().endExceptionally(span, throwable); tracer().endExceptionally(context, throwable);
throw throwable; throw throwable;
} }
tracer().end(span, (HttpResponse) msg); tracer().end(context, (HttpResponse) msg);
} }
} }

View File

@ -11,10 +11,8 @@ import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpRequest;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context; import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope; import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
public class HttpServerRequestTracingHandler extends ChannelInboundHandlerAdapter { public class HttpServerRequestTracingHandler extends ChannelInboundHandlerAdapter {
@ -34,12 +32,12 @@ public class HttpServerRequestTracingHandler extends ChannelInboundHandlerAdapte
return; return;
} }
Context context = tracer().startSpan((HttpRequest) msg, channel, "netty.request"); Context context = tracer().startSpan((HttpRequest) msg, channel, channel, "netty.request");
Span span = Java8BytecodeBridge.spanFromContext(context); try (Scope ignored = context.makeCurrent()) {
try (Scope ignored = tracer().startScope(span, channel)) {
ctx.fireChannelRead(msg); ctx.fireChannelRead(msg);
// the span is ended normally in HttpServerResponseTracingHandler
} catch (Throwable throwable) { } catch (Throwable throwable) {
tracer().endExceptionally(span, throwable); tracer().endExceptionally(context, throwable);
throw throwable; throw throwable;
} }
} }

View File

@ -11,7 +11,6 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOutboundHandlerAdapter; import io.netty.channel.ChannelOutboundHandlerAdapter;
import io.netty.channel.ChannelPromise; import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.http.HttpResponse; import io.netty.handler.codec.http.HttpResponse;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context; import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope; import io.opentelemetry.context.Scope;
@ -25,13 +24,12 @@ public class HttpServerResponseTracingHandler extends ChannelOutboundHandlerAdap
return; return;
} }
Span span = Span.fromContext(context);
try (Scope ignored = context.makeCurrent()) { try (Scope ignored = context.makeCurrent()) {
ctx.write(msg, prm); ctx.write(msg, prm);
} catch (Throwable throwable) { } catch (Throwable throwable) {
tracer().endExceptionally(span, throwable); tracer().endExceptionally(context, throwable);
throw throwable; throw throwable;
} }
tracer().end(span, (HttpResponse) msg); tracer().end(context, (HttpResponse) msg);
} }
} }

View File

@ -7,11 +7,9 @@ package io.opentelemetry.javaagent.instrumentation.servlet.v2_2;
import static io.opentelemetry.javaagent.instrumentation.servlet.v2_2.Servlet2HttpServerTracer.tracer; import static io.opentelemetry.javaagent.instrumentation.servlet.v2_2.Servlet2HttpServerTracer.tracer;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context; import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope; import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.instrumentation.api.InstrumentationContext; import io.opentelemetry.javaagent.instrumentation.api.InstrumentationContext;
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
import javax.servlet.ServletRequest; import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse; import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -24,7 +22,7 @@ public class Servlet2Advice {
public static void onEnter( public static void onEnter(
@Advice.Argument(0) ServletRequest request, @Advice.Argument(0) ServletRequest request,
@Advice.Argument(value = 1, typing = Assigner.Typing.DYNAMIC) ServletResponse response, @Advice.Argument(value = 1, typing = Assigner.Typing.DYNAMIC) ServletResponse response,
@Advice.Local("otelSpan") Span span, @Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) { @Advice.Local("otelScope") Scope scope) {
if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) { if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) {
@ -37,9 +35,8 @@ public class Servlet2Advice {
return; return;
} }
Context ctx = tracer().startSpan(httpServletRequest); context = tracer().startSpan(httpServletRequest);
span = Java8BytecodeBridge.spanFromContext(ctx); scope = context.makeCurrent();
scope = tracer().startScope(span, httpServletRequest);
} }
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
@ -47,14 +44,14 @@ public class Servlet2Advice {
@Advice.Argument(0) ServletRequest request, @Advice.Argument(0) ServletRequest request,
@Advice.Argument(1) ServletResponse response, @Advice.Argument(1) ServletResponse response,
@Advice.Thrown Throwable throwable, @Advice.Thrown Throwable throwable,
@Advice.Local("otelSpan") Span span, @Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) { @Advice.Local("otelScope") Scope scope) {
if (scope == null) { if (scope == null) {
return; return;
} }
scope.close(); scope.close();
tracer().setPrincipal(span, (HttpServletRequest) request); tracer().setPrincipal(context, (HttpServletRequest) request);
Integer responseStatus = Integer responseStatus =
InstrumentationContext.get(ServletResponse.class, Integer.class).get(response); InstrumentationContext.get(ServletResponse.class, Integer.class).get(response);
@ -62,9 +59,9 @@ public class Servlet2Advice {
ResponseWithStatus responseWithStatus = ResponseWithStatus responseWithStatus =
new ResponseWithStatus((HttpServletResponse) response, responseStatus); new ResponseWithStatus((HttpServletResponse) response, responseStatus);
if (throwable == null) { if (throwable == null) {
tracer().end(span, responseWithStatus); tracer().end(context, responseWithStatus);
} else { } else {
tracer().endExceptionally(span, throwable, responseWithStatus); tracer().endExceptionally(context, throwable, responseWithStatus);
} }
} }
} }

View File

@ -7,10 +7,8 @@ package io.opentelemetry.javaagent.instrumentation.servlet.v3_0;
import static io.opentelemetry.javaagent.instrumentation.servlet.v3_0.Servlet3HttpServerTracer.tracer; import static io.opentelemetry.javaagent.instrumentation.servlet.v3_0.Servlet3HttpServerTracer.tracer;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context; import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope; import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import javax.servlet.ServletRequest; import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse; import javax.servlet.ServletResponse;
@ -24,7 +22,7 @@ public class Servlet3Advice {
public static void onEnter( public static void onEnter(
@Advice.Argument(value = 0, readOnly = false) ServletRequest request, @Advice.Argument(value = 0, readOnly = false) ServletRequest request,
@Advice.Argument(value = 1, readOnly = false) ServletResponse response, @Advice.Argument(value = 1, readOnly = false) ServletResponse response,
@Advice.Local("otelSpan") Span span, @Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) { @Advice.Local("otelScope") Scope scope) {
if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) { if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) {
return; return;
@ -42,9 +40,8 @@ public class Servlet3Advice {
return; return;
} }
Context ctx = tracer().startSpan(httpServletRequest); context = tracer().startSpan(httpServletRequest);
span = Java8BytecodeBridge.spanFromContext(ctx); scope = context.makeCurrent();
scope = tracer().startScope(span, httpServletRequest);
} }
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
@ -52,21 +49,21 @@ public class Servlet3Advice {
@Advice.Argument(0) ServletRequest request, @Advice.Argument(0) ServletRequest request,
@Advice.Argument(1) ServletResponse response, @Advice.Argument(1) ServletResponse response,
@Advice.Thrown Throwable throwable, @Advice.Thrown Throwable throwable,
@Advice.Local("otelSpan") Span span, @Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) { @Advice.Local("otelScope") Scope scope) {
if (scope == null) { if (scope == null) {
return; return;
} }
scope.close(); scope.close();
if (span == null) { if (context == null) {
// an existing span was found // an existing span was found
return; return;
} }
tracer().setPrincipal(span, (HttpServletRequest) request); tracer().setPrincipal(context, (HttpServletRequest) request);
if (throwable != null) { if (throwable != null) {
tracer().endExceptionally(span, throwable, (HttpServletResponse) response); tracer().endExceptionally(context, throwable, (HttpServletResponse) response);
return; return;
} }
@ -75,7 +72,9 @@ public class Servlet3Advice {
// In case of async servlets wait for the actual response to be ready // In case of async servlets wait for the actual response to be ready
if (request.isAsyncStarted()) { if (request.isAsyncStarted()) {
try { try {
request.getAsyncContext().addListener(new TagSettingAsyncListener(responseHandled, span)); request
.getAsyncContext()
.addListener(new TagSettingAsyncListener(responseHandled, context));
} catch (IllegalStateException e) { } catch (IllegalStateException e) {
// org.eclipse.jetty.server.Request may throw an exception here if request became // org.eclipse.jetty.server.Request may throw an exception here if request became
// finished after check above. We just ignore that exception and move on. // finished after check above. We just ignore that exception and move on.
@ -84,7 +83,7 @@ public class Servlet3Advice {
// Check again in case the request finished before adding the listener. // Check again in case the request finished before adding the listener.
if (!request.isAsyncStarted() && responseHandled.compareAndSet(false, true)) { if (!request.isAsyncStarted() && responseHandled.compareAndSet(false, true)) {
tracer().end(span, (HttpServletResponse) response); tracer().end(context, (HttpServletResponse) response);
} }
} }
} }

View File

@ -37,24 +37,25 @@ public class Servlet3HttpServerTracer extends ServletHttpServerTracer<HttpServle
@Override @Override
public void endExceptionally( public void endExceptionally(
Span span, Throwable throwable, HttpServletResponse response, long timestamp) { Context context, Throwable throwable, HttpServletResponse response, long timestamp) {
if (response.isCommitted()) { if (response.isCommitted()) {
super.endExceptionally(span, throwable, response, timestamp); super.endExceptionally(context, throwable, response, timestamp);
} else { } else {
// passing null response to super, in order to capture as 500 / INTERNAL, due to servlet spec // passing null response to super, in order to capture as 500 / INTERNAL, due to servlet spec
// https://javaee.github.io/servlet-spec/downloads/servlet-4.0/servlet-4_0_FINAL.pdf: // https://javaee.github.io/servlet-spec/downloads/servlet-4.0/servlet-4_0_FINAL.pdf:
// "If a servlet generates an error that is not handled by the error page mechanism as // "If a servlet generates an error that is not handled by the error page mechanism as
// described above, the container must ensure to send a response with status 500." // described above, the container must ensure to send a response with status 500."
super.endExceptionally(span, throwable, null, timestamp); super.endExceptionally(context, throwable, null, timestamp);
} }
} }
@Override @Override
public void end(Span span, HttpServletResponse response, long timestamp) { public void end(Context context, HttpServletResponse response, long timestamp) {
super.end(span, response, timestamp); super.end(context, response, timestamp);
} }
public void onTimeout(Span span, long timeout) { public void onTimeout(Context context, long timeout) {
Span span = Span.fromContext(context);
span.setStatus(StatusCode.ERROR); span.setStatus(StatusCode.ERROR);
span.setAttribute("timeout", timeout); span.setAttribute("timeout", timeout);
span.end(); span.end();

View File

@ -5,7 +5,7 @@
package io.opentelemetry.javaagent.instrumentation.servlet.v3_0; package io.opentelemetry.javaagent.instrumentation.servlet.v3_0;
import io.opentelemetry.api.trace.Span; import io.opentelemetry.context.Context;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import javax.servlet.AsyncEvent; import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener; import javax.servlet.AsyncListener;
@ -16,24 +16,24 @@ public class TagSettingAsyncListener implements AsyncListener {
new Servlet3HttpServerTracer(); new Servlet3HttpServerTracer();
private final AtomicBoolean responseHandled; private final AtomicBoolean responseHandled;
private final Span span; private final Context context;
public TagSettingAsyncListener(AtomicBoolean responseHandled, Span span) { public TagSettingAsyncListener(AtomicBoolean responseHandled, Context context) {
this.responseHandled = responseHandled; this.responseHandled = responseHandled;
this.span = span; this.context = context;
} }
@Override @Override
public void onComplete(AsyncEvent event) { public void onComplete(AsyncEvent event) {
if (responseHandled.compareAndSet(false, true)) { if (responseHandled.compareAndSet(false, true)) {
servletHttpServerTracer.end(span, (HttpServletResponse) event.getSuppliedResponse()); servletHttpServerTracer.end(context, (HttpServletResponse) event.getSuppliedResponse());
} }
} }
@Override @Override
public void onTimeout(AsyncEvent event) { public void onTimeout(AsyncEvent event) {
if (responseHandled.compareAndSet(false, true)) { if (responseHandled.compareAndSet(false, true)) {
servletHttpServerTracer.onTimeout(span, event.getAsyncContext().getTimeout()); servletHttpServerTracer.onTimeout(context, event.getAsyncContext().getTimeout());
} }
} }
@ -41,7 +41,7 @@ public class TagSettingAsyncListener implements AsyncListener {
public void onError(AsyncEvent event) { public void onError(AsyncEvent event) {
if (responseHandled.compareAndSet(false, true)) { if (responseHandled.compareAndSet(false, true)) {
servletHttpServerTracer.endExceptionally( servletHttpServerTracer.endExceptionally(
span, event.getThrowable(), (HttpServletResponse) event.getSuppliedResponse()); context, event.getThrowable(), (HttpServletResponse) event.getSuppliedResponse());
} }
} }

View File

@ -5,7 +5,6 @@
package io.opentelemetry.instrumentation.spring.webmvc; package io.opentelemetry.instrumentation.spring.webmvc;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Tracer; import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Context; import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope; import io.opentelemetry.context.Scope;
@ -31,13 +30,12 @@ public class WebMvcTracingFilter extends OncePerRequestFilter implements Ordered
public void doFilterInternal( public void doFilterInternal(
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException { throws ServletException, IOException {
Context ctx = tracer.startSpan(request, request, FILTER_CLASS + "." + FILTER_METHOD); Context ctx = tracer.startSpan(request, request, request, FILTER_CLASS + "." + FILTER_METHOD);
Span serverSpan = Span.fromContext(ctx); try (Scope ignored = ctx.makeCurrent()) {
try (Scope ignored = tracer.startScope(serverSpan, request)) {
filterChain.doFilter(request, response); filterChain.doFilter(request, response);
tracer.end(serverSpan, response); tracer.end(ctx, response);
} catch (Throwable t) { } catch (Throwable t) {
tracer.endExceptionally(serverSpan, t, response); tracer.endExceptionally(ctx, t, response);
throw t; throw t;
} }
} }