Use Context more in HttpClientTracer (#1811)

* Use Context more in HttpClientTracer

* Better http-url-connection response object

* Follow database semantic conv for elasticsearch-rest

* Remove unnecessary CallDepth tracking
This commit is contained in:
Trask Stalnaker 2020-12-03 11:47:08 -08:00 committed by GitHub
parent 54e3df3cd4
commit 65f54e450b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
79 changed files with 826 additions and 834 deletions

View File

@ -12,7 +12,6 @@ import io.opentelemetry.api.trace.SpanBuilder;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.api.trace.attributes.SemanticAttributes;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.context.propagation.TextMapPropagator;
import io.opentelemetry.context.propagation.TextMapPropagator.Setter;
import io.opentelemetry.instrumentation.api.tracer.utils.NetPeerUtils;
@ -60,60 +59,63 @@ public abstract class HttpClientTracer<REQUEST, CARRIER, RESPONSE> extends BaseT
super(tracer);
}
public Span startSpan(REQUEST request) {
return startSpan(request, -1);
public boolean shouldStartSpan(Context parentContext) {
return parentContext.get(CONTEXT_CLIENT_SPAN_KEY) == null;
}
public Span startSpan(REQUEST request, long startTimeNanos) {
return internalStartSpan(request, spanNameForRequest(request), startTimeNanos);
public Context startSpan(Context parentContext, REQUEST request, CARRIER carrier) {
return startSpan(parentContext, request, carrier, -1);
}
public Scope startScope(Span span, CARRIER carrier) {
Context context = Context.current().with(span);
public Context startSpan(
Context parentContext, REQUEST request, CARRIER carrier, long startTimeNanos) {
Span span =
internalStartSpan(parentContext, request, spanNameForRequest(request), startTimeNanos);
Setter<CARRIER> setter = getSetter();
if (setter == null) {
throw new IllegalStateException(
"getSetter() not defined but calling startScope(), either getSetter must be implemented or the scope should be setup manually");
}
Context context = parentContext.with(span).with(CONTEXT_CLIENT_SPAN_KEY, span);
OpenTelemetry.getGlobalPropagators().getTextMapPropagator().inject(context, carrier, setter);
context = context.with(CONTEXT_CLIENT_SPAN_KEY, span);
return context.makeCurrent();
return context;
}
public void end(Span span, RESPONSE response) {
end(span, response, -1);
public void end(Context context, RESPONSE response) {
end(context, response, -1);
}
public void end(Span span, RESPONSE response, long endTimeNanos) {
public void end(Context context, RESPONSE response, long endTimeNanos) {
Span span = Span.fromContext(context);
onResponse(span, response);
super.end(span, endTimeNanos);
}
public void endExceptionally(Span span, RESPONSE response, Throwable throwable) {
endExceptionally(span, response, throwable, -1);
public void end(Context context) {
Span span = Span.fromContext(context);
super.end(span);
}
public void endExceptionally(Context context, RESPONSE response, Throwable throwable) {
endExceptionally(context, response, throwable, -1);
}
public void endExceptionally(
Span span, RESPONSE response, Throwable throwable, long endTimeNanos) {
Context context, RESPONSE response, Throwable throwable, long endTimeNanos) {
Span span = Span.fromContext(context);
onResponse(span, response);
super.endExceptionally(span, throwable, endTimeNanos);
}
/**
* Returns a new client {@link Span} if there is no client {@link Span} in the current {@link
* Context}, or an invalid {@link Span} otherwise.
*/
private Span internalStartSpan(REQUEST request, String name, long startTimeNanos) {
Context context = Context.current();
Span clientSpan = context.get(CONTEXT_CLIENT_SPAN_KEY);
public void endExceptionally(Context context, Throwable throwable) {
Span span = Span.fromContext(context);
super.endExceptionally(span, throwable, -1);
}
if (clientSpan != null) {
// We don't want to create two client spans for a given client call, suppress inner spans.
return Span.getInvalid();
}
SpanBuilder spanBuilder = tracer.spanBuilder(name).setSpanKind(Kind.CLIENT).setParent(context);
private Span internalStartSpan(
Context parentContext, REQUEST request, String name, long startTimeNanos) {
SpanBuilder spanBuilder =
tracer.spanBuilder(name).setSpanKind(Kind.CLIENT).setParent(parentContext);
if (startTimeNanos > 0) {
spanBuilder.setStartTimestamp(startTimeNanos, TimeUnit.NANOSECONDS);
}

View File

@ -6,6 +6,7 @@
package io.opentelemetry.javaagent.instrumentation.akkahttp;
import static io.opentelemetry.javaagent.instrumentation.akkahttp.AkkaHttpClientTracer.tracer;
import static io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge.currentContext;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
@ -14,10 +15,9 @@ import akka.http.scaladsl.HttpExt;
import akka.http.scaladsl.model.HttpRequest;
import akka.http.scaladsl.model.HttpResponse;
import com.google.auto.service.AutoService;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.context.propagation.TextMapPropagator;
import io.opentelemetry.javaagent.instrumentation.api.CallDepthThreadLocalMap.Depth;
import io.opentelemetry.javaagent.tooling.InstrumentationModule;
import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
import java.util.Collections;
@ -70,23 +70,24 @@ public class AkkaHttpClientInstrumentationModule extends InstrumentationModule {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void methodEnter(
@Advice.Argument(value = 0, readOnly = false) HttpRequest request,
@Advice.Local("otelSpan") Span span,
@Advice.Local("otelScope") Scope scope,
@Advice.Local("otelCallDepth") Depth callDepth) {
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
/*
Versions 10.0 and 10.1 have slightly different structure that is hard to distinguish so here
we cast 'wider net' and avoid instrumenting twice.
In the future we may want to separate these, but since lots of code is reused we would need to come up
with way of continuing to reusing it.
*/
callDepth = tracer().getCallDepth();
if (callDepth.getAndIncrement() == 0) {
span = tracer().startSpan(request);
// Request is immutable, so we have to assign new value once we update headers
AkkaHttpHeaders headers = new AkkaHttpHeaders(request);
scope = tracer().startScope(span, headers);
request = headers.getRequest();
Context parentContext = currentContext();
if (!tracer().shouldStartSpan(parentContext)) {
return;
}
// Request is immutable, so we have to assign new value once we update headers
AkkaHttpHeaders headers = new AkkaHttpHeaders(request);
context = tracer().startSpan(parentContext, request, headers);
scope = context.makeCurrent();
request = headers.getRequest();
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
@ -95,33 +96,34 @@ public class AkkaHttpClientInstrumentationModule extends InstrumentationModule {
@Advice.This HttpExt thiz,
@Advice.Return Future<HttpResponse> responseFuture,
@Advice.Thrown Throwable throwable,
@Advice.Local("otelSpan") Span span,
@Advice.Local("otelScope") Scope scope,
@Advice.Local("otelCallDepth") Depth callDepth) {
if (callDepth.decrementAndGet() == 0 && scope != null) {
scope.close();
if (throwable == null) {
responseFuture.onComplete(new OnCompleteHandler(span), thiz.system().dispatcher());
} else {
tracer().endExceptionally(span, throwable);
}
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
if (scope == null) {
return;
}
scope.close();
if (throwable == null) {
responseFuture.onComplete(new OnCompleteHandler(context), thiz.system().dispatcher());
} else {
tracer().endExceptionally(context, throwable);
}
}
}
public static class OnCompleteHandler extends AbstractFunction1<Try<HttpResponse>, Void> {
private final Span span;
private final Context context;
public OnCompleteHandler(Span span) {
this.span = span;
public OnCompleteHandler(Context context) {
this.context = context;
}
@Override
public Void apply(Try<HttpResponse> result) {
if (result.isSuccess()) {
tracer().end(span, result.get());
tracer().end(context, result.get());
} else {
tracer().endExceptionally(span, result.failed().get());
tracer().endExceptionally(context, result.failed().get());
}
return null;
}

View File

@ -8,14 +8,11 @@ package io.opentelemetry.javaagent.instrumentation.akkahttp;
import static io.opentelemetry.javaagent.instrumentation.akkahttp.AkkaHttpClientInstrumentationModule.InjectAdapter.SETTER;
import akka.http.javadsl.model.HttpHeader;
import akka.http.scaladsl.HttpExt;
import akka.http.scaladsl.model.HttpRequest;
import akka.http.scaladsl.model.HttpResponse;
import io.opentelemetry.context.propagation.TextMapPropagator.Setter;
import io.opentelemetry.instrumentation.api.tracer.HttpClientTracer;
import io.opentelemetry.javaagent.instrumentation.akkahttp.AkkaHttpClientInstrumentationModule.AkkaHttpHeaders;
import io.opentelemetry.javaagent.instrumentation.api.CallDepthThreadLocalMap;
import io.opentelemetry.javaagent.instrumentation.api.CallDepthThreadLocalMap.Depth;
import java.net.URI;
import java.net.URISyntaxException;
@ -27,10 +24,6 @@ public class AkkaHttpClientTracer
return TRACER;
}
public Depth getCallDepth() {
return CallDepthThreadLocalMap.getCallDepth(HttpExt.class);
}
@Override
protected String method(HttpRequest httpRequest) {
return httpRequest.method().value();

View File

@ -5,8 +5,8 @@
package io.opentelemetry.javaagent.instrumentation.apachehttpasyncclient;
import static io.opentelemetry.instrumentation.api.tracer.HttpClientTracer.DEFAULT_SPAN_NAME;
import static io.opentelemetry.javaagent.instrumentation.apachehttpasyncclient.ApacheHttpAsyncClientTracer.tracer;
import static io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge.currentContext;
import static io.opentelemetry.javaagent.tooling.ClassLoaderMatcher.hasClassesNamed;
import static io.opentelemetry.javaagent.tooling.bytebuddy.matcher.AgentElementMatchers.implementsInterface;
import static java.util.Collections.singletonMap;
@ -15,11 +15,10 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Span.Kind;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
import java.io.IOException;
import java.util.Map;
@ -66,36 +65,41 @@ public class ApacheHttpAsyncClientInstrumentation implements TypeInstrumentation
public static class ClientAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static Span methodEnter(
public static void methodEnter(
@Advice.Argument(value = 0, readOnly = false) HttpAsyncRequestProducer requestProducer,
@Advice.Argument(2) HttpContext context,
@Advice.Argument(value = 3, readOnly = false) FutureCallback<?> futureCallback) {
@Advice.Argument(2) HttpContext httpContext,
@Advice.Argument(value = 3, readOnly = false) FutureCallback<?> futureCallback,
@Advice.Local("otelContext") Context context) {
Context parentContext = Java8BytecodeBridge.currentContext();
Span clientSpan = tracer().startSpan(DEFAULT_SPAN_NAME, Kind.CLIENT);
Context parentContext = currentContext();
if (!tracer().shouldStartSpan(parentContext)) {
return;
}
requestProducer = new DelegatingRequestProducer(clientSpan, requestProducer);
context = tracer().startSpan(parentContext);
requestProducer = new DelegatingRequestProducer(context, requestProducer);
futureCallback =
new TraceContinuedFutureCallback<>(parentContext, clientSpan, context, futureCallback);
return clientSpan;
new TraceContinuedFutureCallback<>(parentContext, context, httpContext, futureCallback);
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void methodExit(
@Advice.Enter Span span, @Advice.Return Object result, @Advice.Thrown Throwable throwable) {
if (throwable != null) {
tracer().endExceptionally(span, throwable);
@Advice.Return Object result,
@Advice.Thrown Throwable throwable,
@Advice.Local("otelContext") Context context) {
if (context != null && throwable != null) {
tracer().endExceptionally(context, throwable);
}
}
}
public static class DelegatingRequestProducer implements HttpAsyncRequestProducer {
Span span;
Context context;
HttpAsyncRequestProducer delegate;
public DelegatingRequestProducer(Span span, HttpAsyncRequestProducer delegate) {
this.span = span;
public DelegatingRequestProducer(Context context, HttpAsyncRequestProducer delegate) {
this.context = context;
this.delegate = delegate;
}
@ -107,13 +111,12 @@ public class ApacheHttpAsyncClientInstrumentation implements TypeInstrumentation
@Override
public HttpRequest generateRequest() throws IOException, HttpException {
HttpRequest request = delegate.generateRequest();
OpenTelemetry.getGlobalPropagators()
.getTextMapPropagator()
.inject(context, request, tracer().getSetter());
Span span = Span.fromContext(context);
span.updateName(tracer().spanNameForRequest(request));
tracer().onRequest(span, request);
// TODO (trask) expose inject separate from startScope, e.g. for async cases
Scope scope = tracer().startScope(span, request);
scope.close();
return request;
}
@ -150,22 +153,25 @@ public class ApacheHttpAsyncClientInstrumentation implements TypeInstrumentation
public static class TraceContinuedFutureCallback<T> implements FutureCallback<T> {
private final Context parentContext;
private final Span clientSpan;
private final HttpContext context;
private final Context context;
private final HttpContext httpContext;
private final FutureCallback<T> delegate;
public TraceContinuedFutureCallback(
Context parentContext, Span clientSpan, HttpContext context, FutureCallback<T> delegate) {
Context parentContext,
Context context,
HttpContext httpContext,
FutureCallback<T> delegate) {
this.parentContext = parentContext;
this.clientSpan = clientSpan;
this.context = context;
this.httpContext = httpContext;
// Note: this can be null in real life, so we have to handle this carefully
this.delegate = delegate;
}
@Override
public void completed(T result) {
tracer().end(clientSpan, getResponse(context));
tracer().end(context, getResponse(httpContext));
if (parentContext == null) {
completeDelegate(result);
@ -179,7 +185,7 @@ public class ApacheHttpAsyncClientInstrumentation implements TypeInstrumentation
@Override
public void failed(Exception ex) {
// end span before calling delegate
tracer().endExceptionally(clientSpan, getResponse(context), ex);
tracer().endExceptionally(context, getResponse(httpContext), ex);
if (parentContext == null) {
failDelegate(ex);
@ -193,7 +199,7 @@ public class ApacheHttpAsyncClientInstrumentation implements TypeInstrumentation
@Override
public void cancelled() {
// end span before calling delegate
tracer().end(clientSpan, getResponse(context));
tracer().end(context, getResponse(httpContext));
if (parentContext == null) {
cancelDelegate();

View File

@ -5,9 +5,11 @@
package io.opentelemetry.javaagent.instrumentation.apachehttpasyncclient;
import static io.opentelemetry.api.trace.Span.Kind.CLIENT;
import static io.opentelemetry.javaagent.instrumentation.apachehttpasyncclient.HttpHeadersInjectAdapter.SETTER;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.propagation.TextMapPropagator.Setter;
import io.opentelemetry.instrumentation.api.tracer.HttpClientTracer;
import java.net.URI;
@ -30,6 +32,16 @@ public class ApacheHttpAsyncClientTracer
return TRACER;
}
public Context startSpan(Context parentContext) {
Span span =
tracer
.spanBuilder(DEFAULT_SPAN_NAME)
.setSpanKind(CLIENT)
.setParent(parentContext)
.startSpan();
return parentContext.with(span).with(CONTEXT_CLIENT_SPAN_KEY, span);
}
@Override
protected String method(HttpRequest request) {
if (request instanceof HttpUriRequest) {

View File

@ -6,6 +6,7 @@
package io.opentelemetry.javaagent.instrumentation.apachehttpclient.v2_0;
import static io.opentelemetry.javaagent.instrumentation.apachehttpclient.v2_0.CommonsHttpClientTracer.tracer;
import static io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge.currentContext;
import static io.opentelemetry.javaagent.tooling.ClassLoaderMatcher.hasClassesNamed;
import static io.opentelemetry.javaagent.tooling.bytebuddy.matcher.AgentElementMatchers.extendsClass;
import static java.util.Collections.singletonList;
@ -16,9 +17,8 @@ import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
import com.google.auto.service.AutoService;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.instrumentation.api.CallDepthThreadLocalMap.Depth;
import io.opentelemetry.javaagent.tooling.InstrumentationModule;
import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
import java.util.List;
@ -67,34 +67,32 @@ public class ApacheHttpClientInstrumentationModule extends InstrumentationModule
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void methodEnter(
@Advice.Argument(1) HttpMethod httpMethod,
@Advice.Local("otelSpan") Span span,
@Advice.Local("otelScope") Scope scope,
@Advice.Local("otelCallDepth") Depth callDepth) {
callDepth = tracer().getCallDepth();
if (callDepth.getAndIncrement() == 0) {
span = tracer().startSpan(httpMethod);
if (span.getSpanContext().isValid()) {
scope = tracer().startScope(span, httpMethod);
}
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
Context parentContext = currentContext();
if (!tracer().shouldStartSpan(parentContext)) {
return;
}
context = tracer().startSpan(parentContext, httpMethod, httpMethod);
scope = context.makeCurrent();
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void methodExit(
@Advice.Argument(1) HttpMethod httpMethod,
@Advice.Thrown Throwable throwable,
@Advice.Local("otelSpan") Span span,
@Advice.Local("otelScope") Scope scope,
@Advice.Local("otelCallDepth") Depth callDepth) {
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
if (scope == null) {
return;
}
if (callDepth.decrementAndGet() == 0 && scope != null) {
scope.close();
if (throwable == null) {
tracer().end(span, httpMethod);
} else {
tracer().endExceptionally(span, httpMethod, throwable);
}
scope.close();
if (throwable == null) {
tracer().end(context, httpMethod);
} else {
tracer().endExceptionally(context, httpMethod, throwable);
}
}
}

View File

@ -7,12 +7,9 @@ package io.opentelemetry.javaagent.instrumentation.apachehttpclient.v2_0;
import io.opentelemetry.context.propagation.TextMapPropagator.Setter;
import io.opentelemetry.instrumentation.api.tracer.HttpClientTracer;
import io.opentelemetry.javaagent.instrumentation.api.CallDepthThreadLocalMap;
import io.opentelemetry.javaagent.instrumentation.api.CallDepthThreadLocalMap.Depth;
import java.net.URI;
import java.net.URISyntaxException;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.StatusLine;
import org.apache.commons.httpclient.URIException;
@ -29,10 +26,6 @@ public class CommonsHttpClientTracer extends HttpClientTracer<HttpMethod, HttpMe
return "io.opentelemetry.javaagent.apache-httpclient";
}
public Depth getCallDepth() {
return CallDepthThreadLocalMap.getCallDepth(HttpClient.class);
}
@Override
protected String method(HttpMethod httpMethod) {
return httpMethod.getName();

View File

@ -8,44 +8,20 @@ package io.opentelemetry.javaagent.instrumentation.apachehttpclient.v4_0;
import static io.opentelemetry.javaagent.instrumentation.apachehttpclient.v4_0.ApacheHttpClientTracer.tracer;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.instrumentation.api.CallDepthThreadLocalMap;
import io.opentelemetry.javaagent.instrumentation.api.SpanWithScope;
import io.opentelemetry.context.Context;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpUriRequest;
public class ApacheHttpClientHelper {
public static SpanWithScope doMethodEnter(HttpUriRequest request) {
Span span = tracer().startSpan(request);
Scope scope = tracer().startScope(span, request);
return new SpanWithScope(span, scope);
}
public static void doMethodExit(Context context, Object result, Throwable throwable) {
if (result instanceof HttpResponse) {
tracer().onResponse(Span.fromContext(context), (HttpResponse) result);
} // else they probably provided a ResponseHandler
public static void doMethodExitAndResetCallDepthThread(
SpanWithScope spanWithScope, Object result, Throwable throwable) {
if (spanWithScope == null) {
return;
}
CallDepthThreadLocalMap.reset(HttpClient.class);
doMethodExit(spanWithScope, result, throwable);
}
public static void doMethodExit(SpanWithScope spanWithScope, Object result, Throwable throwable) {
try {
Span span = spanWithScope.getSpan();
if (result instanceof HttpResponse) {
tracer().onResponse(span, (HttpResponse) result);
} // else they probably provided a ResponseHandler
if (throwable != null) {
tracer().endExceptionally(span, throwable);
} else {
tracer().end(span);
}
} finally {
spanWithScope.closeScope();
if (throwable != null) {
tracer().endExceptionally(context, throwable);
} else {
tracer().end(context);
}
}
}

View File

@ -5,6 +5,8 @@
package io.opentelemetry.javaagent.instrumentation.apachehttpclient.v4_0;
import static io.opentelemetry.javaagent.instrumentation.apachehttpclient.v4_0.ApacheHttpClientTracer.tracer;
import static io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge.currentContext;
import static io.opentelemetry.javaagent.tooling.ClassLoaderMatcher.hasClassesNamed;
import static io.opentelemetry.javaagent.tooling.bytebuddy.matcher.AgentElementMatchers.implementsInterface;
import static java.util.Collections.singletonList;
@ -16,8 +18,8 @@ import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.instrumentation.api.CallDepthThreadLocalMap;
import io.opentelemetry.javaagent.instrumentation.api.SpanWithScope;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.tooling.InstrumentationModule;
import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
import java.util.HashMap;
@ -30,7 +32,6 @@ import net.bytebuddy.implementation.bytecode.assign.Assigner;
import net.bytebuddy.matcher.ElementMatcher;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpUriRequest;
@ -147,90 +148,117 @@ public class ApacheHttpClientInstrumentationModule extends InstrumentationModule
public static class UriRequestAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static SpanWithScope methodEnter(@Advice.Argument(0) HttpUriRequest request) {
int callDepth = CallDepthThreadLocalMap.incrementCallDepth(HttpClient.class);
if (callDepth > 0) {
return null;
public static void methodEnter(
@Advice.Argument(0) HttpUriRequest request,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
Context parentContext = currentContext();
if (!tracer().shouldStartSpan(parentContext)) {
return;
}
return ApacheHttpClientHelper.doMethodEnter(request);
context = tracer().startSpan(parentContext, request, request);
scope = context.makeCurrent();
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void methodExit(
@Advice.Enter SpanWithScope spanWithScope,
@Advice.Return Object result,
@Advice.Thrown Throwable throwable) {
ApacheHttpClientHelper.doMethodExitAndResetCallDepthThread(spanWithScope, result, throwable);
@Advice.Thrown Throwable throwable,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
if (scope == null) {
return;
}
scope.close();
ApacheHttpClientHelper.doMethodExit(context, result, throwable);
}
}
public static class UriRequestWithHandlerAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static SpanWithScope methodEnter(
public static void methodEnter(
@Advice.Argument(0) HttpUriRequest request,
@Advice.Argument(
value = 1,
optional = true,
typing = Assigner.Typing.DYNAMIC,
readOnly = false)
Object handler) {
int callDepth = CallDepthThreadLocalMap.incrementCallDepth(HttpClient.class);
if (callDepth > 0) {
return null;
Object handler,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
Context parentContext = currentContext();
if (!tracer().shouldStartSpan(parentContext)) {
return;
}
SpanWithScope spanWithScope = ApacheHttpClientHelper.doMethodEnter(request);
context = tracer().startSpan(parentContext, request, request);
scope = context.makeCurrent();
// Wrap the handler so we capture the status code
if (handler instanceof ResponseHandler) {
handler =
new WrappingStatusSettingResponseHandler(
spanWithScope.getSpan(), (ResponseHandler) handler);
handler = new WrappingStatusSettingResponseHandler(context, (ResponseHandler) handler);
}
return spanWithScope;
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void methodExit(
@Advice.Enter SpanWithScope spanWithScope,
@Advice.Return Object result,
@Advice.Thrown Throwable throwable) {
ApacheHttpClientHelper.doMethodExitAndResetCallDepthThread(spanWithScope, result, throwable);
@Advice.Thrown Throwable throwable,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
if (scope == null) {
return;
}
scope.close();
ApacheHttpClientHelper.doMethodExit(context, result, throwable);
}
}
public static class RequestAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static SpanWithScope methodEnter(
@Advice.Argument(0) HttpHost host, @Advice.Argument(1) HttpRequest request) {
int callDepth = CallDepthThreadLocalMap.incrementCallDepth(HttpClient.class);
if (callDepth > 0) {
return null;
public static void methodEnter(
@Advice.Argument(0) HttpHost host,
@Advice.Argument(1) HttpRequest request,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
Context parentContext = currentContext();
if (!tracer().shouldStartSpan(parentContext)) {
return;
}
HttpUriRequest httpUriRequest;
if (request instanceof HttpUriRequest) {
return ApacheHttpClientHelper.doMethodEnter((HttpUriRequest) request);
httpUriRequest = (HttpUriRequest) request;
} else {
return ApacheHttpClientHelper.doMethodEnter(
new HostAndRequestAsHttpUriRequest(host, request));
httpUriRequest = new HostAndRequestAsHttpUriRequest(host, request);
}
context = tracer().startSpan(parentContext, httpUriRequest, httpUriRequest);
scope = context.makeCurrent();
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void methodExit(
@Advice.Enter SpanWithScope spanWithScope,
@Advice.Return Object result,
@Advice.Thrown Throwable throwable) {
ApacheHttpClientHelper.doMethodExitAndResetCallDepthThread(spanWithScope, result, throwable);
@Advice.Thrown Throwable throwable,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
if (scope == null) {
return;
}
scope.close();
ApacheHttpClientHelper.doMethodExit(context, result, throwable);
}
}
public static class RequestWithHandlerAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static SpanWithScope methodEnter(
public static void methodEnter(
@Advice.Argument(0) HttpHost host,
@Advice.Argument(1) HttpRequest request,
@Advice.Argument(
@ -238,36 +266,41 @@ public class ApacheHttpClientInstrumentationModule extends InstrumentationModule
optional = true,
typing = Assigner.Typing.DYNAMIC,
readOnly = false)
Object handler) {
int callDepth = CallDepthThreadLocalMap.incrementCallDepth(HttpClient.class);
if (callDepth > 0) {
return null;
Object handler,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
Context parentContext = currentContext();
if (!tracer().shouldStartSpan(parentContext)) {
return;
}
SpanWithScope spanWithScope;
HttpUriRequest httpUriRequest;
if (request instanceof HttpUriRequest) {
spanWithScope = ApacheHttpClientHelper.doMethodEnter((HttpUriRequest) request);
httpUriRequest = (HttpUriRequest) request;
} else {
spanWithScope =
ApacheHttpClientHelper.doMethodEnter(new HostAndRequestAsHttpUriRequest(host, request));
httpUriRequest = new HostAndRequestAsHttpUriRequest(host, request);
}
context = tracer().startSpan(parentContext, httpUriRequest, httpUriRequest);
scope = context.makeCurrent();
// Wrap the handler so we capture the status code
if (handler instanceof ResponseHandler) {
handler =
new WrappingStatusSettingResponseHandler(
spanWithScope.getSpan(), (ResponseHandler) handler);
handler = new WrappingStatusSettingResponseHandler(context, (ResponseHandler) handler);
}
return spanWithScope;
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void methodExit(
@Advice.Enter SpanWithScope spanWithScope,
@Advice.Return Object result,
@Advice.Thrown Throwable throwable) {
ApacheHttpClientHelper.doMethodExitAndResetCallDepthThread(spanWithScope, result, throwable);
@Advice.Thrown Throwable throwable,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
if (scope == null) {
return;
}
scope.close();
ApacheHttpClientHelper.doMethodExit(context, result, throwable);
}
}
}

View File

@ -17,7 +17,7 @@ import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpUriRequest;
import org.checkerframework.checker.nullness.qual.Nullable;
class ApacheHttpClientTracer
public class ApacheHttpClientTracer
extends HttpClientTracer<HttpUriRequest, HttpUriRequest, HttpResponse> {
private static final ApacheHttpClientTracer TRACER = new ApacheHttpClientTracer();

View File

@ -0,0 +1,27 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.apachehttpclient.v4_0;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
public class ContextScopePair {
private final Context context;
private final Scope scope;
public ContextScopePair(Context context, Scope scope) {
this.context = context;
this.scope = scope;
}
public Context getContext() {
return context;
}
public void closeScope() {
scope.close();
}
}

View File

@ -8,24 +8,24 @@ package io.opentelemetry.javaagent.instrumentation.apachehttpclient.v4_0;
import static io.opentelemetry.javaagent.instrumentation.apachehttpclient.v4_0.ApacheHttpClientTracer.tracer;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import java.io.IOException;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.ResponseHandler;
public class WrappingStatusSettingResponseHandler implements ResponseHandler {
final Span span;
final Context context;
final ResponseHandler handler;
public WrappingStatusSettingResponseHandler(Span span, ResponseHandler handler) {
this.span = span;
public WrappingStatusSettingResponseHandler(Context context, ResponseHandler handler) {
this.context = context;
this.handler = handler;
}
@Override
public Object handleResponse(HttpResponse response) throws ClientProtocolException, IOException {
if (null != span) {
tracer().onResponse(span, response);
public Object handleResponse(HttpResponse response) throws IOException {
if (context != null) {
tracer().onResponse(Span.fromContext(context), response);
}
return handler.handleResponse(response);
}

View File

@ -13,6 +13,7 @@ import com.linecorp.armeria.common.HttpResponse;
import com.linecorp.armeria.common.logging.RequestLogProperty;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.instrumentation.api.tracer.utils.NetPeerUtils;
import java.util.concurrent.TimeUnit;
@ -52,8 +53,9 @@ public class OpenTelemetryClient extends SimpleDecoratingHttpClient {
long requestStartTimeMicros =
ctx.log().ensureAvailable(RequestLogProperty.REQUEST_START_TIME).requestStartTimeMicros();
long requestStartTimeNanos = TimeUnit.MICROSECONDS.toNanos(requestStartTimeMicros);
Span span = clientTracer.startSpan(ctx, requestStartTimeNanos);
Context context = clientTracer.startSpan(Context.current(), ctx, ctx, requestStartTimeNanos);
Span span = Span.fromContext(context);
if (span.isRecording()) {
ctx.log()
.whenComplete()
@ -64,14 +66,14 @@ public class OpenTelemetryClient extends SimpleDecoratingHttpClient {
long requestEndTimeNanos = requestStartTimeNanos + log.responseDurationNanos();
if (log.responseCause() != null) {
clientTracer.endExceptionally(
span, log, log.responseCause(), requestEndTimeNanos);
context, log, log.responseCause(), requestEndTimeNanos);
} else {
clientTracer.end(span, log, requestEndTimeNanos);
clientTracer.end(context, log, requestEndTimeNanos);
}
});
}
try (Scope ignored = clientTracer.startScope(span, ctx)) {
try (Scope ignored = context.makeCurrent()) {
return unwrap().execute(ctx, req);
}
}

View File

@ -5,32 +5,40 @@
package io.opentelemetry.javaagent.instrumentation.asynchttpclient;
import static io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge.currentContext;
import static io.opentelemetry.javaagent.instrumentation.asynchttpclient.AsyncHttpClientTracer.tracer;
import com.ning.http.client.AsyncHandler;
import com.ning.http.client.Request;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.instrumentation.api.InstrumentationContext;
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
import io.opentelemetry.javaagent.instrumentation.api.Pair;
import net.bytebuddy.asm.Advice;
public class RequestAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static Scope onEnter(
@Advice.Argument(0) Request request, @Advice.Argument(1) AsyncHandler<?> handler) {
Context parentContext = Java8BytecodeBridge.currentContext();
public static void onEnter(
@Advice.Argument(0) Request request,
@Advice.Argument(1) AsyncHandler<?> handler,
@Advice.Local("otelScope") Scope scope) {
Context parentContext = currentContext();
if (!tracer().shouldStartSpan(parentContext)) {
return;
}
Span span = AsyncHttpClientTracer.tracer().startSpan(request);
Context context = tracer().startSpan(parentContext, request, request);
InstrumentationContext.get(AsyncHandler.class, Pair.class)
.put(handler, Pair.of(parentContext, span));
return AsyncHttpClientTracer.tracer().startScope(span, request);
.put(handler, Pair.of(parentContext, context));
scope = context.makeCurrent();
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void onExit(@Advice.Enter Scope scope) {
// span closed in ClientResponseAdvice
scope.close();
public static void onExit(@Advice.Local("otelScope") Scope scope) {
if (scope != null) {
scope.close();
}
// span ended in ClientResponseAdvice
}
}

View File

@ -8,7 +8,6 @@ package io.opentelemetry.javaagent.instrumentation.asynchttpclient;
import com.ning.http.client.AsyncCompletionHandler;
import com.ning.http.client.AsyncHandler;
import com.ning.http.client.Response;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.instrumentation.api.ContextStore;
@ -26,14 +25,13 @@ public class ResponseAdvice {
// After response was handled by user provided handler.
ContextStore<AsyncHandler, Pair> contextStore =
InstrumentationContext.get(AsyncHandler.class, Pair.class);
Pair<Context, Span> spanWithParent = contextStore.get(handler);
if (null != spanWithParent) {
contextStore.put(handler, null);
Pair<Context, Context> parentAndChildContext = contextStore.get(handler);
if (parentAndChildContext == null) {
return null;
}
if (spanWithParent.hasRight()) {
AsyncHttpClientTracer.tracer().end(spanWithParent.getRight(), response);
}
return spanWithParent.hasLeft() ? spanWithParent.getLeft().makeCurrent() : null;
contextStore.put(handler, null);
AsyncHttpClientTracer.tracer().end(parentAndChildContext.getRight(), response);
return parentAndChildContext.getLeft().makeCurrent();
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)

View File

@ -6,7 +6,7 @@
package io.opentelemetry.javaagent.instrumentation.awssdk.v1_11;
import static io.opentelemetry.javaagent.instrumentation.awssdk.v1_11.AwsSdkClientTracer.tracer;
import static io.opentelemetry.javaagent.instrumentation.awssdk.v1_11.RequestMeta.SPAN_SCOPE_PAIR_CONTEXT_KEY;
import static io.opentelemetry.javaagent.instrumentation.awssdk.v1_11.RequestMeta.CONTEXT_SCOPE_PAIR_CONTEXT_KEY;
import static java.util.Collections.singletonMap;
import static net.bytebuddy.matcher.ElementMatchers.isAbstract;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
@ -16,7 +16,6 @@ import static net.bytebuddy.matcher.ElementMatchers.not;
import com.amazonaws.AmazonClientException;
import com.amazonaws.Request;
import com.amazonaws.handlers.RequestHandler2;
import io.opentelemetry.javaagent.instrumentation.api.SpanWithScope;
import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
import java.util.Map;
import net.bytebuddy.asm.Advice;
@ -49,10 +48,10 @@ public class AwsHttpClientInstrumentation implements TypeInstrumentation {
@Advice.Argument(value = 0, optional = true) Request<?> request,
@Advice.Thrown Throwable throwable) {
if (throwable != null) {
SpanWithScope scope = request.getHandlerContext(SPAN_SCOPE_PAIR_CONTEXT_KEY);
ContextScopePair scope = request.getHandlerContext(CONTEXT_SCOPE_PAIR_CONTEXT_KEY);
if (scope != null) {
request.addHandlerContext(SPAN_SCOPE_PAIR_CONTEXT_KEY, null);
tracer().endExceptionally(scope.getSpan(), throwable);
request.addHandlerContext(CONTEXT_SCOPE_PAIR_CONTEXT_KEY, null);
tracer().endExceptionally(scope.getContext(), throwable);
scope.closeScope();
}
}

View File

@ -10,6 +10,7 @@ import com.amazonaws.AmazonWebServiceResponse;
import com.amazonaws.Request;
import com.amazonaws.Response;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.propagation.TextMapPropagator.Setter;
import io.opentelemetry.instrumentation.api.tracer.HttpClientTracer;
import java.net.URI;
@ -39,8 +40,9 @@ public class AwsSdkClientTracer extends HttpClientTracer<Request<?>, Request<?>,
return qualifiedOperation(awsServiceName, awsOperation);
}
public Span startSpan(Request<?> request, RequestMeta requestMeta) {
Span span = super.startSpan(request);
public Context startSpan(Context parentContext, Request<?> request, RequestMeta requestMeta) {
Context context = super.startSpan(parentContext, request, request);
Span span = Span.fromContext(context);
String awsServiceName = request.getServiceName();
AmazonWebServiceRequest originalRequest = request.getOriginalRequest();
@ -58,7 +60,7 @@ public class AwsSdkClientTracer extends HttpClientTracer<Request<?>, Request<?>,
span.setAttribute("aws.stream.name", requestMeta.getStreamName());
span.setAttribute("aws.table.name", requestMeta.getTableName());
}
return span;
return context;
}
@Override

View File

@ -0,0 +1,27 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.awssdk.v1_11;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
public class ContextScopePair {
private final Context context;
private final Scope scope;
public ContextScopePair(Context context, Scope scope) {
this.context = context;
this.scope = scope;
}
public Context getContext() {
return context;
}
public void closeScope() {
scope.close();
}
}

View File

@ -6,7 +6,7 @@
package io.opentelemetry.javaagent.instrumentation.awssdk.v1_11;
import static io.opentelemetry.javaagent.instrumentation.awssdk.v1_11.AwsSdkClientTracer.tracer;
import static io.opentelemetry.javaagent.instrumentation.awssdk.v1_11.RequestMeta.SPAN_SCOPE_PAIR_CONTEXT_KEY;
import static io.opentelemetry.javaagent.instrumentation.awssdk.v1_11.RequestMeta.CONTEXT_SCOPE_PAIR_CONTEXT_KEY;
import static java.util.Collections.singletonMap;
import static net.bytebuddy.matcher.ElementMatchers.isAbstract;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
@ -14,7 +14,6 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.not;
import com.amazonaws.Request;
import io.opentelemetry.javaagent.instrumentation.api.SpanWithScope;
import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
import java.util.Map;
import net.bytebuddy.asm.Advice;
@ -45,10 +44,10 @@ public class RequestExecutorInstrumentation implements TypeInstrumentation {
public static void methodExit(
@Advice.FieldValue("request") Request<?> request, @Advice.Thrown Throwable throwable) {
if (throwable != null) {
SpanWithScope scope = request.getHandlerContext(SPAN_SCOPE_PAIR_CONTEXT_KEY);
ContextScopePair scope = request.getHandlerContext(CONTEXT_SCOPE_PAIR_CONTEXT_KEY);
if (scope != null) {
request.addHandlerContext(SPAN_SCOPE_PAIR_CONTEXT_KEY, null);
tracer().endExceptionally(scope.getSpan(), throwable);
request.addHandlerContext(CONTEXT_SCOPE_PAIR_CONTEXT_KEY, null);
tracer().endExceptionally(scope.getContext(), throwable);
scope.closeScope();
}
}

View File

@ -6,14 +6,13 @@
package io.opentelemetry.javaagent.instrumentation.awssdk.v1_11;
import com.amazonaws.handlers.HandlerContextKey;
import io.opentelemetry.javaagent.instrumentation.api.SpanWithScope;
import java.util.Objects;
public class RequestMeta {
// Note: aws1.x sdk doesn't have any truly async clients so we can store scope in request context
// safely.
public static final HandlerContextKey<SpanWithScope> SPAN_SCOPE_PAIR_CONTEXT_KEY =
new HandlerContextKey<>("io.opentelemetry.auto.SpanWithScope");
public static final HandlerContextKey<ContextScopePair> CONTEXT_SCOPE_PAIR_CONTEXT_KEY =
new HandlerContextKey<>(RequestMeta.class.getName() + ".ContextSpanPair");
private String bucketName;
private String queueUrl;

View File

@ -6,16 +6,15 @@
package io.opentelemetry.javaagent.instrumentation.awssdk.v1_11;
import static io.opentelemetry.javaagent.instrumentation.awssdk.v1_11.AwsSdkClientTracer.tracer;
import static io.opentelemetry.javaagent.instrumentation.awssdk.v1_11.RequestMeta.SPAN_SCOPE_PAIR_CONTEXT_KEY;
import static io.opentelemetry.javaagent.instrumentation.awssdk.v1_11.RequestMeta.CONTEXT_SCOPE_PAIR_CONTEXT_KEY;
import com.amazonaws.AmazonWebServiceRequest;
import com.amazonaws.Request;
import com.amazonaws.Response;
import com.amazonaws.handlers.RequestHandler2;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.instrumentation.api.ContextStore;
import io.opentelemetry.javaagent.instrumentation.api.SpanWithScope;
/** Tracing Request Handler. */
public class TracingRequestHandler extends RequestHandler2 {
@ -30,28 +29,32 @@ public class TracingRequestHandler extends RequestHandler2 {
public void beforeRequest(Request<?> request) {
AmazonWebServiceRequest originalRequest = request.getOriginalRequest();
RequestMeta requestMeta = contextStore.get(originalRequest);
Span span = tracer().startSpan(request, requestMeta);
Scope scope = tracer().startScope(span, request);
request.addHandlerContext(SPAN_SCOPE_PAIR_CONTEXT_KEY, new SpanWithScope(span, scope));
Context parentContext = Context.current();
if (tracer().shouldStartSpan(parentContext)) {
Context context = tracer().startSpan(parentContext, request, requestMeta);
Scope scope = context.makeCurrent();
request.addHandlerContext(
CONTEXT_SCOPE_PAIR_CONTEXT_KEY, new ContextScopePair(context, scope));
}
}
@Override
public void afterResponse(Request<?> request, Response<?> response) {
SpanWithScope scope = request.getHandlerContext(SPAN_SCOPE_PAIR_CONTEXT_KEY);
ContextScopePair scope = request.getHandlerContext(CONTEXT_SCOPE_PAIR_CONTEXT_KEY);
if (scope != null) {
request.addHandlerContext(SPAN_SCOPE_PAIR_CONTEXT_KEY, null);
request.addHandlerContext(CONTEXT_SCOPE_PAIR_CONTEXT_KEY, null);
scope.closeScope();
tracer().end(scope.getSpan(), response);
tracer().end(scope.getContext(), response);
}
}
@Override
public void afterError(Request<?> request, Response<?> response, Exception e) {
SpanWithScope scope = request.getHandlerContext(SPAN_SCOPE_PAIR_CONTEXT_KEY);
ContextScopePair scope = request.getHandlerContext(CONTEXT_SCOPE_PAIR_CONTEXT_KEY);
if (scope != null) {
request.addHandlerContext(SPAN_SCOPE_PAIR_CONTEXT_KEY, null);
request.addHandlerContext(CONTEXT_SCOPE_PAIR_CONTEXT_KEY, null);
scope.closeScope();
tracer().endExceptionally(scope.getSpan(), response, e);
tracer().endExceptionally(scope.getContext(), response, e);
}
}
}

View File

@ -58,15 +58,6 @@ public class AwsSdk {
return new TracingExecutionInterceptor(kind);
}
/**
* Returns the {@link Span} stored in the {@link ExecutionAttributes}, or {@code null} if there is
* no span set.
*/
public static Span getSpanFromAttributes(ExecutionAttributes attributes) {
Context context = getContextFromAttributes(attributes);
return context == null ? Span.getInvalid() : Span.fromContext(context);
}
/**
* Returns the {@link Span} stored in the {@link ExecutionAttributes}, or {@code null} if there is
* no span set.

View File

@ -5,7 +5,7 @@
package io.opentelemetry.instrumentation.awssdk.v2_2;
import static io.opentelemetry.instrumentation.awssdk.v2_2.AwsSdk.getSpanFromAttributes;
import static io.opentelemetry.instrumentation.awssdk.v2_2.AwsSdk.getContextFromAttributes;
import static io.opentelemetry.instrumentation.awssdk.v2_2.AwsSdkHttpClientTracer.tracer;
import static io.opentelemetry.instrumentation.awssdk.v2_2.RequestType.ofSdkRequest;
@ -108,8 +108,9 @@ final class TracingExecutionInterceptor implements ExecutionInterceptor {
@Override
public void afterMarshalling(
Context.AfterMarshalling context, ExecutionAttributes executionAttributes) {
Span span = getSpanFromAttributes(executionAttributes);
if (span.getSpanContext().isValid()) {
io.opentelemetry.context.Context otelContext = getContextFromAttributes(executionAttributes);
if (otelContext != null) {
Span span = Span.fromContext(otelContext);
tracer().onRequest(span, context.httpRequest());
SdkRequestDecorator decorator = decorator(executionAttributes);
if (decorator != null) {
@ -145,12 +146,13 @@ final class TracingExecutionInterceptor implements ExecutionInterceptor {
@Override
public void afterExecution(
Context.AfterExecution context, ExecutionAttributes executionAttributes) {
Span span = getSpanFromAttributes(executionAttributes);
if (span.getSpanContext().isValid()) {
io.opentelemetry.context.Context otelContext = getContextFromAttributes(executionAttributes);
if (otelContext != null) {
clearAttributes(executionAttributes);
Span span = Span.fromContext(otelContext);
tracer().afterExecution(span, context.httpRequest());
onSdkResponse(span, context.response());
tracer().end(span, context.httpResponse());
tracer().end(otelContext, context.httpResponse());
}
}
@ -163,10 +165,10 @@ final class TracingExecutionInterceptor implements ExecutionInterceptor {
@Override
public void onExecutionFailure(
Context.FailedExecution context, ExecutionAttributes executionAttributes) {
Span span = getSpanFromAttributes(executionAttributes);
if (span.getSpanContext().isValid()) {
io.opentelemetry.context.Context otelContext = getContextFromAttributes(executionAttributes);
if (otelContext != null) {
clearAttributes(executionAttributes);
tracer().endExceptionally(span, context.exception());
tracer().endExceptionally(otelContext, context.exception());
}
}

View File

@ -80,30 +80,16 @@ class Elasticsearch6RestClientTest extends AgentTestRunner {
result.status == "green"
assertTraces(1) {
trace(0, 2) {
trace(0, 1) {
span(0) {
name "GET _cluster/health"
kind CLIENT
hasNoParent()
attributes {
"${SemanticAttributes.NET_PEER_NAME.key()}" httpTransportAddress.address
"${SemanticAttributes.NET_PEER_PORT.key()}" httpTransportAddress.port
"${SemanticAttributes.HTTP_URL.key()}" "_cluster/health"
"${SemanticAttributes.HTTP_METHOD.key()}" "GET"
"${SemanticAttributes.DB_SYSTEM.key()}" "elasticsearch"
"${SemanticAttributes.DB_OPERATION.key()}" "GET _cluster/health"
}
}
span(1) {
name expectedOperationName("GET")
kind CLIENT
childOf span(0)
attributes {
"${SemanticAttributes.NET_TRANSPORT.key()}" "IP.TCP"
"${SemanticAttributes.HTTP_FLAVOR.key()}" "1.1"
"${SemanticAttributes.HTTP_URL.key()}" "_cluster/health"
"${SemanticAttributes.HTTP_METHOD.key()}" "GET"
"${SemanticAttributes.HTTP_STATUS_CODE.key()}" 200
"${SemanticAttributes.NET_PEER_NAME.key()}" httpTransportAddress.address
"${SemanticAttributes.NET_PEER_PORT.key()}" httpTransportAddress.port
}
}
}

View File

@ -71,7 +71,6 @@ public class Elasticsearch5RestClientInstrumentationModule extends Instrumentati
span = tracer().startSpan(null, method + " " + endpoint);
scope = tracer().startScope(span);
tracer().onRequest(span, method, endpoint);
responseListener = new RestResponseListener(responseListener, span);
}

View File

@ -84,30 +84,16 @@ class Elasticsearch5RestClientTest extends AgentTestRunner {
result.status == "green"
assertTraces(1) {
trace(0, 2) {
trace(0, 1) {
span(0) {
name "GET _cluster/health"
kind CLIENT
hasNoParent()
attributes {
"${SemanticAttributes.NET_PEER_NAME.key()}" httpTransportAddress.address
"${SemanticAttributes.NET_PEER_PORT.key()}" httpTransportAddress.port
"${SemanticAttributes.HTTP_URL.key()}" "_cluster/health"
"${SemanticAttributes.HTTP_METHOD.key()}" "GET"
"${SemanticAttributes.DB_SYSTEM.key()}" "elasticsearch"
"${SemanticAttributes.DB_OPERATION.key()}" "GET _cluster/health"
}
}
span(1) {
name expectedOperationName("GET")
kind CLIENT
childOf span(0)
attributes {
"${SemanticAttributes.NET_TRANSPORT.key()}" "IP.TCP"
"${SemanticAttributes.HTTP_URL.key()}" "_cluster/health"
"${SemanticAttributes.HTTP_METHOD.key()}" "GET"
"${SemanticAttributes.HTTP_STATUS_CODE.key()}" 200
"${SemanticAttributes.HTTP_FLAVOR.key()}" "1.1"
"${SemanticAttributes.NET_PEER_NAME.key()}" httpTransportAddress.address
"${SemanticAttributes.NET_PEER_PORT.key()}" httpTransportAddress.port
}
}
}

View File

@ -85,30 +85,16 @@ class Elasticsearch6RestClientTest extends AgentTestRunner {
result.status == "green"
assertTraces(1) {
trace(0, 2) {
trace(0, 1) {
span(0) {
name "GET _cluster/health"
kind CLIENT
hasNoParent()
attributes {
"${SemanticAttributes.NET_PEER_NAME.key()}" httpTransportAddress.address
"${SemanticAttributes.NET_PEER_PORT.key()}" httpTransportAddress.port
"${SemanticAttributes.HTTP_URL.key()}" "_cluster/health"
"${SemanticAttributes.HTTP_METHOD.key()}" "GET"
"${SemanticAttributes.DB_SYSTEM.key()}" "elasticsearch"
"${SemanticAttributes.DB_OPERATION.key()}" "GET _cluster/health"
}
}
span(1) {
name expectedOperationName("GET")
kind CLIENT
childOf span(0)
attributes {
"${SemanticAttributes.NET_TRANSPORT.key()}" "IP.TCP"
"${SemanticAttributes.HTTP_URL.key()}" "_cluster/health"
"${SemanticAttributes.HTTP_METHOD.key()}" "GET"
"${SemanticAttributes.HTTP_STATUS_CODE.key()}" 200
"${SemanticAttributes.HTTP_FLAVOR.key()}" "1.1"
"${SemanticAttributes.NET_PEER_NAME.key()}" httpTransportAddress.address
"${SemanticAttributes.NET_PEER_PORT.key()}" httpTransportAddress.port
}
}
}

View File

@ -69,7 +69,6 @@ public class Elasticsearch6RestClientInstrumentationModule extends Instrumentati
span = tracer().startSpan(null, request.getMethod() + " " + request.getEndpoint());
scope = tracer().startScope(span);
tracer().onRequest(span, request.getMethod(), request.getEndpoint());
responseListener = new RestResponseListener(responseListener, span);
}

View File

@ -80,30 +80,16 @@ class Elasticsearch6RestClientTest extends AgentTestRunner {
result.status == "green"
assertTraces(1) {
trace(0, 2) {
trace(0, 1) {
span(0) {
name "GET _cluster/health"
kind CLIENT
hasNoParent()
attributes {
"${SemanticAttributes.NET_PEER_NAME.key()}" httpTransportAddress.address
"${SemanticAttributes.NET_PEER_PORT.key()}" httpTransportAddress.port
"${SemanticAttributes.HTTP_URL.key()}" "_cluster/health"
"${SemanticAttributes.HTTP_METHOD.key()}" "GET"
"${SemanticAttributes.DB_SYSTEM.key()}" "elasticsearch"
"${SemanticAttributes.DB_OPERATION.key()}" "GET _cluster/health"
}
}
span(1) {
name expectedOperationName("GET")
kind CLIENT
childOf span(0)
attributes {
"${SemanticAttributes.NET_TRANSPORT.key()}" "IP.TCP"
"${SemanticAttributes.HTTP_URL.key()}" "_cluster/health"
"${SemanticAttributes.HTTP_METHOD.key()}" "GET"
"${SemanticAttributes.HTTP_STATUS_CODE.key()}" 200
"${SemanticAttributes.HTTP_FLAVOR.key()}" "1.1"
"${SemanticAttributes.NET_PEER_NAME.key()}" httpTransportAddress.address
"${SemanticAttributes.NET_PEER_PORT.key()}" httpTransportAddress.port
}
}
}

View File

@ -19,12 +19,6 @@ public class ElasticsearchRestClientTracer extends DatabaseClientTracer<Void, St
return TRACER;
}
public Span onRequest(Span span, String method, String endpoint) {
span.setAttribute(SemanticAttributes.HTTP_METHOD, method);
span.setAttribute(SemanticAttributes.HTTP_URL, endpoint);
return span;
}
public Span onResponse(Span span, Response response) {
if (response != null && response.getHost() != null) {
NetPeerUtils.INSTANCE.setNetPeer(span, response.getHost().getHostName(), null);

View File

@ -5,6 +5,8 @@
package io.opentelemetry.javaagent.instrumentation.googlehttpclient;
import static io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge.currentContext;
import static io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge.spanFromContext;
import static io.opentelemetry.javaagent.instrumentation.googlehttpclient.GoogleHttpClientTracer.tracer;
import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap;
@ -17,13 +19,11 @@ import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpResponse;
import com.google.auto.service.AutoService;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.StatusCode;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.instrumentation.api.ContextStore;
import io.opentelemetry.javaagent.instrumentation.api.InstrumentationContext;
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
import io.opentelemetry.javaagent.tooling.InstrumentationModule;
import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
import java.util.HashMap;
@ -82,21 +82,22 @@ public class GoogleHttpClientInstrumentationModule extends InstrumentationModule
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void methodEnter(
@Advice.This HttpRequest request,
@Advice.Local("otelSpan") Span span,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
ContextStore<HttpRequest, Context> contextStore =
InstrumentationContext.get(HttpRequest.class, Context.class);
Context context = contextStore.get(request);
context = contextStore.get(request);
if (context == null) {
span = tracer().startSpan(request);
scope = tracer().startScope(span, request.getHeaders());
// TODO (trask) ideally we could pass current context into startScope to avoid extra lookup
contextStore.put(request, Java8BytecodeBridge.currentContext());
Context parentContext = currentContext();
if (tracer().shouldStartSpan(parentContext)) {
context = tracer().startSpan(parentContext, request, request.getHeaders());
contextStore.put(request, context);
scope = context.makeCurrent();
}
} else {
// span was created by GoogleHttpClientAsyncAdvice instrumentation below
span = Java8BytecodeBridge.spanFromContext(context);
scope = context.makeCurrent();
}
}
@ -105,20 +106,22 @@ public class GoogleHttpClientInstrumentationModule extends InstrumentationModule
public static void methodExit(
@Advice.Return HttpResponse response,
@Advice.Thrown Throwable throwable,
@Advice.Local("otelSpan") Span span,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
if (scope == null) {
return;
}
scope.close();
if (throwable == null) {
tracer().end(span, response);
tracer().end(context, response);
} else {
tracer().endExceptionally(span, response, throwable);
tracer().endExceptionally(context, response, throwable);
}
// If HttpRequest.setThrowExceptionOnExecuteError is set to false, there are no exceptions
// for a failed request. Thus, check the response code
if (response != null && !response.isSuccessStatusCode()) {
span.setStatus(StatusCode.ERROR);
spanFromContext(context).setStatus(StatusCode.ERROR);
}
}
}
@ -128,28 +131,36 @@ public class GoogleHttpClientInstrumentationModule extends InstrumentationModule
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void methodEnter(
@Advice.This HttpRequest request,
@Advice.Local("otelSpan") Span span,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
Context parentContext = currentContext();
if (!tracer().shouldStartSpan(parentContext)) {
return;
}
span = tracer().startSpan(request);
scope = tracer().startScope(span, request.getHeaders());
context = tracer().startSpan(parentContext, request, request.getHeaders());
// propagating the context manually here so this instrumentation will work with and without
// the executors instrumentation
ContextStore<HttpRequest, Context> contextStore =
InstrumentationContext.get(HttpRequest.class, Context.class);
contextStore.put(request, Java8BytecodeBridge.currentContext());
contextStore.put(request, context);
scope = context.makeCurrent();
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void methodExit(
@Advice.Thrown Throwable throwable,
@Advice.Local("otelSpan") Span span,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
if (scope == null) {
return;
}
scope.close();
if (throwable != null) {
tracer().endExceptionally(span, throwable);
tracer().endExceptionally(context, throwable);
}
}
}

View File

@ -5,6 +5,7 @@
package io.opentelemetry.javaagent.instrumentation.httpurlconnection;
import static io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge.currentContext;
import static io.opentelemetry.javaagent.instrumentation.httpurlconnection.HttpUrlConnectionTracer.tracer;
import static io.opentelemetry.javaagent.tooling.bytebuddy.matcher.AgentElementMatchers.extendsClass;
import static io.opentelemetry.javaagent.tooling.matcher.NameMatchers.namedOneOf;
@ -17,7 +18,7 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.not;
import com.google.auto.service.AutoService;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.instrumentation.api.CallDepthThreadLocalMap;
import io.opentelemetry.javaagent.instrumentation.api.ContextStore;
@ -79,7 +80,7 @@ public class HttpUrlConnectionInstrumentationModule extends InstrumentationModul
@Advice.OnMethodEnter(suppress = Throwable.class)
public static HttpUrlState methodEnter(
@Advice.This HttpURLConnection thiz,
@Advice.This HttpURLConnection connection,
@Advice.FieldValue("connected") boolean connected,
@Advice.Local("otelScope") Scope scope) {
@ -90,22 +91,27 @@ public class HttpUrlConnectionInstrumentationModule extends InstrumentationModul
ContextStore<HttpURLConnection, HttpUrlState> contextStore =
InstrumentationContext.get(HttpURLConnection.class, HttpUrlState.class);
HttpUrlState state = contextStore.putIfAbsent(thiz, HttpUrlState.FACTORY);
HttpUrlState state = contextStore.putIfAbsent(connection, HttpUrlState::new);
synchronized (state) {
if (!state.hasSpan() && !state.isFinished()) {
Span span = state.start(thiz);
if (!connected) {
scope = tracer().startScope(span, thiz);
if (!state.initialized) {
Context parentContext = currentContext();
if (tracer().shouldStartSpan(parentContext)) {
state.context = tracer().startSpan(parentContext, connection, connection);
if (!connected) {
scope = state.context.makeCurrent();
}
}
state.initialized = true;
}
return state;
}
return state;
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void methodExit(
@Advice.Enter HttpUrlState state,
@Advice.This HttpURLConnection connection,
@Advice.FieldValue("responseCode") int responseCode,
@Advice.Thrown Throwable throwable,
@Advice.Origin("#m") String methodName,
@ -120,62 +126,28 @@ public class HttpUrlConnectionInstrumentationModule extends InstrumentationModul
CallDepthThreadLocalMap.reset(HttpURLConnection.class);
synchronized (state) {
if (state.hasSpan() && !state.isFinished()) {
if (state.context != null && !state.finished) {
if (throwable != null) {
state.finishSpan(throwable);
tracer().endExceptionally(state.context, throwable);
state.finished = true;
} else if ("getInputStream".equals(methodName)) {
state.finishSpan(responseCode);
// responseCode field is sometimes not populated.
// We can't call getResponseCode() due to some unwanted side-effects
// (e.g. breaks getOutputStream).
if (responseCode > 0) {
tracer().end(state.context, new HttpUrlResponse(connection, responseCode));
state.finished = true;
}
}
}
}
}
}
// state is always accessed under synchronized block
public static class HttpUrlState {
public static final ContextStore.Factory<HttpUrlState> FACTORY =
new ContextStore.Factory<HttpUrlState>() {
@Override
public HttpUrlState create() {
return new HttpUrlState();
}
};
private volatile Span span = null;
private volatile boolean finished = false;
public Span start(HttpURLConnection connection) {
span = tracer().startSpan(connection);
return span;
}
public boolean hasSpan() {
return span != null;
}
public boolean isFinished() {
return finished;
}
public void finishSpan(Throwable throwable) {
tracer().endExceptionally(span, throwable);
span = null;
finished = true;
}
public void finishSpan(int responseCode) {
/*
* responseCode field is sometimes not populated.
* We can't call getResponseCode() due to some unwanted side-effects
* (e.g. breaks getOutputStream).
*/
if (responseCode > 0) {
// Need to explicitly cast to boxed type to make sure correct method is called.
// https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/946
tracer().end(span, (Integer) responseCode);
span = null;
finished = true;
}
}
public boolean initialized;
public Context context;
public boolean finished;
}
}

View File

@ -14,7 +14,7 @@ import java.net.URI;
import java.net.URISyntaxException;
public class HttpUrlConnectionTracer
extends HttpClientTracer<HttpURLConnection, HttpURLConnection, Integer> {
extends HttpClientTracer<HttpURLConnection, HttpURLConnection, HttpUrlResponse> {
private static final HttpUrlConnectionTracer TRACER = new HttpUrlConnectionTracer();
@ -33,8 +33,8 @@ public class HttpUrlConnectionTracer
}
@Override
protected Integer status(Integer status) {
return status;
protected Integer status(HttpUrlResponse response) {
return response.status();
}
@Override
@ -43,8 +43,8 @@ public class HttpUrlConnectionTracer
}
@Override
protected String responseHeader(Integer integer, String name) {
return null;
protected String responseHeader(HttpUrlResponse response, String name) {
return response.header(name);
}
@Override

View File

@ -0,0 +1,26 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.httpurlconnection;
import java.net.HttpURLConnection;
public class HttpUrlResponse {
private final HttpURLConnection connection;
private final int resolvedResponseCode;
public HttpUrlResponse(HttpURLConnection connection, int resolvedResponseCode) {
this.connection = connection;
this.resolvedResponseCode = resolvedResponseCode;
}
int status() {
return resolvedResponseCode;
}
String header(String name) {
return connection.getHeaderField(name);
}
}

View File

@ -5,6 +5,7 @@
package io.opentelemetry.javaagent.instrumentation.httpclient;
import static io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge.currentContext;
import static io.opentelemetry.javaagent.instrumentation.httpclient.JdkHttpClientTracer.tracer;
import static io.opentelemetry.javaagent.tooling.ClassLoaderMatcher.hasClassesNamed;
import static io.opentelemetry.javaagent.tooling.bytebuddy.matcher.AgentElementMatchers.extendsClass;
@ -16,9 +17,8 @@ import static net.bytebuddy.matcher.ElementMatchers.not;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.instrumentation.api.CallDepthThreadLocalMap.Depth;
import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
@ -72,34 +72,32 @@ public class HttpClientInstrumentation implements TypeInstrumentation {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void methodEnter(
@Advice.Argument(value = 0) HttpRequest httpRequest,
@Advice.Local("otelSpan") Span span,
@Advice.Local("otelScope") Scope scope,
@Advice.Local("otelCallDepth") Depth callDepth) {
callDepth = tracer().getCallDepth();
if (callDepth.getAndIncrement() == 0) {
span = tracer().startSpan(httpRequest);
if (span.getSpanContext().isValid()) {
scope = tracer().startScope(span, httpRequest);
}
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
Context parentContext = currentContext();
if (!tracer().shouldStartSpan(parentContext)) {
return;
}
context = tracer().startSpan(parentContext, httpRequest, httpRequest);
scope = context.makeCurrent();
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void methodExit(
@Advice.Return HttpResponse<?> result,
@Advice.Thrown Throwable throwable,
@Advice.Local("otelSpan") Span span,
@Advice.Local("otelScope") Scope scope,
@Advice.Local("otelCallDepth") Depth callDepth) {
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
if (scope == null) {
return;
}
if (callDepth.decrementAndGet() == 0 && scope != null) {
scope.close();
if (throwable == null) {
tracer().end(span, result);
} else {
tracer().endExceptionally(span, result, throwable);
}
scope.close();
if (throwable == null) {
tracer().end(context, result);
} else {
tracer().endExceptionally(context, result, throwable);
}
}
}
@ -109,34 +107,32 @@ public class HttpClientInstrumentation implements TypeInstrumentation {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void methodEnter(
@Advice.Argument(value = 0) HttpRequest httpRequest,
@Advice.Local("otelSpan") Span span,
@Advice.Local("otelScope") Scope scope,
@Advice.Local("otelCallDepth") Depth callDepth) {
callDepth = tracer().getCallDepth();
if (callDepth.getAndIncrement() == 0) {
span = tracer().startSpan(httpRequest);
if (span.getSpanContext().isValid()) {
scope = tracer().startScope(span, httpRequest);
}
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
Context parentContext = currentContext();
if (!tracer().shouldStartSpan(parentContext)) {
return;
}
context = tracer().startSpan(parentContext, httpRequest, httpRequest);
scope = context.makeCurrent();
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void methodExit(
@Advice.Return(readOnly = false) CompletableFuture<HttpResponse<?>> future,
@Advice.Thrown Throwable throwable,
@Advice.Local("otelSpan") Span span,
@Advice.Local("otelScope") Scope scope,
@Advice.Local("otelCallDepth") Depth callDepth) {
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
if (scope == null) {
return;
}
if (callDepth.decrementAndGet() == 0 && scope != null) {
scope.close();
if (throwable != null) {
tracer().endExceptionally(span, null, throwable);
} else {
future = future.whenComplete(new ResponseConsumer(span));
}
scope.close();
if (throwable != null) {
tracer().endExceptionally(context, null, throwable);
} else {
future = future.whenComplete(new ResponseConsumer(context));
}
}
}

View File

@ -11,10 +11,7 @@ import io.opentelemetry.api.trace.attributes.SemanticAttributes;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.propagation.TextMapPropagator.Setter;
import io.opentelemetry.instrumentation.api.tracer.HttpClientTracer;
import io.opentelemetry.javaagent.instrumentation.api.CallDepthThreadLocalMap;
import io.opentelemetry.javaagent.instrumentation.api.CallDepthThreadLocalMap.Depth;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpClient.Version;
import java.net.http.HttpHeaders;
import java.net.http.HttpRequest;
@ -33,10 +30,6 @@ public class JdkHttpClientTracer
return TRACER;
}
public Depth getCallDepth() {
return CallDepthThreadLocalMap.getCallDepth(HttpClient.class);
}
@Override
protected String getInstrumentationName() {
return "io.opentelemetry.javaagent.java-httpclient";

View File

@ -7,23 +7,23 @@ package io.opentelemetry.javaagent.instrumentation.httpclient;
import static io.opentelemetry.javaagent.instrumentation.httpclient.JdkHttpClientTracer.tracer;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import java.net.http.HttpResponse;
import java.util.function.BiConsumer;
public class ResponseConsumer implements BiConsumer<HttpResponse<?>, Throwable> {
private final Span span;
private final Context context;
public ResponseConsumer(Span span) {
this.span = span;
public ResponseConsumer(Context context) {
this.context = context;
}
@Override
public void accept(HttpResponse<?> httpResponse, Throwable throwable) {
if (throwable == null) {
tracer().end(span, httpResponse);
tracer().end(context, httpResponse);
} else {
tracer().endExceptionally(span, httpResponse, throwable);
tracer().endExceptionally(context, httpResponse, throwable);
}
}
}

View File

@ -6,6 +6,7 @@
package io.opentelemetry.javaagent.instrumentation.jaxrsclient.v1_1;
import static io.opentelemetry.instrumentation.api.tracer.HttpServerTracer.CONTEXT_ATTRIBUTE;
import static io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge.currentContext;
import static io.opentelemetry.javaagent.instrumentation.jaxrsclient.v1_1.JaxRsClientV1Tracer.tracer;
import static io.opentelemetry.javaagent.tooling.ClassLoaderMatcher.hasClassesNamed;
import static io.opentelemetry.javaagent.tooling.bytebuddy.matcher.AgentElementMatchers.extendsClass;
@ -17,10 +18,9 @@ import static net.bytebuddy.matcher.ElementMatchers.returns;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import com.google.auto.service.AutoService;
import com.sun.jersey.api.client.ClientHandler;
import com.sun.jersey.api.client.ClientRequest;
import com.sun.jersey.api.client.ClientResponse;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.tooling.InstrumentationModule;
import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
@ -69,15 +69,14 @@ public class JaxRsClientInstrumentationModule extends InstrumentationModule {
@Advice.OnMethodEnter
public static void onEnter(
@Advice.Argument(0) ClientRequest request,
@Advice.This ClientHandler thisObj,
@Advice.Local("otelSpan") Span span,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
// WARNING: this might be a chain...so we only have to trace the first in the chain.
boolean isRootClientHandler = null == request.getProperties().get(CONTEXT_ATTRIBUTE);
if (isRootClientHandler) {
span = tracer().startSpan(request);
scope = tracer().startScope(span, request);
Context parentContext = currentContext();
if (isRootClientHandler && tracer().shouldStartSpan(parentContext)) {
context = tracer().startSpan(parentContext, request, request);
scope = context.makeCurrent();
}
}
@ -85,16 +84,17 @@ public class JaxRsClientInstrumentationModule extends InstrumentationModule {
public static void onExit(
@Advice.Return ClientResponse response,
@Advice.Thrown Throwable throwable,
@Advice.Local("otelSpan") Span span,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
if (scope != null) {
scope.close();
if (scope == null) {
return;
}
scope.close();
if (throwable != null) {
tracer().endExceptionally(span, throwable);
tracer().endExceptionally(context, throwable);
} else {
tracer().end(span, response);
tracer().end(context, response);
}
}
}

View File

@ -7,8 +7,7 @@ package io.opentelemetry.javaagent.instrumentation.jaxrsclient.v2_0;
import static io.opentelemetry.javaagent.instrumentation.jaxrsclient.v2_0.JaxRsClientTracer.tracer;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Scope;
import io.opentelemetry.context.Context;
import javax.annotation.Priority;
import javax.ws.rs.Priorities;
import javax.ws.rs.client.ClientRequestContext;
@ -18,23 +17,23 @@ import javax.ws.rs.client.ClientResponseFilter;
@Priority(Priorities.HEADER_DECORATOR)
public class ClientTracingFilter implements ClientRequestFilter, ClientResponseFilter {
public static final String SPAN_PROPERTY_NAME = "io.opentelemetry.auto.jax-rs-client.span";
public static final String CONTEXT_PROPERTY_NAME = "io.opentelemetry.auto.jax-rs-client.context";
@Override
public void filter(ClientRequestContext requestContext) {
Span span = tracer().startSpan(requestContext);
// TODO (trask) expose inject separate from startScope, e.g. for async cases
Scope scope = tracer().startScope(span, requestContext);
scope.close();
requestContext.setProperty(SPAN_PROPERTY_NAME, span);
Context parentContext = Context.current();
if (tracer().shouldStartSpan(parentContext)) {
Context context = tracer().startSpan(parentContext, requestContext, requestContext);
requestContext.setProperty(CONTEXT_PROPERTY_NAME, context);
}
}
@Override
public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext) {
Object spanObj = requestContext.getProperty(SPAN_PROPERTY_NAME);
if (spanObj instanceof Span) {
Span span = (Span) spanObj;
tracer().end(span, responseContext);
Object contextObj = requestContext.getProperty(CONTEXT_PROPERTY_NAME);
if (contextObj instanceof Context) {
Context context = (Context) contextObj;
tracer().end(context, responseContext);
}
}
}

View File

@ -12,7 +12,7 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.returns;
import com.google.auto.service.AutoService;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import io.opentelemetry.javaagent.tooling.InstrumentationModule;
import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
import java.util.Collections;
@ -71,9 +71,9 @@ public class JerseyClientInstrumentationModule extends InstrumentationModule {
@Advice.FieldValue("requestContext") ClientRequest context,
@Advice.Thrown Throwable throwable) {
if (throwable != null) {
Object prop = context.getProperty(ClientTracingFilter.SPAN_PROPERTY_NAME);
if (prop instanceof Span) {
tracer().endExceptionally((Span) prop, throwable);
Object prop = context.getProperty(ClientTracingFilter.CONTEXT_PROPERTY_NAME);
if (prop instanceof Context) {
tracer().endExceptionally((Context) prop, throwable);
}
}
}

View File

@ -7,7 +7,7 @@ package io.opentelemetry.javaagent.instrumentation.jaxrsclient.v2_0;
import static io.opentelemetry.javaagent.instrumentation.jaxrsclient.v2_0.JaxRsClientTracer.tracer;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
@ -44,9 +44,9 @@ public class WrappedFuture<T> implements Future<T> {
try {
return wrapped.get();
} catch (ExecutionException e) {
Object prop = context.getProperty(ClientTracingFilter.SPAN_PROPERTY_NAME);
if (prop instanceof Span) {
tracer().endExceptionally((Span) prop, e.getCause());
Object prop = context.getProperty(ClientTracingFilter.CONTEXT_PROPERTY_NAME);
if (prop instanceof Context) {
tracer().endExceptionally((Context) prop, e.getCause());
}
throw e;
}
@ -58,9 +58,9 @@ public class WrappedFuture<T> implements Future<T> {
try {
return wrapped.get(timeout, unit);
} catch (ExecutionException e) {
Object prop = context.getProperty(ClientTracingFilter.SPAN_PROPERTY_NAME);
if (prop instanceof Span) {
tracer().endExceptionally((Span) prop, e.getCause());
Object prop = context.getProperty(ClientTracingFilter.CONTEXT_PROPERTY_NAME);
if (prop instanceof Context) {
tracer().endExceptionally((Context) prop, e.getCause());
}
throw e;
}

View File

@ -5,6 +5,7 @@
package io.opentelemetry.javaagent.instrumentation.jaxrsclient.v2_0;
import static io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge.currentContext;
import static io.opentelemetry.javaagent.instrumentation.jaxrsclient.v2_0.ResteasyClientTracer.tracer;
import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap;
@ -14,7 +15,7 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
import com.google.auto.service.AutoService;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.tooling.InstrumentationModule;
import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
@ -67,25 +68,30 @@ public class ResteasyClientInstrumentationModule extends InstrumentationModule {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void methodEnter(
@Advice.This ClientInvocation invocation,
@Advice.Local("otelSpan") Span span,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
span = tracer().startSpan(invocation);
scope = tracer().startScope(span, invocation);
Context parentContext = currentContext();
if (tracer().shouldStartSpan(parentContext)) {
context = tracer().startSpan(parentContext, invocation, invocation);
scope = context.makeCurrent();
}
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void methodExit(
@Advice.Return Response response,
@Advice.Thrown Throwable throwable,
@Advice.Local("otelSpan") Span span,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
if (scope == null) {
return;
}
scope.close();
if (throwable != null) {
tracer().endExceptionally(span, throwable);
tracer().endExceptionally(context, throwable);
} else {
tracer().end(span, response);
tracer().end(context, response);
}
}
}

View File

@ -10,8 +10,8 @@ import static io.opentelemetry.javaagent.instrumentation.jdbc.JdbcUtils.normaliz
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.instrumentation.api.tracer.DatabaseClientTracer;
import io.opentelemetry.javaagent.instrumentation.api.CallDepth;
import io.opentelemetry.javaagent.instrumentation.api.CallDepthThreadLocalMap;
import io.opentelemetry.javaagent.instrumentation.api.CallDepthThreadLocalMap.Depth;
import io.opentelemetry.javaagent.instrumentation.api.db.SqlStatementInfo;
import java.net.InetSocketAddress;
import java.sql.Connection;
@ -62,7 +62,7 @@ public class JdbcTracer extends DatabaseClientTracer<DbInfo, SqlStatementInfo> {
return info.getShortUrl();
}
public Depth getCallDepth() {
public CallDepth getCallDepth() {
return CallDepthThreadLocalMap.getCallDepth(Statement.class);
}

View File

@ -16,7 +16,7 @@ import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.instrumentation.api.CallDepthThreadLocalMap.Depth;
import io.opentelemetry.javaagent.instrumentation.api.CallDepth;
import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
import java.sql.PreparedStatement;
import java.util.Map;
@ -51,7 +51,7 @@ public class PreparedStatementInstrumentation implements TypeInstrumentation {
@Advice.This PreparedStatement statement,
@Advice.Local("otelSpan") Span span,
@Advice.Local("otelScope") Scope scope,
@Advice.Local("otelCallDepth") Depth callDepth) {
@Advice.Local("otelCallDepth") CallDepth callDepth) {
callDepth = tracer().getCallDepth();
if (callDepth.getAndIncrement() == 0) {
@ -67,7 +67,7 @@ public class PreparedStatementInstrumentation implements TypeInstrumentation {
@Advice.Thrown Throwable throwable,
@Advice.Local("otelSpan") Span span,
@Advice.Local("otelScope") Scope scope,
@Advice.Local("otelCallDepth") Depth callDepth) {
@Advice.Local("otelCallDepth") CallDepth callDepth) {
if (callDepth.decrementAndGet() == 0 && scope != null) {
scope.close();
if (throwable == null) {

View File

@ -16,7 +16,7 @@ import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.instrumentation.api.CallDepthThreadLocalMap.Depth;
import io.opentelemetry.javaagent.instrumentation.api.CallDepth;
import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
import java.sql.Statement;
import java.util.Map;
@ -52,7 +52,7 @@ public class StatementInstrumentation implements TypeInstrumentation {
@Advice.This Statement statement,
@Advice.Local("otelSpan") Span span,
@Advice.Local("otelScope") Scope scope,
@Advice.Local("otelCallDepth") Depth callDepth) {
@Advice.Local("otelCallDepth") CallDepth callDepth) {
callDepth = tracer().getCallDepth();
if (callDepth.getAndIncrement() == 0) {
@ -68,7 +68,7 @@ public class StatementInstrumentation implements TypeInstrumentation {
@Advice.Thrown Throwable throwable,
@Advice.Local("otelSpan") Span span,
@Advice.Local("otelScope") Scope scope,
@Advice.Local("otelCallDepth") Depth callDepth) {
@Advice.Local("otelCallDepth") CallDepth callDepth) {
if (callDepth.decrementAndGet() == 0 && scope != null) {
scope.close();
if (throwable == null) {

View File

@ -5,12 +5,12 @@
package io.opentelemetry.javaagent.instrumentation.khttp;
import static io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge.currentContext;
import static io.opentelemetry.javaagent.instrumentation.khttp.KHttpHeadersInjectAdapter.asWritable;
import static io.opentelemetry.javaagent.instrumentation.khttp.KHttpTracer.tracer;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.instrumentation.api.CallDepthThreadLocalMap.Depth;
import java.util.Map;
import khttp.responses.Response;
import net.bytebuddy.asm.Advice;
@ -22,34 +22,33 @@ public class KHttpAdvice {
@Advice.Argument(value = 0) String method,
@Advice.Argument(value = 1) String uri,
@Advice.Argument(value = 2, readOnly = false) Map<String, String> headers,
@Advice.Local("otelSpan") Span span,
@Advice.Local("otelScope") Scope scope,
@Advice.Local("otelCallDepth") Depth callDepth) {
callDepth = tracer().getCallDepth();
if (callDepth.getAndIncrement() == 0) {
span = tracer().startSpan(new RequestWrapper(method, uri, headers));
if (span.getSpanContext().isValid()) {
headers = asWritable(headers);
scope = tracer().startScope(span, headers);
}
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
Context parentContext = currentContext();
if (!tracer().shouldStartSpan(parentContext)) {
return;
}
headers = asWritable(headers);
context = tracer().startSpan(parentContext, new RequestWrapper(method, uri, headers), headers);
scope = context.makeCurrent();
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void methodExit(
@Advice.Return Response response,
@Advice.Thrown Throwable throwable,
@Advice.Local("otelSpan") Span span,
@Advice.Local("otelScope") Scope scope,
@Advice.Local("otelCallDepth") Depth callDepth) {
if (callDepth.decrementAndGet() == 0 && scope != null) {
scope.close();
if (throwable == null) {
tracer().end(span, response);
} else {
tracer().endExceptionally(span, response, throwable);
}
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
if (scope == null) {
return;
}
scope.close();
if (throwable == null) {
tracer().end(context, response);
} else {
tracer().endExceptionally(context, response, throwable);
}
}
}

View File

@ -9,12 +9,9 @@ import static io.opentelemetry.javaagent.instrumentation.khttp.KHttpHeadersInjec
import io.opentelemetry.context.propagation.TextMapPropagator.Setter;
import io.opentelemetry.instrumentation.api.tracer.HttpClientTracer;
import io.opentelemetry.javaagent.instrumentation.api.CallDepthThreadLocalMap;
import io.opentelemetry.javaagent.instrumentation.api.CallDepthThreadLocalMap.Depth;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Map;
import khttp.KHttp;
import khttp.responses.Response;
public class KHttpTracer extends HttpClientTracer<RequestWrapper, Map<String, String>, Response> {
@ -24,10 +21,6 @@ public class KHttpTracer extends HttpClientTracer<RequestWrapper, Map<String, St
return TRACER;
}
public Depth getCallDepth() {
return CallDepthThreadLocalMap.getCallDepth(KHttp.class);
}
@Override
protected String method(RequestWrapper requestWrapper) {
return requestWrapper.method;

View File

@ -7,7 +7,9 @@ package io.opentelemetry.javaagent.instrumentation.kubernetesclient;
import static io.opentelemetry.api.trace.Span.Kind.CLIENT;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.propagation.TextMapPropagator.Setter;
import io.opentelemetry.instrumentation.api.tracer.HttpClientTracer;
import java.net.URI;
@ -21,6 +23,27 @@ public class KubernetesClientTracer extends HttpClientTracer<Request, Request, R
return TRACER;
}
/**
* This method is used to generate an acceptable CLIENT span (operation) name based on a given
* KubernetesRequestDigest.
*/
public Context startSpan(Context parentContext, Request request) {
KubernetesRequestDigest digest = KubernetesRequestDigest.parse(request);
Span span =
tracer
.spanBuilder(digest.toString())
.setSpanKind(CLIENT)
.setParent(parentContext)
.setAttribute("namespace", digest.getResourceMeta().getNamespace())
.setAttribute("name", digest.getResourceMeta().getName())
.startSpan();
Context context = parentContext.with(span).with(CONTEXT_CLIENT_SPAN_KEY, span);
OpenTelemetry.getGlobalPropagators()
.getTextMapPropagator()
.inject(context, request, getSetter());
return context;
}
@Override
protected String method(Request httpRequest) {
return httpRequest.method();
@ -62,17 +85,4 @@ public class KubernetesClientTracer extends HttpClientTracer<Request, Request, R
protected Span onRequest(Span span, Request request) {
return super.onRequest(span, request);
}
/**
* This method is used to generate an acceptable CLIENT span (operation) name based on a given
* KubernetesRequestDigest.
*/
public Span startSpan(KubernetesRequestDigest digest) {
return tracer
.spanBuilder(digest.toString())
.setSpanKind(CLIENT)
.setAttribute("namespace", digest.getResourceMeta().getNamespace())
.setAttribute("name", digest.getResourceMeta().getName())
.startSpan();
}
}

View File

@ -19,22 +19,18 @@ public class TracingInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
KubernetesRequestDigest digest = KubernetesRequestDigest.parse(chain.request());
Span span = tracer().startSpan(digest);
tracer().onRequest(span, chain.request());
Context context = Context.current().with(span);
Context context = tracer().startSpan(Context.current(), chain.request());
tracer().onRequest(Span.fromContext(context), chain.request());
Response response;
try (Scope scope = context.makeCurrent()) {
try (Scope ignored = context.makeCurrent()) {
response = chain.proceed(chain.request());
} catch (Exception e) {
tracer().endExceptionally(span, e);
tracer().endExceptionally(context, e);
throw e;
}
tracer().end(span, response);
tracer().end(context, response);
return response;
}
}

View File

@ -5,7 +5,6 @@
package io.opentelemetry.javaagent.instrumentation.netty.v3_8;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import io.opentelemetry.javaagent.instrumentation.api.ContextStore;
import java.util.Objects;
@ -21,7 +20,6 @@ public class ChannelTraceContext {
}
private Context connectionContext;
private Span clientSpan;
private Context clientParentContext;
private Context context;
@ -33,14 +31,6 @@ public class ChannelTraceContext {
this.connectionContext = connectionContinuation;
}
public Span getClientSpan() {
return clientSpan;
}
public void setClientSpan(Span clientSpan) {
this.clientSpan = clientSpan;
}
public Context getClientParentContext() {
return clientParentContext;
}
@ -67,13 +57,12 @@ public class ChannelTraceContext {
}
ChannelTraceContext other = (ChannelTraceContext) obj;
return Objects.equals(connectionContext, other.connectionContext)
&& Objects.equals(clientSpan, other.clientSpan)
&& Objects.equals(clientParentContext, other.clientParentContext)
&& Objects.equals(context, other.context);
}
@Override
public int hashCode() {
return Objects.hash(connectionContext, clientSpan, clientParentContext, context);
return Objects.hash(connectionContext, clientParentContext, context);
}
}

View File

@ -38,30 +38,33 @@ public class HttpClientRequestTracingHandler extends SimpleChannelDownstreamHand
ChannelTraceContext channelTraceContext =
contextStore.putIfAbsent(ctx.getChannel(), ChannelTraceContext.Factory.INSTANCE);
// TODO pass Context into Tracer.startSpan() and then don't need this scoping
Scope parentScope = null;
Context parentContext = channelTraceContext.getConnectionContext();
if (parentContext != null) {
parentScope = parentContext.makeCurrent();
channelTraceContext.setConnectionContext(null);
} else {
parentContext = Context.current();
}
channelTraceContext.setClientParentContext(Context.current());
if (!tracer().shouldStartSpan(parentContext)) {
ctx.sendDownstream(msg);
return;
}
channelTraceContext.setClientParentContext(parentContext);
HttpRequest request = (HttpRequest) msg.getMessage();
Span span = tracer().startSpan(request);
NetPeerUtils.INSTANCE.setNetPeer(span, (InetSocketAddress) ctx.getChannel().getRemoteAddress());
channelTraceContext.setClientSpan(span);
Context context = tracer().startSpan(parentContext, request, request.headers());
// TODO (trask) move this setNetPeer() call into the Tracer
NetPeerUtils.INSTANCE.setNetPeer(
Span.fromContext(context), (InetSocketAddress) ctx.getChannel().getRemoteAddress());
channelTraceContext.setContext(context);
try (Scope ignored = tracer().startScope(span, request.headers())) {
try (Scope ignored = context.makeCurrent()) {
ctx.sendDownstream(msg);
} catch (Throwable throwable) {
tracer().endExceptionally(span, throwable);
tracer().endExceptionally(context, throwable);
throw throwable;
} finally {
if (parentScope != null) {
parentScope.close();
}
}
}
}

View File

@ -7,7 +7,6 @@ package io.opentelemetry.javaagent.instrumentation.netty.v3_8.client;
import static io.opentelemetry.javaagent.instrumentation.netty.v3_8.client.NettyHttpClientTracer.tracer;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.instrumentation.api.ContextStore;
@ -32,12 +31,12 @@ public class HttpClientResponseTracingHandler extends SimpleChannelUpstreamHandl
contextStore.putIfAbsent(ctx.getChannel(), ChannelTraceContext.Factory.INSTANCE);
Context parentContext = channelTraceContext.getClientParentContext();
Span span = channelTraceContext.getClientSpan();
Context context = channelTraceContext.getContext();
boolean finishSpan = msg.getMessage() instanceof HttpResponse;
if (span != null && finishSpan) {
tracer().end(span, (HttpResponse) msg.getMessage());
if (context != null && finishSpan) {
tracer().end(context, (HttpResponse) msg.getMessage());
}
// We want the callback in the scope of the parent, not the client span

View File

@ -8,9 +8,6 @@ package io.opentelemetry.javaagent.instrumentation.netty.v3_8.client;
import static io.opentelemetry.javaagent.instrumentation.netty.v3_8.client.NettyResponseInjectAdapter.SETTER;
import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.HOST;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.context.propagation.TextMapPropagator.Setter;
import io.opentelemetry.instrumentation.api.tracer.HttpClientTracer;
import java.net.URI;
@ -28,20 +25,6 @@ public class NettyHttpClientTracer
return TRACER;
}
@Override
public Scope startScope(Span span, HttpHeaders headers) {
if (!headers.contains("amz-sdk-invocation-id")) {
return super.startScope(span, headers);
} else {
// TODO (trask) if we move injection up to aws-sdk layer, and start suppressing nested netty
// spans, do we still need this condition?
// AWS calls are often signed, so we can't add headers without breaking the signature.
Context context = Context.current().with(span);
context = context.with(CONTEXT_CLIENT_SPAN_KEY, span);
return context.makeCurrent();
}
}
@Override
protected String method(HttpRequest httpRequest) {
return httpRequest.getMethod().getName();

View File

@ -6,7 +6,6 @@
package io.opentelemetry.javaagent.instrumentation.netty.v4_0;
import io.netty.util.AttributeKey;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import io.opentelemetry.javaagent.instrumentation.api.WeakMap;
import java.util.concurrent.ConcurrentHashMap;
@ -31,7 +30,7 @@ public class AttributeKeys {
public static final AttributeKey<Context> SERVER_SPAN =
attributeKey(AttributeKeys.class.getName() + ".server-span");
public static final AttributeKey<Span> CLIENT_SPAN =
public static final AttributeKey<Context> CLIENT_SPAN =
attributeKey(AttributeKeys.class.getName() + ".client-span");
public static final AttributeKey<Context> CLIENT_PARENT_CONTEXT =

View File

@ -27,30 +27,31 @@ public class HttpClientRequestTracingHandler extends ChannelOutboundHandlerAdapt
return;
}
// TODO pass Context into Tracer.startSpan() and then don't need this scoping
Scope parentScope = null;
Context parentContext = ctx.channel().attr(AttributeKeys.CONNECT_CONTEXT).getAndRemove();
if (parentContext != null) {
parentScope = parentContext.makeCurrent();
if (parentContext == null) {
parentContext = Context.current();
}
if (!tracer().shouldStartSpan(parentContext)) {
ctx.write(msg, prm);
return;
}
HttpRequest request = (HttpRequest) msg;
ctx.channel().attr(AttributeKeys.CLIENT_PARENT_CONTEXT).set(Context.current());
ctx.channel().attr(AttributeKeys.CLIENT_PARENT_CONTEXT).set(parentContext);
Span span = tracer().startSpan(request);
NetPeerUtils.INSTANCE.setNetPeer(span, (InetSocketAddress) ctx.channel().remoteAddress());
ctx.channel().attr(AttributeKeys.CLIENT_SPAN).set(span);
Context context = tracer().startSpan(parentContext, request, request.headers());
// TODO (trask) move this setNetPeer() call into the Tracer
NetPeerUtils.INSTANCE.setNetPeer(
Span.fromContext(context), (InetSocketAddress) ctx.channel().remoteAddress());
ctx.channel().attr(AttributeKeys.CLIENT_SPAN).set(context);
try (Scope scope = tracer().startScope(span, request.headers())) {
try (Scope ignored = context.makeCurrent()) {
ctx.write(msg, prm);
} catch (Throwable throwable) {
tracer().endExceptionally(span, throwable);
tracer().endExceptionally(context, throwable);
throw throwable;
} finally {
if (null != parentScope) {
parentScope.close();
}
}
}
}

View File

@ -11,7 +11,6 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.util.Attribute;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.instrumentation.netty.v4_0.AttributeKeys;
@ -22,12 +21,12 @@ public class HttpClientResponseTracingHandler extends ChannelInboundHandlerAdapt
public void channelRead(ChannelHandlerContext ctx, Object msg) {
Attribute<Context> parentAttr = ctx.channel().attr(AttributeKeys.CLIENT_PARENT_CONTEXT);
Context parentContext = parentAttr.get();
Span span = ctx.channel().attr(AttributeKeys.CLIENT_SPAN).get();
Context context = ctx.channel().attr(AttributeKeys.CLIENT_SPAN).get();
boolean finishSpan = msg instanceof HttpResponse;
if (span != null && finishSpan) {
tracer().end(span, (HttpResponse) msg);
if (context != null && finishSpan) {
tracer().end(context, (HttpResponse) msg);
}
// We want the callback in the scope of the parent, not the client span

View File

@ -11,9 +11,6 @@ import static io.opentelemetry.javaagent.instrumentation.netty.v4_0.client.Netty
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponse;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.context.propagation.TextMapPropagator.Setter;
import io.opentelemetry.instrumentation.api.tracer.HttpClientTracer;
import java.net.URI;
@ -28,20 +25,6 @@ public class NettyHttpClientTracer
return TRACER;
}
@Override
public Scope startScope(Span span, HttpHeaders headers) {
if (!headers.contains("amz-sdk-invocation-id")) {
return super.startScope(span, headers);
} else {
// TODO (trask) if we move injection up to aws-sdk layer, and start suppressing nested netty
// spans, do we still need this condition?
// AWS calls are often signed, so we can't add headers without breaking the signature.
Context context = Context.current().with(span);
context = context.with(CONTEXT_CLIENT_SPAN_KEY, span);
return context.makeCurrent();
}
}
@Override
protected String method(HttpRequest httpRequest) {
return httpRequest.getMethod().name();

View File

@ -6,7 +6,6 @@
package io.opentelemetry.javaagent.instrumentation.netty.v4_1;
import io.netty.util.AttributeKey;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
public class AttributeKeys {
@ -20,7 +19,7 @@ public class AttributeKeys {
public static final AttributeKey<Context> SERVER_SPAN =
AttributeKey.valueOf(AttributeKeys.class, "server-span");
public static final AttributeKey<Span> CLIENT_SPAN =
public static final AttributeKey<Context> CLIENT_SPAN =
AttributeKey.valueOf(AttributeKeys.class, "client-span");
public static final AttributeKey<Context> CLIENT_PARENT_CONTEXT =

View File

@ -26,31 +26,32 @@ public class HttpClientRequestTracingHandler extends ChannelOutboundHandlerAdapt
ctx.write(msg, prm);
return;
}
// TODO pass Context into Tracer.startSpan() and then don't need this scoping
Scope parentScope = null;
Context parentContext = ctx.channel().attr(AttributeKeys.CONNECT_CONTEXT).getAndRemove();
if (parentContext != null) {
parentScope = parentContext.makeCurrent();
}
HttpRequest request = (HttpRequest) msg;
ctx.channel().attr(AttributeKeys.CLIENT_PARENT_CONTEXT).set(Context.current());
Context parentContext = ctx.channel().attr(AttributeKeys.CONNECT_CONTEXT).getAndRemove();
if (parentContext == null) {
parentContext = Context.current();
}
Span span = tracer().startSpan(request);
NetPeerUtils.INSTANCE.setNetPeer(span, (InetSocketAddress) ctx.channel().remoteAddress());
ctx.channel().attr(AttributeKeys.CLIENT_SPAN).set(span);
try (Scope ignored = tracer().startScope(span, request.headers())) {
if (!tracer().shouldStartSpan(parentContext)) {
ctx.write(msg, prm);
return;
}
ctx.channel().attr(AttributeKeys.CLIENT_PARENT_CONTEXT).set(parentContext);
Context context = tracer().startSpan(parentContext, request, request.headers());
// TODO (trask) move this setNetPeer() call into the Tracer
NetPeerUtils.INSTANCE.setNetPeer(
Span.fromContext(context), (InetSocketAddress) ctx.channel().remoteAddress());
ctx.channel().attr(AttributeKeys.CLIENT_SPAN).set(context);
try (Scope ignored = context.makeCurrent()) {
ctx.write(msg, prm);
// span is ended normally in HttpClientResponseTracingHandler
} catch (Throwable throwable) {
tracer().endExceptionally(span, throwable);
tracer().endExceptionally(context, throwable);
throw throwable;
} finally {
if (null != parentScope) {
parentScope.close();
}
}
}
}

View File

@ -11,7 +11,6 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.util.Attribute;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.instrumentation.netty.v4_1.AttributeKeys;
@ -22,12 +21,12 @@ public class HttpClientResponseTracingHandler extends ChannelInboundHandlerAdapt
public void channelRead(ChannelHandlerContext ctx, Object msg) {
Attribute<Context> parentAttr = ctx.channel().attr(AttributeKeys.CLIENT_PARENT_CONTEXT);
Context parentContext = parentAttr.get();
Span span = ctx.channel().attr(AttributeKeys.CLIENT_SPAN).get();
Context context = ctx.channel().attr(AttributeKeys.CLIENT_SPAN).get();
boolean finishSpan = msg instanceof HttpResponse;
if (span != null && finishSpan) {
tracer().end(span, (HttpResponse) msg);
if (context != null && finishSpan) {
tracer().end(context, (HttpResponse) msg);
}
// We want the callback in the scope of the parent, not the client span

View File

@ -10,24 +10,29 @@ import static io.opentelemetry.javaagent.instrumentation.okhttp.v2_2.OkHttpClien
import com.squareup.okhttp.Interceptor;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.Response;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import java.io.IOException;
public class TracingInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Span span = tracer().startSpan(chain.request());
Context parentContext = Context.current();
if (!tracer().shouldStartSpan(parentContext)) {
return chain.proceed(chain.request());
}
Request.Builder requestBuilder = chain.request().newBuilder();
Context context = tracer().startSpan(parentContext, chain.request(), requestBuilder);
Response response;
try (Scope scope = tracer().startScope(span, requestBuilder)) {
try (Scope ignored = context.makeCurrent()) {
response = chain.proceed(requestBuilder.build());
} catch (Exception e) {
tracer().endExceptionally(span, e);
tracer().endExceptionally(context, e);
throw e;
}
tracer().end(span, response);
tracer().end(context, response);
return response;
}
}

View File

@ -7,7 +7,7 @@ package io.opentelemetry.javaagent.instrumentation.okhttp.v3_0;
import static io.opentelemetry.javaagent.instrumentation.okhttp.v3_0.OkHttpClientTracer.tracer;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import java.io.IOException;
import okhttp3.Interceptor;
@ -18,17 +18,22 @@ public class TracingInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Span span = tracer().startSpan(chain.request());
Context parentContext = Context.current();
if (!tracer().shouldStartSpan(parentContext)) {
return chain.proceed(chain.request());
}
Request.Builder requestBuilder = chain.request().newBuilder();
Context context = tracer().startSpan(parentContext, chain.request(), requestBuilder);
Response response;
Request.Builder requestBuilder = chain.request().newBuilder();
try (Scope ignored = tracer().startScope(span, requestBuilder)) {
try (Scope ignored = context.makeCurrent()) {
response = chain.proceed(requestBuilder.build());
} catch (Exception e) {
tracer().endExceptionally(span, e);
tracer().endExceptionally(context, e);
throw e;
}
tracer().end(span, response);
tracer().end(context, response);
return response;
}
}

View File

@ -7,7 +7,6 @@ package io.opentelemetry.javaagent.instrumentation.playws.v1_0;
import static io.opentelemetry.javaagent.instrumentation.playws.PlayWsClientTracer.tracer;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import play.shaded.ahc.org.asynchttpclient.AsyncHandler;
@ -18,15 +17,15 @@ import play.shaded.ahc.org.asynchttpclient.Response;
public class AsyncHandlerWrapper implements AsyncHandler {
private final AsyncHandler delegate;
private final Span span;
private final Context invocationContext;
private final Context context;
private final Context parentContext;
private final Response.ResponseBuilder builder = new Response.ResponseBuilder();
public AsyncHandlerWrapper(AsyncHandler delegate, Span span, Context invocationContext) {
public AsyncHandlerWrapper(AsyncHandler delegate, Context context, Context parentContext) {
this.delegate = delegate;
this.span = span;
this.invocationContext = invocationContext;
this.context = context;
this.parentContext = parentContext;
}
@Override
@ -50,20 +49,18 @@ public class AsyncHandlerWrapper implements AsyncHandler {
@Override
public Object onCompleted() throws Exception {
Response response = builder.build();
tracer().end(span, response);
tracer().end(context, builder.build());
try (Scope scope = invocationContext.makeCurrent()) {
try (Scope scope = parentContext.makeCurrent()) {
return delegate.onCompleted();
}
}
@Override
public void onThrowable(Throwable throwable) {
tracer().endExceptionally(span, throwable);
span.end();
tracer().endExceptionally(context, throwable);
try (Scope scope = invocationContext.makeCurrent()) {
try (Scope scope = parentContext.makeCurrent()) {
delegate.onThrowable(throwable);
}
}

View File

@ -5,14 +5,13 @@
package io.opentelemetry.javaagent.instrumentation.playws.v1_0;
import static io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge.currentContext;
import static io.opentelemetry.javaagent.instrumentation.playws.PlayWsClientTracer.tracer;
import static java.util.Collections.singletonList;
import com.google.auto.service.AutoService;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
import io.opentelemetry.javaagent.instrumentation.playws.AsyncHttpClientInstrumentation;
import io.opentelemetry.javaagent.tooling.InstrumentationModule;
import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
@ -39,32 +38,38 @@ public class PlayWsInstrumentationModule extends InstrumentationModule {
public static void methodEnter(
@Advice.Argument(0) Request request,
@Advice.Argument(value = 1, readOnly = false) AsyncHandler<?> asyncHandler,
@Advice.Local("otelSpan") Span span,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
Context parentContext = Java8BytecodeBridge.currentContext();
Context parentContext = currentContext();
if (!tracer().shouldStartSpan(parentContext)) {
return;
}
span = tracer().startSpan(request);
scope = tracer().startScope(span, request.getHeaders());
context = tracer().startSpan(parentContext, request, request.getHeaders());
scope = context.makeCurrent();
if (asyncHandler instanceof StreamedAsyncHandler) {
asyncHandler =
new StreamedAsyncHandlerWrapper(
(StreamedAsyncHandler<?>) asyncHandler, span, parentContext);
(StreamedAsyncHandler<?>) asyncHandler, context, parentContext);
} else if (!(asyncHandler instanceof WebSocketUpgradeHandler)) {
// websocket upgrade handlers aren't supported
asyncHandler = new AsyncHandlerWrapper(asyncHandler, span, parentContext);
asyncHandler = new AsyncHandlerWrapper(asyncHandler, context, parentContext);
}
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void methodExit(
@Advice.Thrown Throwable throwable,
@Advice.Local("otelSpan") Span span,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
if (scope == null) {
return;
}
scope.close();
if (throwable != null) {
tracer().endExceptionally(span, throwable);
tracer().endExceptionally(context, throwable);
}
}
}

View File

@ -5,7 +5,6 @@
package io.opentelemetry.javaagent.instrumentation.playws.v1_0;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import org.reactivestreams.Publisher;
import play.shaded.ahc.org.asynchttpclient.handler.StreamedAsyncHandler;
@ -15,8 +14,8 @@ public class StreamedAsyncHandlerWrapper extends AsyncHandlerWrapper
private final StreamedAsyncHandler streamedDelegate;
public StreamedAsyncHandlerWrapper(
StreamedAsyncHandler delegate, Span span, Context invocationContext) {
super(delegate, span, invocationContext);
StreamedAsyncHandler delegate, Context context, Context parentContext) {
super(delegate, context, parentContext);
streamedDelegate = delegate;
}

View File

@ -7,7 +7,6 @@ package io.opentelemetry.javaagent.instrumentation.playws.v2_0;
import static io.opentelemetry.javaagent.instrumentation.playws.PlayWsClientTracer.tracer;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import java.net.InetSocketAddress;
@ -22,15 +21,15 @@ import play.shaded.ahc.org.asynchttpclient.netty.request.NettyRequest;
public class AsyncHandlerWrapper implements AsyncHandler {
private final AsyncHandler delegate;
private final Span span;
private final Context context;
private final Context parentContext;
private final Response.ResponseBuilder builder = new Response.ResponseBuilder();
public AsyncHandlerWrapper(AsyncHandler delegate, Span span) {
public AsyncHandlerWrapper(AsyncHandler delegate, Context context, Context parentContext) {
this.delegate = delegate;
this.span = span;
parentContext = Context.current();
this.context = context;
this.parentContext = parentContext;
}
@Override
@ -55,7 +54,7 @@ public class AsyncHandlerWrapper implements AsyncHandler {
@Override
public Object onCompleted() throws Exception {
Response response = builder.build();
tracer().end(span, response);
tracer().end(context, response);
try (Scope ignored = parentContext.makeCurrent()) {
return delegate.onCompleted();
@ -64,7 +63,7 @@ public class AsyncHandlerWrapper implements AsyncHandler {
@Override
public void onThrowable(Throwable throwable) {
tracer().endExceptionally(span, throwable);
tracer().endExceptionally(context, throwable);
try (Scope ignored = parentContext.makeCurrent()) {
delegate.onThrowable(throwable);

View File

@ -5,12 +5,12 @@
package io.opentelemetry.javaagent.instrumentation.playws.v2_0;
import static io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge.currentContext;
import static io.opentelemetry.javaagent.instrumentation.playws.PlayWsClientTracer.tracer;
import static java.util.Collections.singletonList;
import com.google.auto.service.AutoService;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Scope;
import io.opentelemetry.context.Context;
import io.opentelemetry.javaagent.instrumentation.playws.AsyncHttpClientInstrumentation;
import io.opentelemetry.javaagent.tooling.InstrumentationModule;
import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
@ -37,28 +37,29 @@ public class PlayWsInstrumentationModule extends InstrumentationModule {
public static void methodEnter(
@Advice.Argument(0) Request request,
@Advice.Argument(value = 1, readOnly = false) AsyncHandler<?> asyncHandler,
@Advice.Local("otelSpan") Span span) {
@Advice.Local("otelContext") Context context) {
Context parentContext = currentContext();
if (!tracer().shouldStartSpan(parentContext)) {
return;
}
span = tracer().startSpan(request);
// TODO (trask) expose inject separate from startScope, e.g. for async cases
Scope scope = tracer().startScope(span, request.getHeaders());
scope.close();
context = tracer().startSpan(parentContext, request, request.getHeaders());
if (asyncHandler instanceof StreamedAsyncHandler) {
asyncHandler =
new StreamedAsyncHandlerWrapper((StreamedAsyncHandler<?>) asyncHandler, span);
new StreamedAsyncHandlerWrapper(
(StreamedAsyncHandler<?>) asyncHandler, context, parentContext);
} else if (!(asyncHandler instanceof WebSocketUpgradeHandler)) {
// websocket upgrade handlers aren't supported
asyncHandler = new AsyncHandlerWrapper(asyncHandler, span);
asyncHandler = new AsyncHandlerWrapper(asyncHandler, context, parentContext);
}
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void methodExit(
@Advice.Thrown Throwable throwable, @Advice.Local("otelSpan") Span span) {
if (throwable != null) {
tracer().endExceptionally(span, throwable);
@Advice.Thrown Throwable throwable, @Advice.Local("otelContext") Context context) {
if (context != null && throwable != null) {
tracer().endExceptionally(context, throwable);
}
}
}

View File

@ -5,7 +5,7 @@
package io.opentelemetry.javaagent.instrumentation.playws.v2_0;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import org.reactivestreams.Publisher;
import play.shaded.ahc.org.asynchttpclient.handler.StreamedAsyncHandler;
@ -13,8 +13,9 @@ public class StreamedAsyncHandlerWrapper extends AsyncHandlerWrapper
implements StreamedAsyncHandler {
private final StreamedAsyncHandler streamedDelegate;
public StreamedAsyncHandlerWrapper(StreamedAsyncHandler delegate, Span span) {
super(delegate, span);
public StreamedAsyncHandlerWrapper(
StreamedAsyncHandler delegate, Context context, Context parentContext) {
super(delegate, context, parentContext);
streamedDelegate = delegate;
}

View File

@ -7,7 +7,6 @@ package io.opentelemetry.javaagent.instrumentation.playws.v2_1;
import static io.opentelemetry.javaagent.instrumentation.playws.PlayWsClientTracer.tracer;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import java.net.InetSocketAddress;
@ -23,15 +22,15 @@ import play.shaded.ahc.org.asynchttpclient.netty.request.NettyRequest;
public class AsyncHandlerWrapper implements AsyncHandler {
private final AsyncHandler delegate;
private final Span span;
private final Context context;
private final Context parentContext;
private final Response.ResponseBuilder builder = new Response.ResponseBuilder();
public AsyncHandlerWrapper(AsyncHandler delegate, Span span) {
public AsyncHandlerWrapper(AsyncHandler delegate, Context context, Context parentContext) {
this.delegate = delegate;
this.span = span;
parentContext = Context.current();
this.context = context;
this.parentContext = parentContext;
}
@Override
@ -56,7 +55,7 @@ public class AsyncHandlerWrapper implements AsyncHandler {
@Override
public Object onCompleted() throws Exception {
Response response = builder.build();
tracer().end(span, response);
tracer().end(context, response);
try (Scope ignored = parentContext.makeCurrent()) {
return delegate.onCompleted();
@ -65,7 +64,7 @@ public class AsyncHandlerWrapper implements AsyncHandler {
@Override
public void onThrowable(Throwable throwable) {
tracer().endExceptionally(span, throwable);
tracer().endExceptionally(context, throwable);
try (Scope ignored = parentContext.makeCurrent()) {
delegate.onThrowable(throwable);

View File

@ -5,12 +5,12 @@
package io.opentelemetry.javaagent.instrumentation.playws.v2_1;
import static io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge.currentContext;
import static io.opentelemetry.javaagent.instrumentation.playws.PlayWsClientTracer.tracer;
import static java.util.Collections.singletonList;
import com.google.auto.service.AutoService;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Scope;
import io.opentelemetry.context.Context;
import io.opentelemetry.javaagent.instrumentation.playws.AsyncHttpClientInstrumentation;
import io.opentelemetry.javaagent.tooling.InstrumentationModule;
import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
@ -37,28 +37,29 @@ public class PlayWsInstrumentationModule extends InstrumentationModule {
public static void methodEnter(
@Advice.Argument(0) Request request,
@Advice.Argument(value = 1, readOnly = false) AsyncHandler<?> asyncHandler,
@Advice.Local("otelSpan") Span span) {
@Advice.Local("otelContext") Context context) {
Context parentContext = currentContext();
if (!tracer().shouldStartSpan(parentContext)) {
return;
}
span = tracer().startSpan(request);
// TODO (trask) expose inject separate from startScope, e.g. for async cases
Scope scope = tracer().startScope(span, request.getHeaders());
scope.close();
context = tracer().startSpan(parentContext, request, request.getHeaders());
if (asyncHandler instanceof StreamedAsyncHandler) {
asyncHandler =
new StreamedAsyncHandlerWrapper((StreamedAsyncHandler<?>) asyncHandler, span);
new StreamedAsyncHandlerWrapper(
(StreamedAsyncHandler<?>) asyncHandler, context, parentContext);
} else if (!(asyncHandler instanceof WebSocketUpgradeHandler)) {
// websocket upgrade handlers aren't supported
asyncHandler = new AsyncHandlerWrapper(asyncHandler, span);
asyncHandler = new AsyncHandlerWrapper(asyncHandler, context, parentContext);
}
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void methodExit(
@Advice.Thrown Throwable throwable, @Advice.Local("otelSpan") Span span) {
if (throwable != null) {
tracer().endExceptionally(span, throwable);
@Advice.Thrown Throwable throwable, @Advice.Local("otelContext") Context context) {
if (context != null && throwable != null) {
tracer().endExceptionally(context, throwable);
}
}
}

View File

@ -5,7 +5,7 @@
package io.opentelemetry.javaagent.instrumentation.playws.v2_1;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import org.reactivestreams.Publisher;
import play.shaded.ahc.org.asynchttpclient.handler.StreamedAsyncHandler;
@ -13,8 +13,9 @@ public class StreamedAsyncHandlerWrapper extends AsyncHandlerWrapper
implements StreamedAsyncHandler {
private final StreamedAsyncHandler streamedDelegate;
public StreamedAsyncHandlerWrapper(StreamedAsyncHandler delegate, Span span) {
super(delegate, span);
public StreamedAsyncHandlerWrapper(
StreamedAsyncHandler delegate, Context context, Context parentContext) {
super(delegate, context, parentContext);
streamedDelegate = delegate;
}

View File

@ -16,8 +16,8 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
import com.google.auto.service.AutoService;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.instrumentation.api.CallDepth;
import io.opentelemetry.javaagent.instrumentation.api.CallDepthThreadLocalMap;
import io.opentelemetry.javaagent.instrumentation.api.CallDepthThreadLocalMap.Depth;
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
import io.opentelemetry.javaagent.tooling.InstrumentationModule;
import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
@ -65,7 +65,7 @@ public class HttpServletResponseInstrumentationModule extends InstrumentationMod
@Advice.Origin Method method,
@Advice.Local("otelSpan") Span span,
@Advice.Local("otelScope") Scope scope,
@Advice.Local("otelCallDepth") Depth callDepth) {
@Advice.Local("otelCallDepth") CallDepth callDepth) {
callDepth = CallDepthThreadLocalMap.getCallDepth(HttpServletResponse.class);
// Don't want to generate a new top-level span
if (callDepth.getAndIncrement() == 0
@ -80,7 +80,7 @@ public class HttpServletResponseInstrumentationModule extends InstrumentationMod
@Advice.Thrown Throwable throwable,
@Advice.Local("otelSpan") Span span,
@Advice.Local("otelScope") Scope scope,
@Advice.Local("otelCallDepth") Depth callDepth) {
@Advice.Local("otelCallDepth") CallDepth callDepth) {
if (callDepth.decrementAndGet() == 0 && span != null) {
CallDepthThreadLocalMap.reset(HttpServletResponse.class);

View File

@ -7,8 +7,8 @@ package io.opentelemetry.instrumentation.spring.httpclients;
import static io.opentelemetry.instrumentation.spring.httpclients.RestTemplateTracer.tracer;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import java.io.IOException;
import org.springframework.http.HttpRequest;
@ -28,11 +28,15 @@ public final class RestTemplateInterceptor implements ClientHttpRequestIntercept
@Override
public ClientHttpResponse intercept(
HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
Context parentContext = Context.current();
if (!tracer().shouldStartSpan(parentContext)) {
return execution.execute(request, body);
}
Span span = tracer().startSpan(request);
try (Scope scope = tracer().startScope(span, request.getHeaders())) {
Context context = tracer().startSpan(parentContext, request, request.getHeaders());
try (Scope ignored = context.makeCurrent()) {
ClientHttpResponse response = execution.execute(request, body);
tracer().end(span, response);
tracer().end(context, response);
return response;
}
}

View File

@ -9,6 +9,7 @@ import static io.opentelemetry.instrumentation.spring.webflux.client.HttpHeaders
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.propagation.TextMapPropagator.Setter;
import io.opentelemetry.instrumentation.api.tracer.HttpClientTracer;
import java.lang.invoke.MethodHandle;
@ -30,7 +31,8 @@ public class SpringWebfluxHttpClientTracer
private static final MethodHandle RAW_STATUS_CODE = findRawStatusCode();
public void onCancel(Span span) {
public void onCancel(Context context) {
Span span = Span.fromContext(context);
span.setAttribute("event", "cancelled");
span.setAttribute("message", "The subscription was cancelled");
}

View File

@ -7,7 +7,6 @@ package io.opentelemetry.instrumentation.spring.webflux.client;
import static io.opentelemetry.instrumentation.spring.webflux.client.SpringWebfluxHttpClientTracer.tracer;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Scope;
import org.reactivestreams.Subscription;
import org.springframework.web.reactive.function.client.ClientResponse;
@ -23,15 +22,12 @@ public final class TraceWebClientSubscriber implements CoreSubscriber<ClientResp
final reactor.util.context.Context context;
private final Span span;
private final io.opentelemetry.context.Context tracingContext;
public TraceWebClientSubscriber(
CoreSubscriber<? super ClientResponse> actual,
Span span,
io.opentelemetry.context.Context tracingContext) {
this.actual = actual;
this.span = span;
this.tracingContext = tracingContext;
this.context = actual.currentContext();
}
@ -46,7 +42,7 @@ public final class TraceWebClientSubscriber implements CoreSubscriber<ClientResp
try (Scope ignored = tracingContext.makeCurrent()) {
this.actual.onNext(response);
} finally {
tracer().end(span, response);
tracer().end(tracingContext, response);
}
}
@ -55,7 +51,7 @@ public final class TraceWebClientSubscriber implements CoreSubscriber<ClientResp
try (Scope ignored = tracingContext.makeCurrent()) {
this.actual.onError(t);
} finally {
tracer().endExceptionally(span, t);
tracer().endExceptionally(tracingContext, t);
}
}

View File

@ -7,7 +7,7 @@ package io.opentelemetry.instrumentation.spring.webflux.client;
import static io.opentelemetry.instrumentation.spring.webflux.client.SpringWebfluxHttpClientTracer.tracer;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import java.util.List;
import org.springframework.web.reactive.function.client.ClientRequest;
@ -49,19 +49,22 @@ public class WebClientTracingFilter implements ExchangeFilterFunction {
@Override
public void subscribe(CoreSubscriber<? super ClientResponse> subscriber) {
Span span = tracer().startSpan(request);
Context parentContext = Context.current();
if (!tracer().shouldStartSpan(parentContext)) {
return;
}
ClientRequest.Builder builder = ClientRequest.from(request);
try (Scope ignored = tracer().startScope(span, builder)) {
Context context = tracer().startSpan(parentContext, request, builder);
try (Scope ignored = context.makeCurrent()) {
this.next
.exchange(builder.build())
.doOnCancel(
() -> {
tracer().onCancel(span);
tracer().end(span);
tracer().onCancel(context);
tracer().end(context);
})
.subscribe(
new TraceWebClientSubscriber(
subscriber, span, io.opentelemetry.context.Context.current()));
.subscribe(new TraceWebClientSubscriber(subscriber, context));
}
}
}

View File

@ -0,0 +1,26 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.api;
public final class CallDepth {
private int depth;
CallDepth() {
this.depth = 0;
}
public int getAndIncrement() {
return this.depth++;
}
public int decrementAndGet() {
return --this.depth;
}
void reset() {
depth = 0;
}
}

View File

@ -30,7 +30,7 @@ public class CallDepthThreadLocalMap {
}
};
public static Depth getCallDepth(Class<?> k) {
public static CallDepth getCallDepth(Class<?> k) {
return TLS.get(k).get();
}
@ -43,29 +43,13 @@ public class CallDepthThreadLocalMap {
}
public static void reset(Class<?> k) {
TLS.get(k).get().depth = 0;
TLS.get(k).get().reset();
}
public static final class Depth {
private int depth;
private Depth() {
this.depth = 0;
}
public int getAndIncrement() {
return this.depth++;
}
public int decrementAndGet() {
return --this.depth;
}
}
private static final class ThreadLocalDepth extends ThreadLocal<Depth> {
private static final class ThreadLocalDepth extends ThreadLocal<CallDepth> {
@Override
protected Depth initialValue() {
return new Depth();
protected CallDepth initialValue() {
return new CallDepth();
}
}
}

View File

@ -63,7 +63,7 @@ import io.opentelemetry.context.Scope;
* <li>The new pattern is more efficient since it doesn't require instantiating the {@code
* SpanWithScope} holder object
* <li>The new pattern extends nicely in the common case where we also need to pass {@link
* CallDepthThreadLocalMap.Depth} between the methods
* CallDepth} between the methods
* </ul>
*
* @deprecated see above