migrate more http clients to indy-ready (#13897)

Co-authored-by: Lauri Tulmin <tulmin@gmail.com>
This commit is contained in:
SylvainJuge 2025-05-29 17:28:20 +02:00 committed by GitHub
parent 4ec198fbad
commit 8081608fae
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 349 additions and 220 deletions

View File

@ -10,10 +10,12 @@ import static java.util.Arrays.asList;
import com.google.auto.service.AutoService; import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.internal.ExperimentalInstrumentationModule;
import java.util.List; import java.util.List;
@AutoService(InstrumentationModule.class) @AutoService(InstrumentationModule.class)
public class AsyncHttpClientInstrumentationModule extends InstrumentationModule { public class AsyncHttpClientInstrumentationModule extends InstrumentationModule
implements ExperimentalInstrumentationModule {
public AsyncHttpClientInstrumentationModule() { public AsyncHttpClientInstrumentationModule() {
super("async-http-client", "async-http-client-1.9"); super("async-http-client", "async-http-client-1.9");
} }
@ -22,4 +24,9 @@ public class AsyncHttpClientInstrumentationModule extends InstrumentationModule
public List<TypeInstrumentation> typeInstrumentations() { public List<TypeInstrumentation> typeInstrumentations() {
return asList(new RequestInstrumentation(), new ResponseInstrumentation()); return asList(new RequestInstrumentation(), new ResponseInstrumentation());
} }
@Override
public boolean isIndyReady() {
return true;
}
} }

View File

@ -5,9 +5,11 @@
package io.opentelemetry.javaagent.instrumentation.asynchttpclient.v1_9; package io.opentelemetry.javaagent.instrumentation.asynchttpclient.v1_9;
import com.ning.http.client.AsyncHandler;
import com.ning.http.client.Request; import com.ning.http.client.Request;
import com.ning.http.client.Response; import com.ning.http.client.Response;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.instrumentation.api.util.VirtualField;
import io.opentelemetry.javaagent.bootstrap.internal.JavaagentHttpClientInstrumenters; import io.opentelemetry.javaagent.bootstrap.internal.JavaagentHttpClientInstrumenters;
public final class AsyncHttpClientSingletons { public final class AsyncHttpClientSingletons {
@ -15,12 +17,16 @@ public final class AsyncHttpClientSingletons {
private static final Instrumenter<Request, Response> INSTRUMENTER; private static final Instrumenter<Request, Response> INSTRUMENTER;
public static final VirtualField<AsyncHandler<?>, AsyncHandlerData> ASYNC_HANDLER_DATA;
static { static {
INSTRUMENTER = INSTRUMENTER =
JavaagentHttpClientInstrumenters.create( JavaagentHttpClientInstrumenters.create(
INSTRUMENTATION_NAME, INSTRUMENTATION_NAME,
new AsyncHttpClientHttpAttributesGetter(), new AsyncHttpClientHttpAttributesGetter(),
HttpHeaderSetter.INSTANCE); HttpHeaderSetter.INSTANCE);
ASYNC_HANDLER_DATA = VirtualField.find(AsyncHandler.class, AsyncHandlerData.class);
} }
public static Instrumenter<Request, Response> instrumenter() { public static Instrumenter<Request, Response> instrumenter() {

View File

@ -6,6 +6,7 @@
package io.opentelemetry.javaagent.instrumentation.asynchttpclient.v1_9; package io.opentelemetry.javaagent.instrumentation.asynchttpclient.v1_9;
import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext; import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext;
import static io.opentelemetry.javaagent.instrumentation.asynchttpclient.v1_9.AsyncHttpClientSingletons.ASYNC_HANDLER_DATA;
import static io.opentelemetry.javaagent.instrumentation.asynchttpclient.v1_9.AsyncHttpClientSingletons.instrumenter; import static io.opentelemetry.javaagent.instrumentation.asynchttpclient.v1_9.AsyncHttpClientSingletons.instrumenter;
import static net.bytebuddy.matcher.ElementMatchers.isPublic; import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
@ -15,9 +16,9 @@ import com.ning.http.client.AsyncHandler;
import com.ning.http.client.Request; import com.ning.http.client.Request;
import io.opentelemetry.context.Context; import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope; import io.opentelemetry.context.Scope;
import io.opentelemetry.instrumentation.api.util.VirtualField;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import javax.annotation.Nullable;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatcher;
@ -42,24 +43,22 @@ public class RequestInstrumentation implements TypeInstrumentation {
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static class ExecuteAdvice { public static class ExecuteAdvice {
@Nullable
@Advice.OnMethodEnter(suppress = Throwable.class) @Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter( public static Scope onEnter(
@Advice.Argument(0) Request request, @Advice.Argument(0) Request request, @Advice.Argument(1) AsyncHandler<?> handler) {
@Advice.Argument(1) AsyncHandler<?> handler,
@Advice.Local("otelScope") Scope scope) {
Context parentContext = currentContext(); Context parentContext = currentContext();
if (!instrumenter().shouldStart(parentContext, request)) { if (!instrumenter().shouldStart(parentContext, request)) {
return; return null;
} }
Context context = instrumenter().start(parentContext, request); Context context = instrumenter().start(parentContext, request);
VirtualField.find(AsyncHandler.class, AsyncHandlerData.class) ASYNC_HANDLER_DATA.set(handler, AsyncHandlerData.create(parentContext, context, request));
.set(handler, AsyncHandlerData.create(parentContext, context, request)); return context.makeCurrent();
scope = context.makeCurrent();
} }
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void onExit(@Advice.Local("otelScope") Scope scope) { public static void onExit(@Advice.Enter @Nullable Scope scope) {
if (scope != null) { if (scope != null) {
scope.close(); scope.close();
} }

View File

@ -6,6 +6,7 @@
package io.opentelemetry.javaagent.instrumentation.asynchttpclient.v1_9; package io.opentelemetry.javaagent.instrumentation.asynchttpclient.v1_9;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
import static io.opentelemetry.javaagent.instrumentation.asynchttpclient.v1_9.AsyncHttpClientSingletons.ASYNC_HANDLER_DATA;
import static io.opentelemetry.javaagent.instrumentation.asynchttpclient.v1_9.AsyncHttpClientSingletons.instrumenter; import static io.opentelemetry.javaagent.instrumentation.asynchttpclient.v1_9.AsyncHttpClientSingletons.instrumenter;
import static net.bytebuddy.matcher.ElementMatchers.hasSuperClass; import static net.bytebuddy.matcher.ElementMatchers.hasSuperClass;
import static net.bytebuddy.matcher.ElementMatchers.isPublic; import static net.bytebuddy.matcher.ElementMatchers.isPublic;
@ -13,10 +14,8 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument; import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import com.ning.http.client.AsyncCompletionHandler; import com.ning.http.client.AsyncCompletionHandler;
import com.ning.http.client.AsyncHandler;
import com.ning.http.client.Response; import com.ning.http.client.Response;
import io.opentelemetry.context.Scope; import io.opentelemetry.context.Scope;
import io.opentelemetry.instrumentation.api.util.VirtualField;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
@ -54,13 +53,11 @@ public class ResponseInstrumentation implements TypeInstrumentation {
public static Scope onEnter( public static Scope onEnter(
@Advice.This AsyncCompletionHandler<?> handler, @Advice.Argument(0) Response response) { @Advice.This AsyncCompletionHandler<?> handler, @Advice.Argument(0) Response response) {
VirtualField<AsyncHandler<?>, AsyncHandlerData> virtualField = AsyncHandlerData data = ASYNC_HANDLER_DATA.get(handler);
VirtualField.find(AsyncHandler.class, AsyncHandlerData.class);
AsyncHandlerData data = virtualField.get(handler);
if (data == null) { if (data == null) {
return null; return null;
} }
virtualField.set(handler, null); ASYNC_HANDLER_DATA.set(handler, null);
instrumenter().end(data.getContext(), data.getRequest(), response, null); instrumenter().end(data.getContext(), data.getRequest(), response, null);
return data.getParentContext().makeCurrent(); return data.getParentContext().makeCurrent();
} }
@ -80,13 +77,11 @@ public class ResponseInstrumentation implements TypeInstrumentation {
public static Scope onEnter( public static Scope onEnter(
@Advice.This AsyncCompletionHandler<?> handler, @Advice.Argument(0) Throwable throwable) { @Advice.This AsyncCompletionHandler<?> handler, @Advice.Argument(0) Throwable throwable) {
VirtualField<AsyncHandler<?>, AsyncHandlerData> virtualField = AsyncHandlerData data = ASYNC_HANDLER_DATA.get(handler);
VirtualField.find(AsyncHandler.class, AsyncHandlerData.class);
AsyncHandlerData data = virtualField.get(handler);
if (data == null) { if (data == null) {
return null; return null;
} }
virtualField.set(handler, null); ASYNC_HANDLER_DATA.set(handler, null);
instrumenter().end(data.getContext(), data.getRequest(), null, throwable); instrumenter().end(data.getContext(), data.getRequest(), null, throwable);
return data.getParentContext().makeCurrent(); return data.getParentContext().makeCurrent();
} }

View File

@ -6,6 +6,7 @@
package io.opentelemetry.javaagent.instrumentation.asynchttpclient.v2_0; package io.opentelemetry.javaagent.instrumentation.asynchttpclient.v2_0;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
import static io.opentelemetry.javaagent.instrumentation.asynchttpclient.v2_0.AsyncHttpClientSingletons.ASYNC_HANDLER_REQUEST_CONTEXT;
import static io.opentelemetry.javaagent.instrumentation.asynchttpclient.v2_0.AsyncHttpClientSingletons.instrumenter; import static io.opentelemetry.javaagent.instrumentation.asynchttpclient.v2_0.AsyncHttpClientSingletons.instrumenter;
import static net.bytebuddy.matcher.ElementMatchers.hasSuperClass; import static net.bytebuddy.matcher.ElementMatchers.hasSuperClass;
import static net.bytebuddy.matcher.ElementMatchers.isPublic; import static net.bytebuddy.matcher.ElementMatchers.isPublic;
@ -13,14 +14,12 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument; import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import io.opentelemetry.context.Scope; import io.opentelemetry.context.Scope;
import io.opentelemetry.instrumentation.api.util.VirtualField;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatcher;
import org.asynchttpclient.AsyncCompletionHandler; import org.asynchttpclient.AsyncCompletionHandler;
import org.asynchttpclient.AsyncHandler;
import org.asynchttpclient.Response; import org.asynchttpclient.Response;
public class AsyncCompletionHandlerInstrumentation implements TypeInstrumentation { public class AsyncCompletionHandlerInstrumentation implements TypeInstrumentation {
@ -54,13 +53,11 @@ public class AsyncCompletionHandlerInstrumentation implements TypeInstrumentatio
public static Scope onEnter( public static Scope onEnter(
@Advice.This AsyncCompletionHandler<?> handler, @Advice.Argument(0) Response response) { @Advice.This AsyncCompletionHandler<?> handler, @Advice.Argument(0) Response response) {
VirtualField<AsyncHandler<?>, RequestContext> virtualField = RequestContext requestContext = ASYNC_HANDLER_REQUEST_CONTEXT.get(handler);
VirtualField.find(AsyncHandler.class, RequestContext.class);
RequestContext requestContext = virtualField.get(handler);
if (requestContext == null) { if (requestContext == null) {
return null; return null;
} }
virtualField.set(handler, null); ASYNC_HANDLER_REQUEST_CONTEXT.set(handler, null);
instrumenter().end(requestContext.getContext(), requestContext, response, null); instrumenter().end(requestContext.getContext(), requestContext, response, null);
return requestContext.getParentContext().makeCurrent(); return requestContext.getParentContext().makeCurrent();
} }
@ -80,13 +77,11 @@ public class AsyncCompletionHandlerInstrumentation implements TypeInstrumentatio
public static Scope onEnter( public static Scope onEnter(
@Advice.This AsyncCompletionHandler<?> handler, @Advice.Argument(0) Throwable throwable) { @Advice.This AsyncCompletionHandler<?> handler, @Advice.Argument(0) Throwable throwable) {
VirtualField<AsyncHandler<?>, RequestContext> virtualField = RequestContext requestContext = ASYNC_HANDLER_REQUEST_CONTEXT.get(handler);
VirtualField.find(AsyncHandler.class, RequestContext.class);
RequestContext requestContext = virtualField.get(handler);
if (requestContext == null) { if (requestContext == null) {
return null; return null;
} }
virtualField.set(handler, null); ASYNC_HANDLER_REQUEST_CONTEXT.set(handler, null);
instrumenter().end(requestContext.getContext(), requestContext, null, throwable); instrumenter().end(requestContext.getContext(), requestContext, null, throwable);
return requestContext.getParentContext().makeCurrent(); return requestContext.getParentContext().makeCurrent();
} }

View File

@ -7,6 +7,7 @@ package io.opentelemetry.javaagent.instrumentation.asynchttpclient.v2_0;
import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext; import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface; import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface;
import static io.opentelemetry.javaagent.instrumentation.asynchttpclient.v2_0.AsyncHttpClientSingletons.ASYNC_HANDLER_REQUEST_CONTEXT;
import static io.opentelemetry.javaagent.instrumentation.asynchttpclient.v2_0.AsyncHttpClientSingletons.instrumenter; import static io.opentelemetry.javaagent.instrumentation.asynchttpclient.v2_0.AsyncHttpClientSingletons.instrumenter;
import static net.bytebuddy.matcher.ElementMatchers.isPublic; import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
@ -14,9 +15,9 @@ import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import io.opentelemetry.context.Context; import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope; import io.opentelemetry.context.Scope;
import io.opentelemetry.instrumentation.api.util.VirtualField;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import javax.annotation.Nullable;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatcher;
@ -43,15 +44,14 @@ public class AsyncHttpClientInstrumentation implements TypeInstrumentation {
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static class ExecuteRequestAdvice { public static class ExecuteRequestAdvice {
@Nullable
@Advice.OnMethodEnter(suppress = Throwable.class) @Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter( public static Scope onEnter(
@Advice.Argument(0) Request request, @Advice.Argument(0) Request request, @Advice.Argument(1) AsyncHandler<?> handler) {
@Advice.Argument(1) AsyncHandler<?> handler,
@Advice.Local("otelScope") Scope scope) {
Context parentContext = currentContext(); Context parentContext = currentContext();
RequestContext requestContext = new RequestContext(parentContext, request); RequestContext requestContext = new RequestContext(parentContext, request);
if (!instrumenter().shouldStart(parentContext, requestContext)) { if (!instrumenter().shouldStart(parentContext, requestContext)) {
return; return null;
} }
Context context = instrumenter().start(parentContext, requestContext); Context context = instrumenter().start(parentContext, requestContext);
@ -70,12 +70,12 @@ public class AsyncHttpClientInstrumentation implements TypeInstrumentation {
// 2.1, so the instrumentation module will need to be essentially duplicated (or a common // 2.1, so the instrumentation module will need to be essentially duplicated (or a common
// module introduced) // module introduced)
VirtualField.find(AsyncHandler.class, RequestContext.class).set(handler, requestContext); ASYNC_HANDLER_REQUEST_CONTEXT.set(handler, requestContext);
scope = context.makeCurrent(); return context.makeCurrent();
} }
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void onExit(@Advice.Local("otelScope") Scope scope) { public static void onExit(@Advice.Enter @Nullable Scope scope) {
if (scope != null) { if (scope != null) {
scope.close(); scope.close();
} }

View File

@ -10,10 +10,12 @@ import static java.util.Arrays.asList;
import com.google.auto.service.AutoService; import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.internal.ExperimentalInstrumentationModule;
import java.util.List; import java.util.List;
@AutoService(InstrumentationModule.class) @AutoService(InstrumentationModule.class)
public class AsyncHttpClientInstrumentationModule extends InstrumentationModule { public class AsyncHttpClientInstrumentationModule extends InstrumentationModule
implements ExperimentalInstrumentationModule {
public AsyncHttpClientInstrumentationModule() { public AsyncHttpClientInstrumentationModule() {
super("async-http-client", "async-http-client-2.0"); super("async-http-client", "async-http-client-2.0");
} }
@ -26,4 +28,9 @@ public class AsyncHttpClientInstrumentationModule extends InstrumentationModule
new NettyRequestSenderInstrumentation(), new NettyRequestSenderInstrumentation(),
new NettyResponseFutureInstrumentation()); new NettyResponseFutureInstrumentation());
} }
@Override
public boolean isIndyReady() {
return true;
}
} }

View File

@ -5,14 +5,20 @@
package io.opentelemetry.javaagent.instrumentation.asynchttpclient.v2_0; package io.opentelemetry.javaagent.instrumentation.asynchttpclient.v2_0;
import io.opentelemetry.context.Context;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.instrumentation.api.util.VirtualField;
import io.opentelemetry.javaagent.bootstrap.internal.JavaagentHttpClientInstrumenters; import io.opentelemetry.javaagent.bootstrap.internal.JavaagentHttpClientInstrumenters;
import org.asynchttpclient.AsyncHandler;
import org.asynchttpclient.Request;
import org.asynchttpclient.Response; import org.asynchttpclient.Response;
public final class AsyncHttpClientSingletons { public final class AsyncHttpClientSingletons {
private static final String INSTRUMENTATION_NAME = "io.opentelemetry.async-http-client-2.0"; private static final String INSTRUMENTATION_NAME = "io.opentelemetry.async-http-client-2.0";
private static final Instrumenter<RequestContext, Response> INSTRUMENTER; private static final Instrumenter<RequestContext, Response> INSTRUMENTER;
public static final VirtualField<AsyncHandler<?>, RequestContext> ASYNC_HANDLER_REQUEST_CONTEXT;
public static final VirtualField<Request, Context> REQUEST_CONTEXT;
static { static {
INSTRUMENTER = INSTRUMENTER =
@ -20,6 +26,9 @@ public final class AsyncHttpClientSingletons {
INSTRUMENTATION_NAME, INSTRUMENTATION_NAME,
new AsyncHttpClientHttpAttributesGetter(), new AsyncHttpClientHttpAttributesGetter(),
HttpHeaderSetter.INSTANCE); HttpHeaderSetter.INSTANCE);
ASYNC_HANDLER_REQUEST_CONTEXT = VirtualField.find(AsyncHandler.class, RequestContext.class);
REQUEST_CONTEXT = VirtualField.find(Request.class, Context.class);
} }
public static Instrumenter<RequestContext, Response> instrumenter() { public static Instrumenter<RequestContext, Response> instrumenter() {

View File

@ -5,6 +5,8 @@
package io.opentelemetry.javaagent.instrumentation.asynchttpclient.v2_0; package io.opentelemetry.javaagent.instrumentation.asynchttpclient.v2_0;
import static io.opentelemetry.javaagent.instrumentation.asynchttpclient.v2_0.AsyncHttpClientSingletons.ASYNC_HANDLER_REQUEST_CONTEXT;
import static io.opentelemetry.javaagent.instrumentation.asynchttpclient.v2_0.AsyncHttpClientSingletons.REQUEST_CONTEXT;
import static net.bytebuddy.matcher.ElementMatchers.isPublic; import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.returns; import static net.bytebuddy.matcher.ElementMatchers.returns;
@ -12,14 +14,12 @@ import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import io.opentelemetry.context.Context; import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope; import io.opentelemetry.context.Scope;
import io.opentelemetry.instrumentation.api.util.VirtualField;
import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge; import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatcher;
import org.asynchttpclient.AsyncHandler;
import org.asynchttpclient.Request; import org.asynchttpclient.Request;
import org.asynchttpclient.netty.NettyResponseFuture; import org.asynchttpclient.netty.NettyResponseFuture;
@ -59,8 +59,7 @@ public class NettyRequestSenderInstrumentation implements TypeInstrumentation {
@Advice.OnMethodEnter @Advice.OnMethodEnter
public static void attachContext(@Advice.Argument(0) Request request) { public static void attachContext(@Advice.Argument(0) Request request) {
VirtualField.find(Request.class, Context.class) REQUEST_CONTEXT.set(request, Java8BytecodeBridge.currentContext());
.set(request, Java8BytecodeBridge.currentContext());
} }
} }
@ -70,7 +69,7 @@ public class NettyRequestSenderInstrumentation implements TypeInstrumentation {
@Advice.OnMethodEnter @Advice.OnMethodEnter
public static Scope mountContext(@Advice.Argument(0) NettyResponseFuture<?> responseFuture) { public static Scope mountContext(@Advice.Argument(0) NettyResponseFuture<?> responseFuture) {
Request request = responseFuture.getCurrentRequest(); Request request = responseFuture.getCurrentRequest();
Context context = VirtualField.find(Request.class, Context.class).get(request); Context context = REQUEST_CONTEXT.get(request);
return context == null ? null : context.makeCurrent(); return context == null ? null : context.makeCurrent();
} }
@ -88,8 +87,7 @@ public class NettyRequestSenderInstrumentation implements TypeInstrumentation {
@Advice.OnMethodExit @Advice.OnMethodExit
public static void rememberNettyRequest(@Advice.Return NettyResponseFuture<?> responseFuture) { public static void rememberNettyRequest(@Advice.Return NettyResponseFuture<?> responseFuture) {
RequestContext requestContext = RequestContext requestContext =
VirtualField.find(AsyncHandler.class, RequestContext.class) ASYNC_HANDLER_REQUEST_CONTEXT.get(responseFuture.getAsyncHandler());
.get(responseFuture.getAsyncHandler());
if (requestContext != null) { if (requestContext != null) {
requestContext.setNettyRequest(responseFuture.getNettyRequest()); requestContext.setNettyRequest(responseFuture.getNettyRequest());
} }

View File

@ -14,6 +14,7 @@ import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
import net.bytebuddy.asm.Advice.AssignReturned;
import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatcher;
@ -34,9 +35,10 @@ public class NettyResponseFutureInstrumentation implements TypeInstrumentation {
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static class WrapFutureAdvice { public static class WrapFutureAdvice {
@AssignReturned.ToReturned
@Advice.OnMethodExit(suppress = Throwable.class) @Advice.OnMethodExit(suppress = Throwable.class)
public static void onExit(@Advice.Return(readOnly = false) CompletableFuture<?> result) { public static CompletableFuture<?> onExit(@Advice.Return CompletableFuture<?> result) {
result = CompletableFutureWrapper.wrap(result, Java8BytecodeBridge.currentContext()); return CompletableFutureWrapper.wrap(result, Java8BytecodeBridge.currentContext());
} }
} }
} }

View File

@ -10,10 +10,12 @@ import static java.util.Collections.singletonList;
import com.google.auto.service.AutoService; import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.internal.ExperimentalInstrumentationModule;
import java.util.List; import java.util.List;
@AutoService(InstrumentationModule.class) @AutoService(InstrumentationModule.class)
public class GoogleHttpClientInstrumentationModule extends InstrumentationModule { public class GoogleHttpClientInstrumentationModule extends InstrumentationModule
implements ExperimentalInstrumentationModule {
public GoogleHttpClientInstrumentationModule() { public GoogleHttpClientInstrumentationModule() {
super("google-http-client", "google-http-client-1.19"); super("google-http-client", "google-http-client-1.19");
} }
@ -22,4 +24,9 @@ public class GoogleHttpClientInstrumentationModule extends InstrumentationModule
public List<TypeInstrumentation> typeInstrumentations() { public List<TypeInstrumentation> typeInstrumentations() {
return singletonList(new GoogleHttpRequestInstrumentation()); return singletonList(new GoogleHttpRequestInstrumentation());
} }
@Override
public boolean isIndyReady() {
return true;
}
} }

View File

@ -21,6 +21,7 @@ import io.opentelemetry.instrumentation.api.util.VirtualField;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import javax.annotation.Nullable;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatcher;
@ -49,78 +50,108 @@ public class GoogleHttpRequestInstrumentation implements TypeInstrumentation {
this.getClass().getName() + "$ExecuteAsyncAdvice"); this.getClass().getName() + "$ExecuteAsyncAdvice");
} }
public static class AdviceScope {
private static final VirtualField<HttpRequest, Context> HTTP_REQUEST_CONTEXT =
VirtualField.find(HttpRequest.class, Context.class);
private final Context context;
private final Scope scope;
private final HttpRequest request;
public AdviceScope(Context context, Scope scope, HttpRequest request) {
this.context = context;
this.scope = scope;
this.request = request;
}
@Nullable
public static AdviceScope start(HttpRequest request) {
Context parentContext = currentContext();
if (!instrumenter().shouldStart(parentContext, request)) {
return null;
}
Context context = instrumenter().start(parentContext, request);
return new AdviceScope(context, context.makeCurrent(), request);
}
public void end(HttpResponse response, Throwable throwable) {
scope.close();
instrumenter().end(context, request, response, throwable);
}
public void endWhenThrown(HttpRequest request, Throwable throwable) {
scope.close();
if (throwable != null) {
instrumenter().end(context, request, null, throwable);
}
}
public static AdviceScope fromVirtualFieldContext(HttpRequest request) {
Context context = HTTP_REQUEST_CONTEXT.get(request);
if (context == null) {
return null;
}
return new AdviceScope(context, context.makeCurrent(), request);
}
public void storeContextToVirtualField(HttpRequest request) {
HTTP_REQUEST_CONTEXT.set(request, context);
}
}
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static class ExecuteAdvice { public static class ExecuteAdvice {
@Nullable
@Advice.OnMethodEnter(suppress = Throwable.class) @Advice.OnMethodEnter(suppress = Throwable.class)
public static void methodEnter( public static AdviceScope methodEnter(@Advice.This HttpRequest request) {
@Advice.This HttpRequest request,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
context = VirtualField.find(HttpRequest.class, Context.class).get(request); AdviceScope scope = AdviceScope.fromVirtualFieldContext(request);
if (context != null) { if (scope != null) {
// span was created by GoogleHttpClientAsyncAdvice instrumentation below // span was created by GoogleHttpClientAsyncAdvice instrumentation below
// (executeAsync ends up calling execute from a separate thread) // (executeAsync ends up calling execute from a separate thread)
// so make it current and end it in method exit // so make it current and end it in method exit
scope = context.makeCurrent(); return scope;
return;
} }
Context parentContext = currentContext();
if (!instrumenter().shouldStart(parentContext, request)) { return AdviceScope.start(request);
return;
}
context = instrumenter().start(parentContext, request);
scope = context.makeCurrent();
} }
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void methodExit( public static void methodExit(
@Advice.This HttpRequest request,
@Advice.Return HttpResponse response, @Advice.Return HttpResponse response,
@Advice.Thrown Throwable throwable, @Advice.Thrown Throwable throwable,
@Advice.Local("otelContext") Context context, @Advice.Enter @Nullable AdviceScope scope) {
@Advice.Local("otelScope") Scope scope) {
if (scope == null) {
return;
}
scope.close(); if (scope != null) {
instrumenter().end(context, request, response, throwable); scope.end(response, throwable);
}
} }
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static class ExecuteAsyncAdvice { public static class ExecuteAsyncAdvice {
@Nullable
@Advice.OnMethodEnter(suppress = Throwable.class) @Advice.OnMethodEnter(suppress = Throwable.class)
public static void methodEnter( public static AdviceScope methodEnter(@Advice.This HttpRequest request) {
@Advice.This HttpRequest request,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
Context parentContext = currentContext();
if (!instrumenter().shouldStart(parentContext, request)) {
return;
}
context = instrumenter().start(parentContext, request);
scope = context.makeCurrent();
VirtualField.find(HttpRequest.class, Context.class).set(request, context); AdviceScope scope = AdviceScope.start(request);
if (scope != null) {
scope.storeContextToVirtualField(request);
}
return scope;
} }
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void methodExit( public static void methodExit(
@Advice.This HttpRequest request, @Advice.This HttpRequest request,
@Advice.Thrown Throwable throwable, @Advice.Thrown Throwable throwable,
@Advice.Local("otelContext") Context context, @Advice.Enter @Nullable AdviceScope scope) {
@Advice.Local("otelScope") Scope scope) {
if (scope == null) {
return;
}
scope.close(); if (scope != null) {
if (throwable != null) { scope.endWhenThrown(request, throwable);
instrumenter().end(context, request, null, throwable);
} }
} }
} }

View File

@ -28,7 +28,9 @@ import java.net.http.HttpClient;
import java.net.http.HttpRequest; import java.net.http.HttpRequest;
import java.net.http.HttpResponse; import java.net.http.HttpResponse;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import javax.annotation.Nullable;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
import net.bytebuddy.asm.Advice.AssignReturned;
import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatcher;
@ -68,84 +70,122 @@ public class HttpClientInstrumentation implements TypeInstrumentation {
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static class SendAdvice { public static class SendAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class) public static class AdviceScope {
public static void methodEnter( private final Context context;
@Advice.Argument(value = 0) HttpRequest httpRequest, private final Scope scope;
@Advice.Local("otelContext") Context context, private final HttpRequest request;
@Advice.Local("otelScope") Scope scope) {
Context parentContext = currentContext(); private AdviceScope(Context context, Scope scope, HttpRequest request) {
if (!instrumenter().shouldStart(parentContext, httpRequest)) { this.context = context;
return; this.scope = scope;
this.request = request;
} }
context = instrumenter().start(parentContext, httpRequest); @Nullable
scope = context.makeCurrent(); public static AdviceScope start(HttpRequest request) {
Context parentContext = currentContext();
if (!instrumenter().shouldStart(parentContext, request)) {
return null;
}
Context context = instrumenter().start(parentContext, request);
return new AdviceScope(context, context.makeCurrent(), request);
}
public void end(@Nullable HttpResponse<?> response, @Nullable Throwable throwable) {
scope.close();
instrumenter().end(context, request, response, throwable);
}
}
@Nullable
@Advice.OnMethodEnter(suppress = Throwable.class)
public static AdviceScope methodEnter(@Advice.Argument(value = 0) HttpRequest httpRequest) {
return AdviceScope.start(httpRequest);
} }
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void methodExit( public static void methodExit(
@Advice.Argument(0) HttpRequest httpRequest, @Advice.Argument(0) HttpRequest httpRequest,
@Advice.Return HttpResponse<?> httpResponse, @Advice.Return @Nullable HttpResponse<?> httpResponse,
@Advice.Thrown Throwable throwable, @Advice.Thrown @Nullable Throwable throwable,
@Advice.Local("otelContext") Context context, @Advice.Enter @Nullable AdviceScope scope) {
@Advice.Local("otelScope") Scope scope) {
if (scope == null) {
return;
}
scope.close(); if (scope != null) {
instrumenter().end(context, httpRequest, httpResponse, throwable); scope.end(httpResponse, throwable);
}
} }
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static class SendAsyncAdvice { public static class SendAsyncAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class) public static class AsyncAdviceScope {
public static void methodEnter( private final Context parentContext;
@Advice.Argument(value = 0) HttpRequest httpRequest, private final Context context;
@Advice.Local("otelCallDepth") CallDepth callDepth, private final Scope scope;
@Advice.Local("otelContext") Context context, private final CallDepth callDepth;
@Advice.Local("otelParentContext") Context parentContext, private final HttpRequest request;
@Advice.Local("otelScope") Scope scope) {
callDepth = CallDepth.forClass(HttpClient.class); public AsyncAdviceScope(
if (callDepth.getAndIncrement() > 0) { Context parentContext,
return; Context context,
Scope scope,
CallDepth callDepth,
HttpRequest request) {
this.parentContext = parentContext;
this.context = context;
this.scope = scope;
this.callDepth = callDepth;
this.request = request;
} }
parentContext = currentContext(); @Nullable
if (!instrumenter().shouldStart(parentContext, httpRequest)) { public static AsyncAdviceScope start(HttpRequest request) {
return; CallDepth callDepth = CallDepth.forClass(HttpClient.class);
if (callDepth.getAndIncrement() > 0) {
return new AsyncAdviceScope(null, null, null, callDepth, request);
}
Context parentContext = currentContext();
if (!instrumenter().shouldStart(parentContext, request)) {
return null;
}
Context context = instrumenter().start(parentContext, request);
return new AsyncAdviceScope(
parentContext, context, context.makeCurrent(), callDepth, request);
} }
context = instrumenter().start(parentContext, httpRequest); public CompletableFuture<HttpResponse<?>> end(
scope = context.makeCurrent(); @Nullable Throwable throwable, @Nullable CompletableFuture<HttpResponse<?>> future) {
if (callDepth.decrementAndGet() > 0 || scope == null) {
// async end nested call
return future;
}
scope.close();
if (throwable != null) {
// async end with exception: ending span and no wrapping needed
instrumenter().end(context, request, null, throwable);
return future;
}
future = future.whenComplete(new ResponseConsumer(instrumenter(), context, request));
return CompletableFutureWrapper.wrap(future, parentContext);
}
} }
@Nullable
@Advice.OnMethodEnter(suppress = Throwable.class)
public static AsyncAdviceScope methodEnter(
@Advice.Argument(value = 0) HttpRequest httpRequest) {
return AsyncAdviceScope.start(httpRequest);
}
@AssignReturned.ToReturned
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void methodExit( public static CompletableFuture<HttpResponse<?>> methodExit(
@Advice.Argument(value = 0) HttpRequest httpRequest, @Advice.Return @Nullable CompletableFuture<HttpResponse<?>> future,
@Advice.Return(readOnly = false) CompletableFuture<HttpResponse<?>> future, @Advice.Thrown @Nullable Throwable throwable,
@Advice.Thrown Throwable throwable, @Advice.Enter @Nullable AsyncAdviceScope scope) {
@Advice.Local("otelCallDepth") CallDepth callDepth, return scope == null ? future : scope.end(throwable, future);
@Advice.Local("otelContext") Context context,
@Advice.Local("otelParentContext") Context parentContext,
@Advice.Local("otelScope") Scope scope) {
if (callDepth.decrementAndGet() > 0) {
return;
}
if (scope == null) {
return;
}
scope.close();
if (throwable != null) {
instrumenter().end(context, httpRequest, null, throwable);
} else {
future = future.whenComplete(new ResponseConsumer(instrumenter(), context, httpRequest));
future = CompletableFutureWrapper.wrap(future, parentContext);
}
} }
} }
} }

View File

@ -10,10 +10,12 @@ import static java.util.Arrays.asList;
import com.google.auto.service.AutoService; import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.internal.ExperimentalInstrumentationModule;
import java.util.List; import java.util.List;
@AutoService(InstrumentationModule.class) @AutoService(InstrumentationModule.class)
public class HttpClientInstrumentationModule extends InstrumentationModule { public class HttpClientInstrumentationModule extends InstrumentationModule
implements ExperimentalInstrumentationModule {
public HttpClientInstrumentationModule() { public HttpClientInstrumentationModule() {
super("java-http-client"); super("java-http-client");
} }
@ -22,4 +24,9 @@ public class HttpClientInstrumentationModule extends InstrumentationModule {
public List<TypeInstrumentation> typeInstrumentations() { public List<TypeInstrumentation> typeInstrumentations() {
return asList(new HttpClientInstrumentation(), new HttpHeadersInstrumentation()); return asList(new HttpClientInstrumentation(), new HttpHeadersInstrumentation());
} }
@Override
public boolean isIndyReady() {
return true;
}
} }

View File

@ -16,6 +16,7 @@ import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import java.net.http.HttpHeaders; import java.net.http.HttpHeaders;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
import net.bytebuddy.asm.Advice.AssignReturned;
import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatcher;
@ -38,9 +39,10 @@ public class HttpHeadersInstrumentation implements TypeInstrumentation {
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static class HeadersAdvice { public static class HeadersAdvice {
@AssignReturned.ToReturned
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void methodExit(@Advice.Return(readOnly = false) HttpHeaders headers) { public static HttpHeaders methodExit(@Advice.Return HttpHeaders headers) {
headers = setter().inject(headers, Context.current()); return setter().inject(headers, Context.current());
} }
} }
} }

View File

@ -16,6 +16,7 @@ import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope; import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import javax.annotation.Nullable;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatcher;
@ -54,55 +55,46 @@ public class JettyClient12ResponseListenersInstrumentation implements TypeInstru
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static class JettyHttpClient12RespListenersNotifyAdvice { public static class JettyHttpClient12RespListenersNotifyAdvice {
@Nullable
@Advice.OnMethodEnter(suppress = Throwable.class) @Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnterNotify( public static Scope onEnterNotify(@Advice.Argument(0) Response response) {
@Advice.Argument(0) Response response,
@Advice.Local("otelContext") Context context, Context context =
@Advice.Local("otelScope") Scope scope) { (Context) response.getRequest().getAttributes().get(JETTY_CLIENT_CONTEXT_KEY);
context = (Context) response.getRequest().getAttributes().get(JETTY_CLIENT_CONTEXT_KEY); return context == null ? null : context.makeCurrent();
if (context != null) {
scope = context.makeCurrent();
}
} }
@Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class)
public static void onExitNotify( public static void onExitNotify(
@Advice.Argument(0) Response response, @Advice.Argument(0) Response response,
@Advice.Thrown Throwable throwable, @Advice.Thrown Throwable throwable,
@Advice.Local("otelContext") Context context, @Advice.Enter @Nullable Scope scope) {
@Advice.Local("otelScope") Scope scope) {
if (scope == null) {
return;
}
scope.close(); if (scope != null) {
scope.close();
}
} }
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static class JettyHttpClient12CompleteListenersNotifyAdvice { public static class JettyHttpClient12CompleteListenersNotifyAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class) @Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnterComplete( public static Scope onEnterComplete(@Advice.Argument(0) Result result) {
@Advice.Argument(0) Result result,
@Advice.Local("otelContext") Context context, Context context = (Context) result.getRequest().getAttributes().get(JETTY_CLIENT_CONTEXT_KEY);
@Advice.Local("otelScope") Scope scope) { return context == null ? null : context.makeCurrent();
context = (Context) result.getRequest().getAttributes().get(JETTY_CLIENT_CONTEXT_KEY);
if (context != null) {
scope = context.makeCurrent();
}
} }
@Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class)
public static void onExitComplete( public static void onExitComplete(
@Advice.Argument(0) Result result, @Advice.Argument(0) Result result,
@Advice.Thrown Throwable throwable, @Advice.Thrown Throwable throwable,
@Advice.Local("otelContext") Context context, @Advice.Enter @Nullable Scope scope) {
@Advice.Local("otelScope") Scope scope) { if (scope != null) {
if (scope == null) { scope.close();
return;
} }
scope.close();
} }
} }
} }

View File

@ -17,6 +17,7 @@ import io.opentelemetry.context.Scope;
import io.opentelemetry.instrumentation.jetty.httpclient.v12_0.internal.JettyClientTracingListener; import io.opentelemetry.instrumentation.jetty.httpclient.v12_0.internal.JettyClientTracingListener;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import javax.annotation.Nullable;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatcher;
@ -45,63 +46,68 @@ public class JettyHttpClient12Instrumentation implements TypeInstrumentation {
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static class JettyHttpClient12SendAdvice { public static class JettyHttpClient12SendAdvice {
public static class AdviceLocals {
public final Context context;
public final Scope scope;
public AdviceLocals(Context context, Scope scope) {
this.context = context;
this.scope = scope;
}
}
@Nullable
@Advice.OnMethodEnter(suppress = Throwable.class) @Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnterSend( public static AdviceLocals onEnterSend(@Advice.This HttpRequest request) {
@Advice.This HttpRequest request,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
// start span // start span
Context parentContext = Context.current(); Context parentContext = Context.current();
context = JettyClientTracingListener.handleRequest(parentContext, request, instrumenter()); Context context =
JettyClientTracingListener.handleRequest(parentContext, request, instrumenter());
if (context == null) { if (context == null) {
return; return null;
} }
// set context for responseListeners // set context for responseListeners
request.attribute(JETTY_CLIENT_CONTEXT_KEY, parentContext); request.attribute(JETTY_CLIENT_CONTEXT_KEY, parentContext);
scope = context.makeCurrent(); return new AdviceLocals(context, context.makeCurrent());
} }
@Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class)
public static void onExitSend( public static void onExitSend(
@Advice.This HttpRequest request, @Advice.This HttpRequest request,
@Advice.Thrown Throwable throwable, @Advice.Thrown Throwable throwable,
@Advice.Local("otelContext") Context context, @Advice.Enter @Nullable AdviceLocals locals) {
@Advice.Local("otelScope") Scope scope) {
if (scope == null) { if (locals == null) {
return; return;
} }
// not ending span here unless error, span ended in the interceptor // not ending span here unless error, span ended in the interceptor
scope.close(); locals.scope.close();
if (throwable != null) { if (throwable != null) {
instrumenter().end(context, request, null, throwable); instrumenter().end(locals.context, request, null, throwable);
} }
} }
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static class JettyHttpClient12NotifyAdvice { public static class JettyHttpClient12NotifyAdvice {
@Nullable
@Advice.OnMethodEnter(suppress = Throwable.class) @Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnterNotify( public static Scope onEnterNotify(@Advice.This HttpRequest request) {
@Advice.This HttpRequest request, Context context = (Context) request.getAttributes().get(JETTY_CLIENT_CONTEXT_KEY);
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
context = (Context) request.getAttributes().get(JETTY_CLIENT_CONTEXT_KEY);
if (context == null) { if (context == null) {
return; return null;
} }
scope = context.makeCurrent(); return context.makeCurrent();
} }
@Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class)
public static void onExitNotify( public static void onExitNotify(@Advice.Enter Scope scope) {
@Advice.Local("otelContext") Context context, @Advice.Local("otelScope") Scope scope) { if (scope != null) {
if (scope == null) { scope.close();
return;
} }
scope.close();
} }
} }
} }

View File

@ -10,10 +10,12 @@ import static java.util.Arrays.asList;
import com.google.auto.service.AutoService; import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.internal.ExperimentalInstrumentationModule;
import java.util.List; import java.util.List;
@AutoService(InstrumentationModule.class) @AutoService(InstrumentationModule.class)
public class JettyHttpClient12InstrumentationModule extends InstrumentationModule { public class JettyHttpClient12InstrumentationModule extends InstrumentationModule
implements ExperimentalInstrumentationModule {
public JettyHttpClient12InstrumentationModule() { public JettyHttpClient12InstrumentationModule() {
super("jetty-httpclient", "jetty-httpclient-12.0"); super("jetty-httpclient", "jetty-httpclient-12.0");
} }
@ -24,4 +26,9 @@ public class JettyHttpClient12InstrumentationModule extends InstrumentationModul
new JettyHttpClient12Instrumentation(), new JettyHttpClient12Instrumentation(),
new JettyClient12ResponseListenersInstrumentation()); new JettyClient12ResponseListenersInstrumentation());
} }
@Override
public boolean isIndyReady() {
return true;
}
} }

View File

@ -19,6 +19,8 @@ import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import java.util.List; import java.util.List;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
import net.bytebuddy.asm.Advice.AssignReturned;
import net.bytebuddy.asm.Advice.AssignReturned.ToArguments.ToArgument;
import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatcher;
import org.eclipse.jetty.client.HttpRequest; import org.eclipse.jetty.client.HttpRequest;
@ -43,38 +45,48 @@ public class JettyHttpClient9Instrumentation implements TypeInstrumentation {
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static class JettyHttpClient9Advice { public static class JettyHttpClient9Advice {
public static class AdviceLocals {
public final Context context;
public final Scope scope;
public AdviceLocals(Context context, Scope scope) {
this.context = context;
this.scope = scope;
}
}
@AssignReturned.ToArguments(@ToArgument(value = 1, index = 1))
@Advice.OnMethodEnter(suppress = Throwable.class) @Advice.OnMethodEnter(suppress = Throwable.class)
public static void addTracingEnter( public static Object[] addTracingEnter(
@Advice.Argument(value = 0) HttpRequest httpRequest, @Advice.Argument(value = 0) HttpRequest httpRequest,
@Advice.Argument(value = 1, readOnly = false) List<Response.ResponseListener> listeners, @Advice.Argument(1) List<Response.ResponseListener> listeners) {
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
Context parentContext = currentContext(); Context parentContext = currentContext();
context = Context context =
JettyClientTracingListener.handleRequest(parentContext, httpRequest, instrumenter()); JettyClientTracingListener.handleRequest(parentContext, httpRequest, instrumenter());
if (context == null) { if (context == null) {
return; return new Object[] {null, listeners};
} }
listeners = wrapResponseListeners(parentContext, listeners); List<Response.ResponseListener> wrappedListeners =
scope = context.makeCurrent(); wrapResponseListeners(parentContext, listeners);
return new Object[] {new AdviceLocals(context, context.makeCurrent()), wrappedListeners};
} }
@Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class)
public static void exitTracingInterceptor( public static void exitTracingInterceptor(
@Advice.Argument(value = 0) HttpRequest httpRequest, @Advice.Argument(value = 0) HttpRequest httpRequest,
@Advice.Thrown Throwable throwable, @Advice.Thrown Throwable throwable,
@Advice.Local("otelContext") Context context, @Advice.Enter Object[] enterResult) {
@Advice.Local("otelScope") Scope scope) { AdviceLocals locals = (AdviceLocals) enterResult[0];
if (scope == null) { if (locals == null) {
return; return;
} }
// not ending span here unless error, span ended in the interceptor // not ending span here unless error, span ended in the interceptor
scope.close(); locals.scope.close();
if (throwable != null) { if (throwable != null) {
instrumenter().end(context, httpRequest, null, throwable); instrumenter().end(locals.context, httpRequest, null, throwable);
} }
} }
} }

View File

@ -11,11 +11,13 @@ import static java.util.Collections.singletonList;
import com.google.auto.service.AutoService; import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.internal.ExperimentalInstrumentationModule;
import java.util.List; import java.util.List;
import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatcher;
@AutoService(InstrumentationModule.class) @AutoService(InstrumentationModule.class)
public class JettyHttpClient9InstrumentationModule extends InstrumentationModule { public class JettyHttpClient9InstrumentationModule extends InstrumentationModule
implements ExperimentalInstrumentationModule {
public JettyHttpClient9InstrumentationModule() { public JettyHttpClient9InstrumentationModule() {
super("jetty-httpclient", "jetty-httpclient-9.2"); super("jetty-httpclient", "jetty-httpclient-9.2");
@ -31,4 +33,9 @@ public class JettyHttpClient9InstrumentationModule extends InstrumentationModule
// AbstractTypedContentProvider showed up in version Jetty Client 9.2 on to 10.x // AbstractTypedContentProvider showed up in version Jetty Client 9.2 on to 10.x
return hasClassesNamed("org.eclipse.jetty.client.util.AbstractTypedContentProvider"); return hasClassesNamed("org.eclipse.jetty.client.util.AbstractTypedContentProvider");
} }
@Override
public boolean isIndyReady() {
return true;
}
} }