Changed instrumentation tracer" TRACER.startSpan() to never return `null` (#499)
This commit is contained in:
parent
3cd9ffa370
commit
4b665dcbe2
|
@ -51,10 +51,6 @@ public abstract class HttpServerTracer<REQUEST> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Span startSpan(REQUEST request, Method origin, String originType) {
|
public Span startSpan(REQUEST request, Method origin, String originType) {
|
||||||
if (getAttachedSpan(request) != null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
final Span.Builder builder =
|
final Span.Builder builder =
|
||||||
tracer
|
tracer
|
||||||
.spanBuilder(spanNameForMethod(origin))
|
.spanBuilder(spanNameForMethod(origin))
|
||||||
|
@ -132,10 +128,6 @@ public abstract class HttpServerTracer<REQUEST> {
|
||||||
// TODO set resource name from URL.
|
// TODO set resource name from URL.
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean sameTrace(Span oneSpan, Span otherSpan) {
|
|
||||||
return oneSpan.getContext().getTraceId().equals(otherSpan.getContext().getTraceId());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is used to generate an acceptable span (operation) name based on a given method
|
* This method is used to generate an acceptable span (operation) name based on a given method
|
||||||
* reference. Anonymous classes are named based on their parent.
|
* reference. Anonymous classes are named based on their parent.
|
||||||
|
|
|
@ -79,6 +79,10 @@ public class GrizzlyHttpHandlerInstrumentation extends Instrumenter.Default {
|
||||||
@Advice.Argument(0) final Request request,
|
@Advice.Argument(0) final Request request,
|
||||||
@Advice.Local("otelSpan") Span span,
|
@Advice.Local("otelSpan") Span span,
|
||||||
@Advice.Local("otelScope") Scope scope) {
|
@Advice.Local("otelScope") Scope scope) {
|
||||||
|
if (TRACER.getAttachedSpan(request) != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
request.addAfterServiceListener(SpanClosingListener.LISTENER);
|
request.addAfterServiceListener(SpanClosingListener.LISTENER);
|
||||||
|
|
||||||
span = TRACER.startSpan(request, method, null);
|
span = TRACER.startSpan(request, method, null);
|
||||||
|
@ -96,10 +100,6 @@ public class GrizzlyHttpHandlerInstrumentation extends Instrumenter.Default {
|
||||||
}
|
}
|
||||||
scope.close();
|
scope.close();
|
||||||
|
|
||||||
if (span == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (throwable != null) {
|
if (throwable != null) {
|
||||||
TRACER.endExceptionally(span, throwable, response.getStatus());
|
TRACER.endExceptionally(span, throwable, response.getStatus());
|
||||||
}
|
}
|
||||||
|
@ -108,7 +108,6 @@ public class GrizzlyHttpHandlerInstrumentation extends Instrumenter.Default {
|
||||||
|
|
||||||
public static class SpanClosingListener implements AfterServiceListener {
|
public static class SpanClosingListener implements AfterServiceListener {
|
||||||
public static final SpanClosingListener LISTENER = new SpanClosingListener();
|
public static final SpanClosingListener LISTENER = new SpanClosingListener();
|
||||||
public static final GrizzlyHttpServerTracer TRACER = new GrizzlyHttpServerTracer();
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAfterService(final Request request) {
|
public void onAfterService(final Request request) {
|
||||||
|
|
|
@ -75,11 +75,12 @@ public class GrizzlyHttpServerTracer extends HttpServerTracer<Request> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Span getAttachedSpan(Request request) {
|
public Span getAttachedSpan(Request request) {
|
||||||
Object span = request.getAttribute(SPAN_ATTRIBUTE);
|
Object span = request.getAttribute(SPAN_ATTRIBUTE);
|
||||||
return span instanceof Span ? (Span) span : null;
|
return span instanceof Span ? (Span) span : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void onRequest(Span span, Request request) {
|
public void onRequest(Span span, Request request) {
|
||||||
request.setAttribute("traceId", span.getContext().getTraceId().toLowerBase16());
|
request.setAttribute("traceId", span.getContext().getTraceId().toLowerBase16());
|
||||||
request.setAttribute("spanId", span.getContext().getSpanId().toLowerBase16());
|
request.setAttribute("spanId", span.getContext().getSpanId().toLowerBase16());
|
||||||
|
|
|
@ -38,12 +38,16 @@ public class Servlet2Advice {
|
||||||
@Advice.Local("otelSpan") Span span,
|
@Advice.Local("otelSpan") Span span,
|
||||||
@Advice.Local("otelScope") Scope scope) {
|
@Advice.Local("otelScope") Scope scope) {
|
||||||
|
|
||||||
if (!(request instanceof HttpServletRequest)) {
|
if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final HttpServletRequest httpServletRequest = (HttpServletRequest) request;
|
final HttpServletRequest httpServletRequest = (HttpServletRequest) request;
|
||||||
|
|
||||||
|
if (TRACER.getAttachedSpan(httpServletRequest) != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// For use by HttpServletResponseInstrumentation:
|
// For use by HttpServletResponseInstrumentation:
|
||||||
InstrumentationContext.get(HttpServletResponse.class, HttpServletRequest.class)
|
InstrumentationContext.get(HttpServletResponse.class, HttpServletRequest.class)
|
||||||
.put((HttpServletResponse) response, httpServletRequest);
|
.put((HttpServletResponse) response, httpServletRequest);
|
||||||
|
@ -64,21 +68,15 @@ public class Servlet2Advice {
|
||||||
}
|
}
|
||||||
scope.close();
|
scope.close();
|
||||||
|
|
||||||
if (request instanceof HttpServletRequest && response instanceof HttpServletResponse) {
|
TRACER.setPrincipal((HttpServletRequest) request);
|
||||||
TRACER.setPrincipal((HttpServletRequest) request);
|
|
||||||
|
|
||||||
if (span == null) {
|
Integer responseStatus =
|
||||||
return;
|
InstrumentationContext.get(ServletResponse.class, Integer.class).get(response);
|
||||||
}
|
|
||||||
|
|
||||||
Integer responseStatus =
|
if (throwable == null) {
|
||||||
InstrumentationContext.get(ServletResponse.class, Integer.class).get(response);
|
TRACER.end(span, responseStatus);
|
||||||
|
} else {
|
||||||
if (throwable == null) {
|
TRACER.endExceptionally(span, throwable, responseStatus);
|
||||||
TRACER.end(span, responseStatus);
|
|
||||||
} else {
|
|
||||||
TRACER.endExceptionally(span, throwable, responseStatus);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,30 +39,17 @@ public class Servlet3Advice {
|
||||||
@Advice.Argument(1) final ServletResponse response,
|
@Advice.Argument(1) final ServletResponse response,
|
||||||
@Advice.Local("otelSpan") Span span,
|
@Advice.Local("otelSpan") Span span,
|
||||||
@Advice.Local("otelScope") Scope scope) {
|
@Advice.Local("otelScope") Scope scope) {
|
||||||
if (!(request instanceof HttpServletRequest)) {
|
|
||||||
|
if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final HttpServletRequest httpServletRequest = (HttpServletRequest) request;
|
final HttpServletRequest httpServletRequest = (HttpServletRequest) request;
|
||||||
final Span existingSpan = TRACER.getAttachedSpan(httpServletRequest);
|
|
||||||
if (existingSpan != null) {
|
|
||||||
/*
|
|
||||||
Given request already has a span associated with it.
|
|
||||||
As there should not be nested spans of kind SERVER, we should NOT create a new span here.
|
|
||||||
|
|
||||||
But it may happen that there is no span in current Context or it is from a different trace.
|
Span attachedSpan = TRACER.getAttachedSpan(httpServletRequest);
|
||||||
E.g. in case of async servlet request processing we create span for incoming request in one thread,
|
if (attachedSpan != null) {
|
||||||
but actual request continues processing happens in another thread.
|
if (TRACER.needsRescoping(attachedSpan)) {
|
||||||
Depending on servlet container implementation, this processing may again arrive into this method.
|
scope = currentContextWith(attachedSpan);
|
||||||
E.g. Jetty handles async requests in a way that calls HttpServlet.service method twice.
|
|
||||||
|
|
||||||
In this case we have to put the span from the request into current context before continuing.
|
|
||||||
*/
|
|
||||||
final boolean spanContextWasLost = !TRACER.sameTrace(TRACER.getCurrentSpan(), existingSpan);
|
|
||||||
if (spanContextWasLost) {
|
|
||||||
// Put span from request attribute into current context.
|
|
||||||
// We did not create a new span here, so return null instead
|
|
||||||
scope = currentContextWith(existingSpan);
|
|
||||||
}
|
}
|
||||||
// We are inside nested servlet/filter, don't create new span
|
// We are inside nested servlet/filter, don't create new span
|
||||||
return;
|
return;
|
||||||
|
@ -88,34 +75,33 @@ public class Servlet3Advice {
|
||||||
}
|
}
|
||||||
scope.close();
|
scope.close();
|
||||||
|
|
||||||
if (request instanceof HttpServletRequest && response instanceof HttpServletResponse) {
|
if (span == null) {
|
||||||
TRACER.setPrincipal((HttpServletRequest) request);
|
// an existing span was found
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (throwable != null) {
|
TRACER.setPrincipal((HttpServletRequest) request);
|
||||||
TRACER.endExceptionally(span, throwable, ((HttpServletResponse) response).getStatus());
|
|
||||||
return;
|
if (throwable != null) {
|
||||||
|
TRACER.endExceptionally(span, throwable, ((HttpServletResponse) response).getStatus());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final AtomicBoolean responseHandled = new AtomicBoolean(false);
|
||||||
|
|
||||||
|
// In case of async servlets wait for the actual response to be ready
|
||||||
|
if (request.isAsyncStarted()) {
|
||||||
|
try {
|
||||||
|
request.getAsyncContext().addListener(new TagSettingAsyncListener(responseHandled, span));
|
||||||
|
} catch (final IllegalStateException e) {
|
||||||
|
// 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.
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (span == null) {
|
// Check again in case the request finished before adding the listener.
|
||||||
return;
|
if (!request.isAsyncStarted() && responseHandled.compareAndSet(false, true)) {
|
||||||
}
|
TRACER.end(span, ((HttpServletResponse) response).getStatus());
|
||||||
|
|
||||||
final AtomicBoolean responseHandled = new AtomicBoolean(false);
|
|
||||||
|
|
||||||
// In case of async servlets wait for the actual response to be ready
|
|
||||||
if (request.isAsyncStarted()) {
|
|
||||||
try {
|
|
||||||
request.getAsyncContext().addListener(new TagSettingAsyncListener(responseHandled, span));
|
|
||||||
} catch (final IllegalStateException e) {
|
|
||||||
// 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.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check again in case the request finished before adding the listener.
|
|
||||||
if (!request.isAsyncStarted() && responseHandled.compareAndSet(false, true)) {
|
|
||||||
TRACER.end(span, ((HttpServletResponse) response).getStatus());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||||
public class Servlet3HttpServerTracer extends ServletHttpServerTracer {
|
public class Servlet3HttpServerTracer extends ServletHttpServerTracer {
|
||||||
public static final Servlet3HttpServerTracer TRACER = new Servlet3HttpServerTracer();
|
public static final Servlet3HttpServerTracer TRACER = new Servlet3HttpServerTracer();
|
||||||
|
|
||||||
|
@Override
|
||||||
protected String getInstrumentationName() {
|
protected String getInstrumentationName() {
|
||||||
return "io.opentelemetry.auto.servlet-3.0";
|
return "io.opentelemetry.auto.servlet-3.0";
|
||||||
}
|
}
|
||||||
|
@ -44,6 +45,22 @@ public class Servlet3HttpServerTracer extends ServletHttpServerTracer {
|
||||||
span.end();
|
span.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Given request already has a span associated with it.
|
||||||
|
As there should not be nested spans of kind SERVER, we should NOT create a new span here.
|
||||||
|
|
||||||
|
But it may happen that there is no span in current Context or it is from a different trace.
|
||||||
|
E.g. in case of async servlet request processing we create span for incoming request in one thread,
|
||||||
|
but actual request continues processing happens in another thread.
|
||||||
|
Depending on servlet container implementation, this processing may again arrive into this method.
|
||||||
|
E.g. Jetty handles async requests in a way that calls HttpServlet.service method twice.
|
||||||
|
|
||||||
|
In this case we have to put the span from the request into current context before continuing.
|
||||||
|
*/
|
||||||
|
public boolean needsRescoping(Span attachedSpan) {
|
||||||
|
return !sameTrace(tracer.getCurrentSpan(), attachedSpan);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRequest(Span span, HttpServletRequest request) {
|
public void onRequest(Span span, HttpServletRequest request) {
|
||||||
super.onRequest(span, request);
|
super.onRequest(span, request);
|
||||||
|
@ -75,4 +92,8 @@ public class Servlet3HttpServerTracer extends ServletHttpServerTracer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean sameTrace(final Span oneSpan, final Span otherSpan) {
|
||||||
|
return oneSpan.getContext().getTraceId().equals(otherSpan.getContext().getTraceId());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public abstract class ServletHttpServerTracer extends HttpServerTracer<HttpServletRequest> {
|
public abstract class ServletHttpServerTracer extends HttpServerTracer<HttpServletRequest> {
|
||||||
|
|
||||||
|
@Override
|
||||||
protected String getVersion() {
|
protected String getVersion() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -73,6 +74,7 @@ public abstract class ServletHttpServerTracer extends HttpServerTracer<HttpServl
|
||||||
return request.getMethod();
|
return request.getMethod();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void onRequest(Span span, HttpServletRequest request) {
|
public void onRequest(Span span, HttpServletRequest request) {
|
||||||
// we do this e.g. so that servlet containers can use these values in their access logs
|
// we do this e.g. so that servlet containers can use these values in their access logs
|
||||||
request.setAttribute("traceId", span.getContext().getTraceId().toLowerBase16());
|
request.setAttribute("traceId", span.getContext().getTraceId().toLowerBase16());
|
||||||
|
@ -89,6 +91,7 @@ public abstract class ServletHttpServerTracer extends HttpServerTracer<HttpServl
|
||||||
return HttpServletRequestGetter.GETTER;
|
return HttpServletRequestGetter.GETTER;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
protected Throwable unwrapThrowable(Throwable throwable) {
|
protected Throwable unwrapThrowable(Throwable throwable) {
|
||||||
Throwable result = throwable;
|
Throwable result = throwable;
|
||||||
if (throwable instanceof ServletException && throwable.getCause() != null) {
|
if (throwable instanceof ServletException && throwable.getCause() != null) {
|
||||||
|
|
Loading…
Reference in New Issue