diff --git a/instrumentation/apache-httpclient/apache-httpclient-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v2_0/ApacheHttpClientInstrumentation.java b/instrumentation/apache-httpclient/apache-httpclient-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v2_0/ApacheHttpClientInstrumentation.java index 765ac61ec642..68385f2f9491 100644 --- a/instrumentation/apache-httpclient/apache-httpclient-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v2_0/ApacheHttpClientInstrumentation.java +++ b/instrumentation/apache-httpclient/apache-httpclient-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v2_0/ApacheHttpClientInstrumentation.java @@ -18,6 +18,7 @@ import io.opentelemetry.context.Scope; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import javax.annotation.Nullable; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; @@ -47,32 +48,46 @@ public void transform(TypeTransformer transformer) { @SuppressWarnings("unused") public static class ExecuteMethodAdvice { - @Advice.OnMethodEnter(suppress = Throwable.class) - public static void methodEnter( - @Advice.Argument(1) HttpMethod httpMethod, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - Context parentContext = currentContext(); - if (!instrumenter().shouldStart(parentContext, httpMethod)) { - return; + public static class AdviceScope { + private final Context context; + private final Scope scope; + private final HttpMethod httpMethod; + + private AdviceScope(Context context, Scope scope, HttpMethod httpMethod) { + this.context = context; + this.scope = scope; + this.httpMethod = httpMethod; + } + + @Nullable + public static AdviceScope start(HttpMethod httpMethod) { + Context parentContext = currentContext(); + if (!instrumenter().shouldStart(parentContext, httpMethod)) { + return null; + } + Context context = instrumenter().start(parentContext, httpMethod); + return new AdviceScope(context, context.makeCurrent(), httpMethod); + } + + public void end(Throwable throwable) { + scope.close(); + instrumenter().end(context, httpMethod, httpMethod, throwable); } + } - context = instrumenter().start(parentContext, httpMethod); - scope = context.makeCurrent(); + @Advice.OnMethodEnter(suppress = Throwable.class) + public static AdviceScope methodEnter(@Advice.Argument(1) HttpMethod httpMethod) { + return AdviceScope.start(httpMethod); } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void methodExit( - @Advice.Argument(1) HttpMethod httpMethod, - @Advice.Thrown Throwable throwable, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - if (scope == null) { - return; - } + @Advice.Thrown @Nullable Throwable throwable, + @Advice.Enter @Nullable AdviceScope adviceScope) { - scope.close(); - instrumenter().end(context, httpMethod, httpMethod, throwable); + if (adviceScope != null) { + adviceScope.end(throwable); + } } } } diff --git a/instrumentation/apache-httpclient/apache-httpclient-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v2_0/ApacheHttpClientInstrumentationModule.java b/instrumentation/apache-httpclient/apache-httpclient-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v2_0/ApacheHttpClientInstrumentationModule.java index 6b6bd1897a12..ef649105013d 100644 --- a/instrumentation/apache-httpclient/apache-httpclient-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v2_0/ApacheHttpClientInstrumentationModule.java +++ b/instrumentation/apache-httpclient/apache-httpclient-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v2_0/ApacheHttpClientInstrumentationModule.java @@ -10,10 +10,12 @@ import com.google.auto.service.AutoService; import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.internal.ExperimentalInstrumentationModule; import java.util.List; @AutoService(InstrumentationModule.class) -public class ApacheHttpClientInstrumentationModule extends InstrumentationModule { +public class ApacheHttpClientInstrumentationModule extends InstrumentationModule + implements ExperimentalInstrumentationModule { public ApacheHttpClientInstrumentationModule() { super("apache-httpclient", "apache-httpclient-2.0"); @@ -23,4 +25,9 @@ public ApacheHttpClientInstrumentationModule() { public List typeInstrumentations() { return singletonList(new ApacheHttpClientInstrumentation()); } + + @Override + public boolean isIndyReady() { + return true; + } } diff --git a/instrumentation/apache-httpclient/apache-httpclient-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v4_0/ApacheHttpClientHelper.java b/instrumentation/apache-httpclient/apache-httpclient-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v4_0/ApacheHttpClientHelper.java deleted file mode 100644 index bdfc241a81fd..000000000000 --- a/instrumentation/apache-httpclient/apache-httpclient-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v4_0/ApacheHttpClientHelper.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.instrumentation.apachehttpclient.v4_0; - -import static io.opentelemetry.javaagent.instrumentation.apachehttpclient.v4_0.ApacheHttpClientSingletons.instrumenter; - -import io.opentelemetry.context.Context; -import org.apache.http.HttpResponse; - -public final class ApacheHttpClientHelper { - - public static void doMethodExit( - Context context, ApacheHttpClientRequest request, Object result, Throwable throwable) { - if (throwable != null) { - instrumenter().end(context, request, null, throwable); - } else if (result instanceof HttpResponse) { - instrumenter().end(context, request, (HttpResponse) result, null); - } else { - // ended in WrappingStatusSettingResponseHandler - } - } - - private ApacheHttpClientHelper() {} -} diff --git a/instrumentation/apache-httpclient/apache-httpclient-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v4_0/ApacheHttpClientInstrumentation.java b/instrumentation/apache-httpclient/apache-httpclient-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v4_0/ApacheHttpClientInstrumentation.java index 09b38bd89f3b..78e94e664678 100644 --- a/instrumentation/apache-httpclient/apache-httpclient-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v4_0/ApacheHttpClientInstrumentation.java +++ b/instrumentation/apache-httpclient/apache-httpclient-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v4_0/ApacheHttpClientInstrumentation.java @@ -20,11 +20,15 @@ import io.opentelemetry.context.Scope; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import javax.annotation.Nullable; 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.matcher.ElementMatcher; import org.apache.http.HttpHost; import org.apache.http.HttpRequest; +import org.apache.http.HttpResponse; import org.apache.http.client.ResponseHandler; import org.apache.http.client.methods.HttpUriRequest; @@ -123,87 +127,93 @@ public void transform(TypeTransformer transformer) { this.getClass().getName() + "$RequestWithHandlerAdvice"); } - @SuppressWarnings("unused") - public static class UriRequestAdvice { + public static class AdviceScope { + private final ApacheHttpClientRequest otelRequest; + private final Context parentContext; + private final Context context; + private final Scope scope; + + private AdviceScope( + ApacheHttpClientRequest otelRequest, Context parentContext, Context context, Scope scope) { + this.otelRequest = otelRequest; + this.parentContext = parentContext; + this.context = context; + this.scope = scope; + } - @Advice.OnMethodEnter(suppress = Throwable.class) - public static void methodEnter( - @Advice.Argument(0) HttpUriRequest request, - @Advice.Local("otelRequest") ApacheHttpClientRequest otelRequest, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { + @Nullable + public static AdviceScope start(ApacheHttpClientRequest otelRequest) { Context parentContext = currentContext(); + if (!instrumenter().shouldStart(parentContext, otelRequest)) { + return null; + } + Context context = instrumenter().start(parentContext, otelRequest); + return new AdviceScope(otelRequest, parentContext, context, context.makeCurrent()); + } - otelRequest = new ApacheHttpClientRequest(request); + public ResponseHandler wrapHandler(ResponseHandler handler) { + return new WrappingStatusSettingResponseHandler<>( + context, parentContext, otelRequest, handler); + } - if (!instrumenter().shouldStart(parentContext, otelRequest)) { - return; + public void end(@Nullable Object result, @Nullable Throwable throwable) { + scope.close(); + if (throwable != null) { + instrumenter().end(context, otelRequest, null, throwable); + } else if (result instanceof HttpResponse) { + instrumenter().end(context, otelRequest, (HttpResponse) result, null); } + // ended in WrappingStatusSettingResponseHandler + } + } + + @SuppressWarnings("unused") + public static class UriRequestAdvice { - context = instrumenter().start(parentContext, otelRequest); - scope = context.makeCurrent(); + @Advice.OnMethodEnter(suppress = Throwable.class) + public static AdviceScope methodEnter(@Advice.Argument(0) HttpUriRequest request) { + return AdviceScope.start(new ApacheHttpClientRequest(request)); } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void methodExit( @Advice.Argument(0) HttpUriRequest request, - @Advice.Return Object result, - @Advice.Thrown Throwable throwable, - @Advice.Local("otelRequest") ApacheHttpClientRequest otelRequest, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - if (scope == null) { - return; - } + @Advice.Return @Nullable Object result, + @Advice.Thrown @Nullable Throwable throwable, + @Advice.Enter @Nullable AdviceScope adviceScope) { - scope.close(); - ApacheHttpClientHelper.doMethodExit(context, otelRequest, result, throwable); + if (adviceScope != null) { + adviceScope.end(result, throwable); + } } } @SuppressWarnings("unused") public static class UriRequestWithHandlerAdvice { + @AssignReturned.ToArguments(@ToArgument(value = 1, index = 1)) @Advice.OnMethodEnter(suppress = Throwable.class) - public static void methodEnter( + public static Object[] methodEnter( @Advice.Argument(0) HttpUriRequest request, - @Advice.Argument(value = 1, readOnly = false) ResponseHandler handler, - @Advice.Local("otelRequest") ApacheHttpClientRequest otelRequest, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - Context parentContext = currentContext(); - - otelRequest = new ApacheHttpClientRequest(request); - - if (!instrumenter().shouldStart(parentContext, otelRequest)) { - return; - } - - context = instrumenter().start(parentContext, otelRequest); - scope = context.makeCurrent(); + @Advice.Argument(1) ResponseHandler handler) { + AdviceScope adviceScope = AdviceScope.start(new ApacheHttpClientRequest(request)); // Wrap the handler so we capture the status code - if (handler != null) { - handler = - new WrappingStatusSettingResponseHandler<>( - context, parentContext, otelRequest, handler); - } + return new Object[] { + adviceScope, adviceScope == null ? handler : adviceScope.wrapHandler(handler) + }; } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void methodExit( - @Advice.Argument(0) HttpUriRequest request, @Advice.Return Object result, @Advice.Thrown Throwable throwable, - @Advice.Local("otelRequest") ApacheHttpClientRequest otelRequest, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - if (scope == null) { - return; - } + @Advice.Enter Object[] enterResult) { - scope.close(); - ApacheHttpClientHelper.doMethodExit(context, otelRequest, result, throwable); + AdviceScope adviceScope = (AdviceScope) enterResult[0]; + if (adviceScope != null) { + adviceScope.end(result, throwable); + } } } @@ -211,83 +221,51 @@ public static void methodExit( public static class RequestAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) - public static void methodEnter( - @Advice.Argument(0) HttpHost host, - @Advice.Argument(1) HttpRequest request, - @Advice.Local("otelRequest") ApacheHttpClientRequest otelRequest, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - Context parentContext = currentContext(); - - otelRequest = new ApacheHttpClientRequest(host, request); - - if (!instrumenter().shouldStart(parentContext, otelRequest)) { - return; - } - - context = instrumenter().start(parentContext, otelRequest); - scope = context.makeCurrent(); + public static AdviceScope methodEnter( + @Advice.Argument(0) HttpHost host, @Advice.Argument(1) HttpRequest request) { + return AdviceScope.start(new ApacheHttpClientRequest(host, request)); } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void methodExit( @Advice.Return Object result, @Advice.Thrown Throwable throwable, - @Advice.Local("otelRequest") ApacheHttpClientRequest otelRequest, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - if (scope == null) { - return; - } + @Advice.Enter AdviceScope adviceScope) { - scope.close(); - ApacheHttpClientHelper.doMethodExit(context, otelRequest, result, throwable); + if (adviceScope != null) { + adviceScope.end(result, throwable); + } } } @SuppressWarnings("unused") public static class RequestWithHandlerAdvice { + @AssignReturned.ToArguments(@ToArgument(value = 2, index = 1)) @Advice.OnMethodEnter(suppress = Throwable.class) - public static void methodEnter( + public static Object[] methodEnter( @Advice.Argument(0) HttpHost host, @Advice.Argument(1) HttpRequest request, - @Advice.Argument(value = 2, readOnly = false) ResponseHandler handler, - @Advice.Local("otelRequest") ApacheHttpClientRequest otelRequest, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - Context parentContext = currentContext(); - - otelRequest = new ApacheHttpClientRequest(host, request); - - if (!instrumenter().shouldStart(parentContext, otelRequest)) { - return; - } - - context = instrumenter().start(parentContext, otelRequest); - scope = context.makeCurrent(); - - // Wrap the handler so we capture the status code - if (handler != null) { - handler = - new WrappingStatusSettingResponseHandler<>( - context, parentContext, otelRequest, handler); - } + @Advice.Argument(2) ResponseHandler handler) { + + AdviceScope adviceScope = AdviceScope.start(new ApacheHttpClientRequest(host, request)); + return new Object[] { + adviceScope, + // Wrap the handler so we capture the status code + adviceScope == null ? handler : adviceScope.wrapHandler(handler) + }; } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void methodExit( @Advice.Return Object result, @Advice.Thrown Throwable throwable, - @Advice.Local("otelRequest") ApacheHttpClientRequest otelRequest, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - if (scope == null) { - return; - } + @Advice.Enter Object[] enterResult) { - scope.close(); - ApacheHttpClientHelper.doMethodExit(context, otelRequest, result, throwable); + AdviceScope adviceScope = (AdviceScope) enterResult[0]; + if (adviceScope != null) { + adviceScope.end(result, throwable); + } } } } diff --git a/instrumentation/apache-httpclient/apache-httpclient-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v4_0/ApacheHttpClientInstrumentationModule.java b/instrumentation/apache-httpclient/apache-httpclient-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v4_0/ApacheHttpClientInstrumentationModule.java index b0821b7dcfc3..c89fc473d45c 100644 --- a/instrumentation/apache-httpclient/apache-httpclient-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v4_0/ApacheHttpClientInstrumentationModule.java +++ b/instrumentation/apache-httpclient/apache-httpclient-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v4_0/ApacheHttpClientInstrumentationModule.java @@ -10,10 +10,12 @@ import com.google.auto.service.AutoService; import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.internal.ExperimentalInstrumentationModule; import java.util.List; @AutoService(InstrumentationModule.class) -public class ApacheHttpClientInstrumentationModule extends InstrumentationModule { +public class ApacheHttpClientInstrumentationModule extends InstrumentationModule + implements ExperimentalInstrumentationModule { public ApacheHttpClientInstrumentationModule() { super("apache-httpclient", "apache-httpclient-4.0"); @@ -23,4 +25,9 @@ public ApacheHttpClientInstrumentationModule() { public List typeInstrumentations() { return singletonList(new ApacheHttpClientInstrumentation()); } + + @Override + public boolean isIndyReady() { + return true; + } } diff --git a/instrumentation/apache-httpclient/apache-httpclient-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v5_0/ApacheHttpAsyncClientInstrumentation.java b/instrumentation/apache-httpclient/apache-httpclient-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v5_0/ApacheHttpAsyncClientInstrumentation.java index 1b7b8d8149b1..e43f6fc51750 100644 --- a/instrumentation/apache-httpclient/apache-httpclient-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v5_0/ApacheHttpAsyncClientInstrumentation.java +++ b/instrumentation/apache-httpclient/apache-httpclient-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v5_0/ApacheHttpAsyncClientInstrumentation.java @@ -23,6 +23,8 @@ import java.util.logging.Logger; import javax.annotation.Nullable; 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.matcher.ElementMatcher; import org.apache.hc.core5.concurrent.FutureCallback; @@ -66,11 +68,17 @@ public void transform(TypeTransformer transformer) { @SuppressWarnings("unused") public static class ClientAdvice { + @AssignReturned.ToArguments({ + @ToArgument(value = 0, index = 0), + @ToArgument(value = 3, index = 1), + @ToArgument(value = 4, index = 2) + }) @Advice.OnMethodEnter(suppress = Throwable.class) - public static void methodEnter( - @Advice.Argument(value = 0, readOnly = false) AsyncRequestProducer requestProducer, - @Advice.Argument(value = 3, readOnly = false) HttpContext httpContext, - @Advice.Argument(value = 4, readOnly = false) FutureCallback futureCallback) { + public static Object[] methodEnter( + @Advice.Argument(0) AsyncRequestProducer requestProducer, + @Advice.Argument(3) HttpContext originalHttpContext, + @Advice.Argument(4) FutureCallback futureCallback) { + HttpContext httpContext = originalHttpContext; Context parentContext = currentContext(); if (httpContext == null) { @@ -79,9 +87,11 @@ public static void methodEnter( WrappedFutureCallback wrappedFutureCallback = new WrappedFutureCallback<>(parentContext, httpContext, futureCallback); - requestProducer = - new DelegatingRequestProducer(parentContext, requestProducer, wrappedFutureCallback); - futureCallback = wrappedFutureCallback; + return new Object[] { + new DelegatingRequestProducer(parentContext, requestProducer, wrappedFutureCallback), + httpContext, + wrappedFutureCallback + }; } } diff --git a/instrumentation/apache-httpclient/apache-httpclient-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v5_0/ApacheHttpClientHelper.java b/instrumentation/apache-httpclient/apache-httpclient-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v5_0/ApacheHttpClientHelper.java deleted file mode 100644 index 0a75742dd513..000000000000 --- a/instrumentation/apache-httpclient/apache-httpclient-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v5_0/ApacheHttpClientHelper.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.instrumentation.apachehttpclient.v5_0; - -import static io.opentelemetry.javaagent.instrumentation.apachehttpclient.v5_0.ApacheHttpClientSingletons.instrumenter; - -import io.opentelemetry.context.Context; -import org.apache.hc.core5.http.ClassicHttpRequest; -import org.apache.hc.core5.http.HttpResponse; - -public class ApacheHttpClientHelper { - - public static void doMethodExit( - Context context, ClassicHttpRequest request, Object result, Throwable throwable) { - if (throwable != null) { - instrumenter().end(context, request, null, throwable); - } else if (result instanceof HttpResponse) { - instrumenter().end(context, request, (HttpResponse) result, null); - } else { - // ended in WrappingStatusSettingResponseHandler - } - } - - private ApacheHttpClientHelper() {} -} diff --git a/instrumentation/apache-httpclient/apache-httpclient-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v5_0/ApacheHttpClientInstrumentation.java b/instrumentation/apache-httpclient/apache-httpclient-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v5_0/ApacheHttpClientInstrumentation.java index b5897ec9ecc6..79ba36350b8c 100644 --- a/instrumentation/apache-httpclient/apache-httpclient-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v5_0/ApacheHttpClientInstrumentation.java +++ b/instrumentation/apache-httpclient/apache-httpclient-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v5_0/ApacheHttpClientInstrumentation.java @@ -20,11 +20,15 @@ import io.opentelemetry.context.Scope; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import javax.annotation.Nullable; 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.matcher.ElementMatcher; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.HttpHost; +import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.io.HttpClientResponseHandler; class ApacheHttpClientInstrumentation implements TypeInstrumentation { @@ -121,242 +125,234 @@ public void transform(TypeTransformer transformer) { this.getClass().getName() + "$RequestWithHostAndContextAndHandlerAdvice"); } - @SuppressWarnings("unused") - public static class RequestAdvice { + public static class AdviceScope { + private final ClassicHttpRequest request; + private final Context parentContext; + private final Context context; + private final Scope scope; + + private AdviceScope( + ClassicHttpRequest request, Context parentContext, Context context, Scope scope) { + this.request = request; + this.context = context; + this.parentContext = parentContext; + this.scope = scope; + } - @Advice.OnMethodEnter(suppress = Throwable.class) - public static void methodEnter( - @Advice.Argument(0) ClassicHttpRequest request, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { + @Nullable + public static AdviceScope start(ClassicHttpRequest request) { Context parentContext = currentContext(); + if (!instrumenter().shouldStart(parentContext, request)) { - return; + return null; } + Context context = instrumenter().start(parentContext, request); + return new AdviceScope(request, parentContext, context, context.makeCurrent()); + } - context = instrumenter().start(parentContext, request); - scope = context.makeCurrent(); + public void end(@Nullable Object result, @Nullable Throwable throwable) { + scope.close(); + if (throwable != null) { + instrumenter().end(context, request, null, throwable); + } else if (result instanceof HttpResponse) { + instrumenter().end(context, request, (HttpResponse) result, null); + } + // ended in WrappingStatusSettingResponseHandler + } + + public WrappingStatusSettingResponseHandler wrapResponseHandler( + HttpClientResponseHandler handler) { + return new WrappingStatusSettingResponseHandler<>(context, parentContext, request, handler); + } + } + + @SuppressWarnings("unused") + public static class RequestAdvice { + + @Nullable + @Advice.OnMethodEnter(suppress = Throwable.class) + public static AdviceScope methodEnter(@Advice.Argument(0) ClassicHttpRequest request) { + return AdviceScope.start(request); } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void methodExit( - @Advice.Argument(0) ClassicHttpRequest request, @Advice.Return Object result, - @Advice.Thrown Throwable throwable, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - if (scope == null) { - return; - } + @Advice.Thrown @Nullable Throwable throwable, + @Advice.Enter @Nullable AdviceScope scope) { - scope.close(); - ApacheHttpClientHelper.doMethodExit(context, request, result, throwable); + if (scope != null) { + scope.end(result, throwable); + } } } @SuppressWarnings("unused") public static class RequestWithHandlerAdvice { + @AssignReturned.ToArguments(@ToArgument(value = 1, index = 1)) @Advice.OnMethodEnter(suppress = Throwable.class) - public static void methodEnter( + public static Object[] methodEnter( @Advice.Argument(0) ClassicHttpRequest request, - @Advice.Argument(value = 1, readOnly = false) HttpClientResponseHandler handler, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - Context parentContext = currentContext(); - if (!instrumenter().shouldStart(parentContext, request)) { - return; - } + @Advice.Argument(1) HttpClientResponseHandler originalHandler) { - context = instrumenter().start(parentContext, request); - scope = context.makeCurrent(); + HttpClientResponseHandler handler = originalHandler; + AdviceScope scope = AdviceScope.start(request); + if (scope == null) { + return new Object[] {null, handler}; + } // Wrap the handler so we capture the status code if (handler != null) { - handler = - new WrappingStatusSettingResponseHandler<>(context, parentContext, request, handler); + handler = scope.wrapResponseHandler(handler); } + return new Object[] {scope, handler}; } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void methodExit( @Advice.Argument(0) ClassicHttpRequest request, - @Advice.Return Object result, - @Advice.Thrown Throwable throwable, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - if (scope == null) { - return; - } + @Advice.Return @Nullable Object result, + @Advice.Thrown @Nullable Throwable throwable, + @Advice.Enter Object[] enterResult) { - scope.close(); - ApacheHttpClientHelper.doMethodExit(context, request, result, throwable); + AdviceScope scope = (AdviceScope) enterResult[0]; + if (scope != null) { + scope.end(result, throwable); + } } } @SuppressWarnings("unused") public static class RequestWithContextAndHandlerAdvice { + @AssignReturned.ToArguments(@ToArgument(value = 2, index = 1)) @Advice.OnMethodEnter(suppress = Throwable.class) - public static void methodEnter( + public static Object[] methodEnter( @Advice.Argument(0) ClassicHttpRequest request, - @Advice.Argument(value = 2, readOnly = false) HttpClientResponseHandler handler, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - Context parentContext = currentContext(); - if (!instrumenter().shouldStart(parentContext, request)) { - return; - } + @Advice.Argument(2) HttpClientResponseHandler originalHandler) { - context = instrumenter().start(parentContext, request); - scope = context.makeCurrent(); + HttpClientResponseHandler handler = originalHandler; + AdviceScope scope = AdviceScope.start(request); + if (scope == null) { + return new Object[] {null, handler}; + } // Wrap the handler so we capture the status code if (handler != null) { - handler = - new WrappingStatusSettingResponseHandler<>(context, parentContext, request, handler); + handler = scope.wrapResponseHandler(handler); } + return new Object[] {scope, handler}; } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void methodExit( @Advice.Argument(0) ClassicHttpRequest request, - @Advice.Return Object result, - @Advice.Thrown Throwable throwable, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - if (scope == null) { - return; - } + @Advice.Return @Nullable Object result, + @Advice.Thrown @Nullable Throwable throwable, + @Advice.Enter Object[] enterResult) { - scope.close(); - ApacheHttpClientHelper.doMethodExit(context, request, result, throwable); + AdviceScope scope = (AdviceScope) enterResult[0]; + if (scope != null) { + scope.end(result, throwable); + } } } @SuppressWarnings("unused") public static class RequestWithHostAdvice { + @Nullable @Advice.OnMethodEnter(suppress = Throwable.class) - public static void methodEnter( - @Advice.Argument(0) HttpHost host, - @Advice.Argument(1) ClassicHttpRequest request, - @Advice.Local("otelFullRequest") ClassicHttpRequest fullRequest, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - Context parentContext = currentContext(); - fullRequest = new RequestWithHost(host, request); - if (!instrumenter().shouldStart(parentContext, fullRequest)) { - return; - } + public static AdviceScope methodEnter( + @Advice.Argument(0) HttpHost host, @Advice.Argument(1) ClassicHttpRequest request) { - context = instrumenter().start(parentContext, fullRequest); - scope = context.makeCurrent(); + return AdviceScope.start(new RequestWithHost(host, request)); } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void methodExit( @Advice.Return Object result, - @Advice.Thrown Throwable throwable, - @Advice.Local("otelFullRequest") ClassicHttpRequest fullRequest, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - if (scope == null) { - return; - } + @Advice.Thrown @Nullable Throwable throwable, + @Advice.Enter @Nullable AdviceScope scope) { - scope.close(); - ApacheHttpClientHelper.doMethodExit(context, fullRequest, result, throwable); + if (scope != null) { + scope.end(result, throwable); + } } } @SuppressWarnings("unused") public static class RequestWithHostAndHandlerAdvice { + @AssignReturned.ToArguments(@ToArgument(value = 2, index = 1)) @Advice.OnMethodEnter(suppress = Throwable.class) - public static void methodEnter( + public static Object[] methodEnter( @Advice.Argument(0) HttpHost host, @Advice.Argument(1) ClassicHttpRequest request, - @Advice.Argument(value = 2, readOnly = false) HttpClientResponseHandler handler, - @Advice.Local("otelFullRequest") ClassicHttpRequest fullRequest, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { + @Advice.Argument(2) HttpClientResponseHandler originalHandler) { - Context parentContext = currentContext(); - fullRequest = new RequestWithHost(host, request); - if (!instrumenter().shouldStart(parentContext, fullRequest)) { - return; - } + HttpClientResponseHandler handler = originalHandler; + AdviceScope scope = AdviceScope.start(new RequestWithHost(host, request)); - context = instrumenter().start(parentContext, fullRequest); - scope = context.makeCurrent(); + if (scope == null) { + return new Object[] {null, handler}; + } // Wrap the handler so we capture the status code if (handler != null) { - handler = - new WrappingStatusSettingResponseHandler<>( - context, parentContext, fullRequest, handler); + handler = scope.wrapResponseHandler(handler); } + return new Object[] {scope, handler}; } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void methodExit( - @Advice.Return Object result, - @Advice.Thrown Throwable throwable, - @Advice.Local("otelFullRequest") ClassicHttpRequest fullRequest, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - if (scope == null) { - return; - } + @Advice.Return @Nullable Object result, + @Advice.Thrown @Nullable Throwable throwable, + @Advice.Enter Object[] enterResult) { - scope.close(); - ApacheHttpClientHelper.doMethodExit(context, fullRequest, result, throwable); + AdviceScope scope = (AdviceScope) enterResult[0]; + if (scope != null) { + scope.end(result, throwable); + } } } @SuppressWarnings("unused") public static class RequestWithHostAndContextAndHandlerAdvice { + @AssignReturned.ToArguments(@ToArgument(value = 3, index = 1)) @Advice.OnMethodEnter(suppress = Throwable.class) - public static void methodEnter( + public static Object[] methodEnter( @Advice.Argument(0) HttpHost host, @Advice.Argument(1) ClassicHttpRequest request, - @Advice.Argument(value = 3, readOnly = false) HttpClientResponseHandler handler, - @Advice.Local("otelFullRequest") ClassicHttpRequest fullRequest, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { + @Advice.Argument(3) HttpClientResponseHandler originalHandler) { - Context parentContext = currentContext(); - fullRequest = new RequestWithHost(host, request); - if (!instrumenter().shouldStart(parentContext, fullRequest)) { - return; - } + HttpClientResponseHandler handler = originalHandler; + AdviceScope scope = AdviceScope.start(new RequestWithHost(host, request)); - context = instrumenter().start(parentContext, fullRequest); - scope = context.makeCurrent(); + if (scope == null) { + return new Object[] {null, handler}; + } // Wrap the handler so we capture the status code if (handler != null) { - handler = - new WrappingStatusSettingResponseHandler<>( - context, parentContext, fullRequest, handler); + handler = scope.wrapResponseHandler(handler); } + return new Object[] {scope, handler}; } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void methodExit( @Advice.Return Object result, @Advice.Thrown Throwable throwable, - @Advice.Local("otelFullRequest") ClassicHttpRequest fullRequest, - @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { - if (scope == null) { - return; - } + @Advice.Enter Object[] enterResult) { - scope.close(); - ApacheHttpClientHelper.doMethodExit(context, fullRequest, result, throwable); + AdviceScope scope = (AdviceScope) enterResult[0]; + if (scope != null) { + scope.end(result, throwable); + } } } } diff --git a/instrumentation/apache-httpclient/apache-httpclient-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v5_0/ApacheHttpClientInstrumentationModule.java b/instrumentation/apache-httpclient/apache-httpclient-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v5_0/ApacheHttpClientInstrumentationModule.java index 0e478a395f1c..43febca4b4eb 100644 --- a/instrumentation/apache-httpclient/apache-httpclient-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v5_0/ApacheHttpClientInstrumentationModule.java +++ b/instrumentation/apache-httpclient/apache-httpclient-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v5_0/ApacheHttpClientInstrumentationModule.java @@ -8,11 +8,13 @@ import com.google.auto.service.AutoService; import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.internal.ExperimentalInstrumentationModule; import java.util.Arrays; import java.util.List; @AutoService(InstrumentationModule.class) -public class ApacheHttpClientInstrumentationModule extends InstrumentationModule { +public class ApacheHttpClientInstrumentationModule extends InstrumentationModule + implements ExperimentalInstrumentationModule { public ApacheHttpClientInstrumentationModule() { super("apache-httpclient", "apache-httpclient-5.0"); @@ -23,4 +25,9 @@ public List typeInstrumentations() { return Arrays.asList( new ApacheHttpClientInstrumentation(), new ApacheHttpAsyncClientInstrumentation()); } + + @Override + public boolean isIndyReady() { + return true; + } }