diff --git a/.fossa.yml b/.fossa.yml index 161ceb50792f..0b6c25d6df66 100644 --- a/.fossa.yml +++ b/.fossa.yml @@ -337,6 +337,12 @@ targets: - type: gradle path: ./ target: ':instrumentation:armeria:armeria-grpc-1.14:javaagent' + - type: gradle + path: ./ + target: ':instrumentation:async-http-client:async-http-client-1-common:javaagent' + - type: gradle + path: ./ + target: ':instrumentation:async-http-client:async-http-client-1.8:javaagent' - type: gradle path: ./ target: ':instrumentation:async-http-client:async-http-client-1.9:javaagent' diff --git a/docs/supported-libraries.md b/docs/supported-libraries.md index 48406ce915a7..2eb0694bcc62 100644 --- a/docs/supported-libraries.md +++ b/docs/supported-libraries.md @@ -46,7 +46,7 @@ These are the supported libraries and frameworks: | [Apache Wicket](https://wicket.apache.org/) | 8.0+ | N/A | Provides `http.route` [2] | | [Armeria](https://armeria.dev) | 1.3+ | [opentelemetry-armeria-1.3](../instrumentation/armeria/armeria-1.3/library) | [HTTP Client Spans], [HTTP Client Metrics], [HTTP Server Spans], [HTTP Server Metrics] | | [Armeria gRPC](https://armeria.dev) | 1.14+ | | [RPC Client Spans], [RPC Client Metrics], [RPC Server Spans], [RPC Server Metrics] | -| [AsyncHttpClient](https://github.com/AsyncHttpClient/async-http-client) | 1.9+ | N/A | [HTTP Client Spans], [HTTP Client Metrics] | +| [AsyncHttpClient](https://github.com/AsyncHttpClient/async-http-client) | 1.8+ | N/A | [HTTP Client Spans], [HTTP Client Metrics] | | [Avaje Jex](https://avaje.io/jex/) | 3.0+ | N/A | Provides `http.route` [2] | | [AWS Lambda](https://docs.aws.amazon.com/lambda/latest/dg/java-handler.html) | 1.0+ | [opentelemetry-aws-lambda-core-1.0](../instrumentation/aws-lambda/aws-lambda-core-1.0/library),
[opentelemetry-aws-lambda-events-3.11](../instrumentation/aws-lambda/aws-lambda-events-3.11/library) | [FaaS Server Spans] | | [AWS SDK](https://aws.amazon.com/sdk-for-java/) | 1.11 - 1.12.583,
2.2+ | [opentelemetry-aws-sdk-1.11](../instrumentation/aws-sdk/aws-sdk-1.11/library),
[opentelemetry-aws-sdk-1.11-autoconfigure](../instrumentation/aws-sdk/aws-sdk-1.11/library-autoconfigure),
[opentelemetry-aws-sdk-2.2](../instrumentation/aws-sdk/aws-sdk-2.2/library),
[opentelemetry-aws-sdk-2.2-autoconfigure](../instrumentation/aws-sdk/aws-sdk-2.2/library-autoconfigure) | [Messaging Spans], [Database Client Spans], [Database Client Metrics] [6], [HTTP Client Spans], [GenAI Client Spans], [GenAI Client Metrics] | diff --git a/instrumentation/async-http-client/async-http-client-1-common/javaagent/build.gradle.kts b/instrumentation/async-http-client/async-http-client-1-common/javaagent/build.gradle.kts new file mode 100644 index 000000000000..8f984b4d940a --- /dev/null +++ b/instrumentation/async-http-client/async-http-client-1-common/javaagent/build.gradle.kts @@ -0,0 +1,10 @@ +plugins { + id("otel.javaagent-instrumentation") +} + +dependencies { + compileOnly("com.ning:async-http-client:1.8.0") + + compileOnly("com.google.auto.value:auto-value-annotations") + annotationProcessor("com.google.auto.value:auto-value") +} diff --git a/instrumentation/async-http-client/async-http-client-1-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/common/AsyncHandlerData.java b/instrumentation/async-http-client/async-http-client-1-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/common/AsyncHandlerData.java new file mode 100644 index 000000000000..e704bb743d0e --- /dev/null +++ b/instrumentation/async-http-client/async-http-client-1-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/common/AsyncHandlerData.java @@ -0,0 +1,38 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.asynchttpclient.common; + +import com.google.auto.value.AutoValue; +import com.ning.http.client.Request; +import com.ning.http.client.Response; +import io.opentelemetry.context.Context; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import javax.annotation.Nullable; + +@AutoValue +public abstract class AsyncHandlerData { + + public static AsyncHandlerData create( + Context parentContext, + Context context, + Request request, + Instrumenter instrumenter) { + return new AutoValue_AsyncHandlerData(parentContext, context, request, instrumenter); + } + + public abstract Context getParentContext(); + + public abstract Context getContext(); + + public abstract Request getRequest(); + + public abstract Instrumenter getInstrumenter(); + + public void end(@Nullable Response response, @Nullable Throwable throwable) { + Instrumenter instrumenter = getInstrumenter(); + instrumenter.end(getContext(), getRequest(), response, throwable); + } +} diff --git a/instrumentation/async-http-client/async-http-client-1-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/common/AsyncHttpClientHelper.java b/instrumentation/async-http-client/async-http-client-1-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/common/AsyncHttpClientHelper.java new file mode 100644 index 000000000000..5d6f2a788a38 --- /dev/null +++ b/instrumentation/async-http-client/async-http-client-1-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/common/AsyncHttpClientHelper.java @@ -0,0 +1,46 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.asynchttpclient.common; + +import com.ning.http.client.Request; +import javax.annotation.Nullable; + +public interface AsyncHttpClientHelper { + + /** + * Get the full URL from the request. + * + * @param request the HTTP request + * @return the full URL as a string, or null if it cannot be determined + */ + @Nullable + String getUrlFull(Request request); + + /** + * Get the server address (host) from the request. + * + * @param request the HTTP request + * @return the server address + */ + String getServerAddress(Request request); + + /** + * Get the server port from the request. + * + * @param request the HTTP request + * @return the server port + */ + Integer getServerPort(Request request); + + /** + * Set a header on the request. + * + * @param request the HTTP request + * @param key the header name + * @param value the header value + */ + void setHeader(Request request, String key, String value); +} diff --git a/instrumentation/async-http-client/async-http-client-1.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v1_9/AsyncHttpClientHttpAttributesGetter.java b/instrumentation/async-http-client/async-http-client-1-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/common/AsyncHttpClientHttpAttributesGetter.java similarity index 71% rename from instrumentation/async-http-client/async-http-client-1.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v1_9/AsyncHttpClientHttpAttributesGetter.java rename to instrumentation/async-http-client/async-http-client-1-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/common/AsyncHttpClientHttpAttributesGetter.java index f30f6dc1af27..3caa394a3c85 100644 --- a/instrumentation/async-http-client/async-http-client-1.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v1_9/AsyncHttpClientHttpAttributesGetter.java +++ b/instrumentation/async-http-client/async-http-client-1-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/common/AsyncHttpClientHttpAttributesGetter.java @@ -3,18 +3,25 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.asynchttpclient.v1_9; +package io.opentelemetry.javaagent.instrumentation.asynchttpclient.common; + +import static java.util.Collections.emptyList; import com.ning.http.client.Request; import com.ning.http.client.Response; import io.opentelemetry.instrumentation.api.semconv.http.HttpClientAttributesGetter; -import java.util.Collections; import java.util.List; import javax.annotation.Nullable; final class AsyncHttpClientHttpAttributesGetter implements HttpClientAttributesGetter { + private final AsyncHttpClientHelper helper; + + AsyncHttpClientHttpAttributesGetter(AsyncHttpClientHelper helper) { + this.helper = helper; + } + @Override public String getHttpRequestMethod(Request request) { return request.getMethod(); @@ -22,12 +29,12 @@ public String getHttpRequestMethod(Request request) { @Override public String getUrlFull(Request request) { - return request.getUri().toUrl(); + return helper.getUrlFull(request); } @Override public List getHttpRequestHeader(Request request, String name) { - return request.getHeaders().getOrDefault(name, Collections.emptyList()); + return request.getHeaders().getOrDefault(name, emptyList()); } @Override @@ -38,16 +45,16 @@ public Integer getHttpResponseStatusCode( @Override public List getHttpResponseHeader(Request request, Response response, String name) { - return response.getHeaders().getOrDefault(name, Collections.emptyList()); + return response.getHeaders().getOrDefault(name, emptyList()); } @Override public String getServerAddress(Request request) { - return request.getUri().getHost(); + return helper.getServerAddress(request); } @Override public Integer getServerPort(Request request) { - return request.getUri().getPort(); + return helper.getServerPort(request); } } diff --git a/instrumentation/async-http-client/async-http-client-1-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/common/AsyncHttpClientInstrumenterFactory.java b/instrumentation/async-http-client/async-http-client-1-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/common/AsyncHttpClientInstrumenterFactory.java new file mode 100644 index 000000000000..88572dc1a1ef --- /dev/null +++ b/instrumentation/async-http-client/async-http-client-1-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/common/AsyncHttpClientInstrumenterFactory.java @@ -0,0 +1,26 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.asynchttpclient.common; + +import com.ning.http.client.Request; +import com.ning.http.client.Response; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.javaagent.bootstrap.internal.JavaagentHttpClientInstrumenters; + +public final class AsyncHttpClientInstrumenterFactory { + + public static Instrumenter create( + String instrumentationName, AsyncHttpClientHelper helper) { + AsyncHttpClientHttpAttributesGetter httpAttributesGetter = + new AsyncHttpClientHttpAttributesGetter(helper); + HttpHeaderSetter headerSetter = new HttpHeaderSetter(helper); + + return JavaagentHttpClientInstrumenters.create( + instrumentationName, httpAttributesGetter, headerSetter); + } + + private AsyncHttpClientInstrumenterFactory() {} +} diff --git a/instrumentation/async-http-client/async-http-client-1.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v1_9/HttpHeaderSetter.java b/instrumentation/async-http-client/async-http-client-1-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/common/HttpHeaderSetter.java similarity index 57% rename from instrumentation/async-http-client/async-http-client-1.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v1_9/HttpHeaderSetter.java rename to instrumentation/async-http-client/async-http-client-1-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/common/HttpHeaderSetter.java index 55f7c597338e..6c7098b75336 100644 --- a/instrumentation/async-http-client/async-http-client-1.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v1_9/HttpHeaderSetter.java +++ b/instrumentation/async-http-client/async-http-client-1-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/common/HttpHeaderSetter.java @@ -3,16 +3,21 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.asynchttpclient.v1_9; +package io.opentelemetry.javaagent.instrumentation.asynchttpclient.common; import com.ning.http.client.Request; import io.opentelemetry.context.propagation.TextMapSetter; -enum HttpHeaderSetter implements TextMapSetter { - INSTANCE; +final class HttpHeaderSetter implements TextMapSetter { + + private final AsyncHttpClientHelper helper; + + HttpHeaderSetter(AsyncHttpClientHelper helper) { + this.helper = helper; + } @Override public void set(Request carrier, String key, String value) { - carrier.getHeaders().replaceWith(key, value); + helper.setHeader(carrier, key, value); } } diff --git a/instrumentation/async-http-client/async-http-client-1.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v1_9/ResponseInstrumentation.java b/instrumentation/async-http-client/async-http-client-1-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/common/ResponseInstrumentation.java similarity index 90% rename from instrumentation/async-http-client/async-http-client-1.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v1_9/ResponseInstrumentation.java rename to instrumentation/async-http-client/async-http-client-1-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/common/ResponseInstrumentation.java index ffb2551ba7de..b123c5967fa6 100644 --- a/instrumentation/async-http-client/async-http-client-1.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v1_9/ResponseInstrumentation.java +++ b/instrumentation/async-http-client/async-http-client-1-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/common/ResponseInstrumentation.java @@ -3,11 +3,10 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.asynchttpclient.v1_9; +package io.opentelemetry.javaagent.instrumentation.asynchttpclient.common; 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.common.VirtualFieldHelper.ASYNC_HANDLER_DATA; import static net.bytebuddy.matcher.ElementMatchers.hasSuperClass; import static net.bytebuddy.matcher.ElementMatchers.isPublic; import static net.bytebuddy.matcher.ElementMatchers.named; @@ -58,7 +57,8 @@ public static Scope onEnter( return null; } ASYNC_HANDLER_DATA.set(handler, null); - instrumenter().end(data.getContext(), data.getRequest(), response, null); + + data.end(response, null); return data.getParentContext().makeCurrent(); } @@ -82,7 +82,8 @@ public static Scope onEnter( return null; } ASYNC_HANDLER_DATA.set(handler, null); - instrumenter().end(data.getContext(), data.getRequest(), null, throwable); + + data.end(null, throwable); return data.getParentContext().makeCurrent(); } diff --git a/instrumentation/async-http-client/async-http-client-1-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/common/VirtualFieldHelper.java b/instrumentation/async-http-client/async-http-client-1-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/common/VirtualFieldHelper.java new file mode 100644 index 000000000000..929afb920f9f --- /dev/null +++ b/instrumentation/async-http-client/async-http-client-1-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/common/VirtualFieldHelper.java @@ -0,0 +1,16 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.asynchttpclient.common; + +import com.ning.http.client.AsyncHandler; +import io.opentelemetry.instrumentation.api.util.VirtualField; + +public final class VirtualFieldHelper { + public static final VirtualField, AsyncHandlerData> ASYNC_HANDLER_DATA = + VirtualField.find(AsyncHandler.class, AsyncHandlerData.class); + + private VirtualFieldHelper() {} +} diff --git a/instrumentation/async-http-client/async-http-client-1.8/javaagent/build.gradle.kts b/instrumentation/async-http-client/async-http-client-1.8/javaagent/build.gradle.kts new file mode 100644 index 000000000000..9098c6baadee --- /dev/null +++ b/instrumentation/async-http-client/async-http-client-1.8/javaagent/build.gradle.kts @@ -0,0 +1,54 @@ +plugins { + id("otel.javaagent-instrumentation") +} + +muzzle { + pass { + group.set("com.ning") + module.set("async-http-client") + versions.set("[1.8.0,1.9.0)") + assertInverse.set(true) + } +} + +dependencies { + implementation(project(":instrumentation:async-http-client:async-http-client-1-common:javaagent")) + + library("com.ning:async-http-client:1.8.0") + + compileOnly("com.google.auto.value:auto-value-annotations") + annotationProcessor("com.google.auto.value:auto-value") + + testInstrumentation(project(":instrumentation:netty:netty-3.8:javaagent")) + testInstrumentation(project(":instrumentation:async-http-client:async-http-client-1.9:javaagent")) + testInstrumentation(project(":instrumentation:async-http-client:async-http-client-2.0:javaagent")) + + latestDepTestLibrary("com.ning:async-http-client:1.8.+") // see async-http-client-1.9 module +} + +tasks { + withType().configureEach { + // required on jdk17 + jvmArgs("--add-exports=java.base/sun.security.util=ALL-UNNAMED") + jvmArgs("-XX:+IgnoreUnrecognizedVMOptions") + + systemProperty("testLatestDeps", findProperty("testLatestDeps") as Boolean) + + systemProperty("collectMetadata", findProperty("collectMetadata")?.toString() ?: "false") + } +} + +// async-http-client 1.8.x does not work with Netty versions newer than this due to referencing an +// internal file. +configurations.configureEach { + if (!name.contains("muzzle")) { + resolutionStrategy { + eachDependency { + // specifying a fixed version for all libraries with io.netty' group + if (requested.group == "io.netty" && requested.name != "netty-bom") { + useVersion("3.9.0.Final") + } + } + } + } +} diff --git a/instrumentation/async-http-client/async-http-client-1.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v1_8/AsyncHttpClient18Helper.java b/instrumentation/async-http-client/async-http-client-1.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v1_8/AsyncHttpClient18Helper.java new file mode 100644 index 000000000000..a2e48e44a6be --- /dev/null +++ b/instrumentation/async-http-client/async-http-client-1.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v1_8/AsyncHttpClient18Helper.java @@ -0,0 +1,43 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.asynchttpclient.v1_8; + +import com.ning.http.client.Request; +import io.opentelemetry.javaagent.instrumentation.asynchttpclient.common.AsyncHttpClientHelper; +import java.net.MalformedURLException; +import javax.annotation.Nullable; + +final class AsyncHttpClient18Helper implements AsyncHttpClientHelper { + + static final AsyncHttpClient18Helper INSTANCE = new AsyncHttpClient18Helper(); + + private AsyncHttpClient18Helper() {} + + @Override + @Nullable + public String getUrlFull(Request request) { + try { + return request.getURI().toURL().toString(); + } catch (MalformedURLException e) { + return null; + } + } + + @Override + public String getServerAddress(Request request) { + return request.getOriginalURI().getHost(); + } + + @Override + public Integer getServerPort(Request request) { + return request.getOriginalURI().getPort(); + } + + @Override + public void setHeader(Request request, String key, String value) { + request.getHeaders().replace(key, value); + } +} diff --git a/instrumentation/async-http-client/async-http-client-1.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v1_8/AsyncHttpClientInstrumentationModule.java b/instrumentation/async-http-client/async-http-client-1.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v1_8/AsyncHttpClientInstrumentationModule.java new file mode 100644 index 000000000000..48559dd8e652 --- /dev/null +++ b/instrumentation/async-http-client/async-http-client-1.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v1_8/AsyncHttpClientInstrumentationModule.java @@ -0,0 +1,41 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.asynchttpclient.v1_8; + +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; +import static java.util.Arrays.asList; + +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 io.opentelemetry.javaagent.instrumentation.asynchttpclient.common.ResponseInstrumentation; +import java.util.List; +import net.bytebuddy.matcher.ElementMatcher; + +@AutoService(InstrumentationModule.class) +public class AsyncHttpClientInstrumentationModule extends InstrumentationModule + implements ExperimentalInstrumentationModule { + public AsyncHttpClientInstrumentationModule() { + super("async-http-client", "async-http-client-1.8"); + } + + @Override + public ElementMatcher.Junction classLoaderMatcher() { + // TimeoutsHolder class was added in 1.8.0, not present in 1.7.x + return hasClassesNamed("com.ning.http.client.providers.netty.timeout.TimeoutsHolder"); + } + + @Override + public List typeInstrumentations() { + return asList(new AsyncHttpProviderInstrumentation(), new ResponseInstrumentation()); + } + + @Override + public boolean isIndyReady() { + return true; + } +} diff --git a/instrumentation/async-http-client/async-http-client-1.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v1_8/AsyncHttpClientSingletons.java b/instrumentation/async-http-client/async-http-client-1.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v1_8/AsyncHttpClientSingletons.java new file mode 100644 index 000000000000..9c37e33f40bc --- /dev/null +++ b/instrumentation/async-http-client/async-http-client-1.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v1_8/AsyncHttpClientSingletons.java @@ -0,0 +1,25 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.asynchttpclient.v1_8; + +import com.ning.http.client.Request; +import com.ning.http.client.Response; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.javaagent.instrumentation.asynchttpclient.common.AsyncHttpClientInstrumenterFactory; + +public final class AsyncHttpClientSingletons { + private static final String INSTRUMENTATION_NAME = "io.opentelemetry.async-http-client-1.8"; + + private static final Instrumenter INSTRUMENTER = + AsyncHttpClientInstrumenterFactory.create( + INSTRUMENTATION_NAME, AsyncHttpClient18Helper.INSTANCE); + + public static Instrumenter instrumenter() { + return INSTRUMENTER; + } + + private AsyncHttpClientSingletons() {} +} diff --git a/instrumentation/async-http-client/async-http-client-1.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v1_8/AsyncHttpProviderInstrumentation.java b/instrumentation/async-http-client/async-http-client-1.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v1_8/AsyncHttpProviderInstrumentation.java new file mode 100644 index 000000000000..c4a4f2ed5770 --- /dev/null +++ b/instrumentation/async-http-client/async-http-client-1.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v1_8/AsyncHttpProviderInstrumentation.java @@ -0,0 +1,71 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.asynchttpclient.v1_8; + +import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext; +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface; +import static io.opentelemetry.javaagent.instrumentation.asynchttpclient.common.VirtualFieldHelper.ASYNC_HANDLER_DATA; +import static io.opentelemetry.javaagent.instrumentation.asynchttpclient.v1_8.AsyncHttpClientSingletons.instrumenter; +import static net.bytebuddy.matcher.ElementMatchers.isPublic; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArgument; + +import com.ning.http.client.AsyncHandler; +import com.ning.http.client.Request; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import io.opentelemetry.javaagent.instrumentation.asynchttpclient.common.AsyncHandlerData; +import javax.annotation.Nullable; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +public class AsyncHttpProviderInstrumentation implements TypeInstrumentation { + + @Override + public ElementMatcher typeMatcher() { + return implementsInterface(named("com.ning.http.client.AsyncHttpProvider")); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod( + named("execute") + .and(takesArgument(0, named("com.ning.http.client.Request"))) + .and(takesArgument(1, named("com.ning.http.client.AsyncHandler"))) + .and(isPublic()), + this.getClass().getName() + "$ExecuteAdvice"); + } + + @SuppressWarnings("unused") + public static class ExecuteAdvice { + + @Nullable + @Advice.OnMethodEnter(suppress = Throwable.class) + public static Scope onEnter( + @Advice.Argument(0) Request request, @Advice.Argument(1) AsyncHandler handler) { + + Context parentContext = currentContext(); + if (!instrumenter().shouldStart(parentContext, request)) { + return null; + } + + Context context = instrumenter().start(parentContext, request); + ASYNC_HANDLER_DATA.set( + handler, AsyncHandlerData.create(parentContext, context, request, instrumenter())); + return context.makeCurrent(); + } + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void onExit(@Advice.Enter @Nullable Scope scope) { + if (scope != null) { + scope.close(); + } + } + } +} diff --git a/instrumentation/async-http-client/async-http-client-1.8/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v1_8/AsyncHttpClientTest.java b/instrumentation/async-http-client/async-http-client-1.8/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v1_8/AsyncHttpClientTest.java new file mode 100644 index 000000000000..5675dfc9786d --- /dev/null +++ b/instrumentation/async-http-client/async-http-client-1.8/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v1_8/AsyncHttpClientTest.java @@ -0,0 +1,120 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.asynchttpclient.v1_8; + +import static io.opentelemetry.semconv.NetworkAttributes.NETWORK_PROTOCOL_VERSION; + +import com.ning.http.client.AsyncCompletionHandler; +import com.ning.http.client.AsyncHttpClient; +import com.ning.http.client.AsyncHttpClientConfig; +import com.ning.http.client.Request; +import com.ning.http.client.RequestBuilder; +import com.ning.http.client.Response; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpClientTest; +import io.opentelemetry.instrumentation.testing.junit.http.HttpClientInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.HttpClientResult; +import io.opentelemetry.instrumentation.testing.junit.http.HttpClientTestOptions; +import java.io.IOException; +import java.net.URI; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ExecutionException; +import org.junit.jupiter.api.extension.RegisterExtension; + +class AsyncHttpClientTest extends AbstractHttpClientTest { + + @RegisterExtension + public static final InstrumentationExtension testing = + HttpClientInstrumentationExtension.forAgent(); + + private static final int CONNECTION_TIMEOUT_MS = (int) CONNECTION_TIMEOUT.toMillis(); + private static final int READ_TIMEOUT_MS = (int) READ_TIMEOUT.toMillis(); + + private static final AsyncHttpClient client = buildClient(false); + private static final AsyncHttpClient clientWithReadTimeout = buildClient(true); + + private static AsyncHttpClient buildClient(boolean readTimeout) { + AsyncHttpClientConfig.Builder builder = + new AsyncHttpClientConfig.Builder().setConnectionTimeoutInMs(CONNECTION_TIMEOUT_MS); + if (readTimeout) { + builder.setRequestTimeoutInMs(READ_TIMEOUT_MS); + } + return new AsyncHttpClient(builder.build()); + } + + private static AsyncHttpClient getClient(URI uri) { + if (uri.toString().contains("/read-timeout")) { + return clientWithReadTimeout; + } + return client; + } + + @Override + public Request buildRequest(String method, URI uri, Map headers) { + RequestBuilder requestBuilder = new RequestBuilder(method).setURI(uri); + for (Map.Entry entry : headers.entrySet()) { + requestBuilder.setHeader(entry.getKey(), entry.getValue()); + } + return requestBuilder.build(); + } + + @Override + public int sendRequest(Request request, String method, URI uri, Map headers) + throws ExecutionException, InterruptedException, IOException { + return getClient(uri).executeRequest(request).get().getStatusCode(); + } + + @Override + public void sendRequestWithCallback( + Request request, + String method, + URI uri, + Map headers, + HttpClientResult requestResult) { + try { + getClient(uri) + .executeRequest( + request, + new AsyncCompletionHandler() { + @Override + public Void onCompleted(Response response) { + requestResult.complete(response.getStatusCode()); + return null; + } + + @Override + public void onThrowable(Throwable throwable) { + requestResult.complete(throwable); + } + }); + } catch (IOException e) { + requestResult.complete(e); + } + } + + @Override + protected void configure(HttpClientTestOptions.Builder optionsBuilder) { + optionsBuilder.spanEndsAfterBody(); + optionsBuilder.disableTestRedirects(); + optionsBuilder.disableTestHttps(); + + // disable read timeout test for non latest because it is flaky with 1.8.x + if (!Boolean.getBoolean("testLatestDeps")) { + optionsBuilder.disableTestReadTimeout(); + } + + optionsBuilder.setHttpAttributes( + endpoint -> { + Set> attributes = + new HashSet<>(HttpClientTestOptions.DEFAULT_HTTP_ATTRIBUTES); + attributes.remove(NETWORK_PROTOCOL_VERSION); + return attributes; + }); + } +} diff --git a/instrumentation/async-http-client/async-http-client-1.8/metadata.yaml b/instrumentation/async-http-client/async-http-client-1.8/metadata.yaml new file mode 100644 index 000000000000..023f7bb6185b --- /dev/null +++ b/instrumentation/async-http-client/async-http-client-1.8/metadata.yaml @@ -0,0 +1,2 @@ +description: This instrumentation enables HTTP client spans and HTTP client metrics for version 1.8 of the AsyncHttpClient (AHC) HTTP client. +library_link: https://github.com/AsyncHttpClient/async-http-client diff --git a/instrumentation/async-http-client/async-http-client-1.9/javaagent/build.gradle.kts b/instrumentation/async-http-client/async-http-client-1.9/javaagent/build.gradle.kts index 4f6dd4fc20c8..7aa386e7829a 100644 --- a/instrumentation/async-http-client/async-http-client-1.9/javaagent/build.gradle.kts +++ b/instrumentation/async-http-client/async-http-client-1.9/javaagent/build.gradle.kts @@ -12,12 +12,15 @@ muzzle { } dependencies { + implementation(project(":instrumentation:async-http-client:async-http-client-1-common:javaagent")) + library("com.ning:async-http-client:1.9.0") compileOnly("com.google.auto.value:auto-value-annotations") annotationProcessor("com.google.auto.value:auto-value") testInstrumentation(project(":instrumentation:netty:netty-3.8:javaagent")) + testInstrumentation(project(":instrumentation:async-http-client:async-http-client-1.8:javaagent")) testInstrumentation(project(":instrumentation:async-http-client:async-http-client-2.0:javaagent")) } diff --git a/instrumentation/async-http-client/async-http-client-1.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v1_9/AsyncHandlerData.java b/instrumentation/async-http-client/async-http-client-1.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v1_9/AsyncHandlerData.java deleted file mode 100644 index c9bf1449281b..000000000000 --- a/instrumentation/async-http-client/async-http-client-1.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v1_9/AsyncHandlerData.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.instrumentation.asynchttpclient.v1_9; - -import com.google.auto.value.AutoValue; -import com.ning.http.client.Request; -import io.opentelemetry.context.Context; - -@AutoValue -public abstract class AsyncHandlerData { - - public static AsyncHandlerData create(Context parentContext, Context context, Request request) { - return new AutoValue_AsyncHandlerData(parentContext, context, request); - } - - public abstract Context getParentContext(); - - public abstract Context getContext(); - - public abstract Request getRequest(); -} diff --git a/instrumentation/async-http-client/async-http-client-1.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v1_9/AsyncHttpClient19Helper.java b/instrumentation/async-http-client/async-http-client-1.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v1_9/AsyncHttpClient19Helper.java new file mode 100644 index 000000000000..38857354f606 --- /dev/null +++ b/instrumentation/async-http-client/async-http-client-1.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v1_9/AsyncHttpClient19Helper.java @@ -0,0 +1,36 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.asynchttpclient.v1_9; + +import com.ning.http.client.Request; +import io.opentelemetry.javaagent.instrumentation.asynchttpclient.common.AsyncHttpClientHelper; + +final class AsyncHttpClient19Helper implements AsyncHttpClientHelper { + + static final AsyncHttpClient19Helper INSTANCE = new AsyncHttpClient19Helper(); + + private AsyncHttpClient19Helper() {} + + @Override + public String getUrlFull(Request request) { + return request.getUri().toUrl(); + } + + @Override + public String getServerAddress(Request request) { + return request.getUri().getHost(); + } + + @Override + public Integer getServerPort(Request request) { + return request.getUri().getPort(); + } + + @Override + public void setHeader(Request request, String key, String value) { + request.getHeaders().replaceWith(key, value); + } +} diff --git a/instrumentation/async-http-client/async-http-client-1.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v1_9/AsyncHttpClientInstrumentationModule.java b/instrumentation/async-http-client/async-http-client-1.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v1_9/AsyncHttpClientInstrumentationModule.java index a5c64bf4aa89..2120d9e6ebde 100644 --- a/instrumentation/async-http-client/async-http-client-1.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v1_9/AsyncHttpClientInstrumentationModule.java +++ b/instrumentation/async-http-client/async-http-client-1.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v1_9/AsyncHttpClientInstrumentationModule.java @@ -5,13 +5,16 @@ package io.opentelemetry.javaagent.instrumentation.asynchttpclient.v1_9; +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; import static java.util.Arrays.asList; 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 io.opentelemetry.javaagent.instrumentation.asynchttpclient.common.ResponseInstrumentation; import java.util.List; +import net.bytebuddy.matcher.ElementMatcher; @AutoService(InstrumentationModule.class) public class AsyncHttpClientInstrumentationModule extends InstrumentationModule @@ -20,6 +23,12 @@ public AsyncHttpClientInstrumentationModule() { super("async-http-client", "async-http-client-1.9"); } + @Override + public ElementMatcher.Junction classLoaderMatcher() { + // Uri class was added in 1.9.0, not present in 1.8.x + return hasClassesNamed("com.ning.http.client.uri.Uri"); + } + @Override public List typeInstrumentations() { return asList(new RequestInstrumentation(), new ResponseInstrumentation()); diff --git a/instrumentation/async-http-client/async-http-client-1.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v1_9/AsyncHttpClientSingletons.java b/instrumentation/async-http-client/async-http-client-1.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v1_9/AsyncHttpClientSingletons.java index 59f052b89d32..ec99a27ff28d 100644 --- a/instrumentation/async-http-client/async-http-client-1.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v1_9/AsyncHttpClientSingletons.java +++ b/instrumentation/async-http-client/async-http-client-1.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v1_9/AsyncHttpClientSingletons.java @@ -5,29 +5,17 @@ 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.Response; 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.instrumentation.asynchttpclient.common.AsyncHttpClientInstrumenterFactory; public final class AsyncHttpClientSingletons { private static final String INSTRUMENTATION_NAME = "io.opentelemetry.async-http-client-1.9"; - private static final Instrumenter INSTRUMENTER; - - public static final VirtualField, AsyncHandlerData> ASYNC_HANDLER_DATA; - - static { - INSTRUMENTER = - JavaagentHttpClientInstrumenters.create( - INSTRUMENTATION_NAME, - new AsyncHttpClientHttpAttributesGetter(), - HttpHeaderSetter.INSTANCE); - - ASYNC_HANDLER_DATA = VirtualField.find(AsyncHandler.class, AsyncHandlerData.class); - } + private static final Instrumenter INSTRUMENTER = + AsyncHttpClientInstrumenterFactory.create( + INSTRUMENTATION_NAME, AsyncHttpClient19Helper.INSTANCE); public static Instrumenter instrumenter() { return INSTRUMENTER; diff --git a/instrumentation/async-http-client/async-http-client-1.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v1_9/RequestInstrumentation.java b/instrumentation/async-http-client/async-http-client-1.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v1_9/RequestInstrumentation.java index 7b1fdadf5266..c5a8130b3343 100644 --- a/instrumentation/async-http-client/async-http-client-1.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v1_9/RequestInstrumentation.java +++ b/instrumentation/async-http-client/async-http-client-1.9/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/asynchttpclient/v1_9/RequestInstrumentation.java @@ -6,7 +6,7 @@ package io.opentelemetry.javaagent.instrumentation.asynchttpclient.v1_9; 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.common.VirtualFieldHelper.ASYNC_HANDLER_DATA; import static io.opentelemetry.javaagent.instrumentation.asynchttpclient.v1_9.AsyncHttpClientSingletons.instrumenter; import static net.bytebuddy.matcher.ElementMatchers.isPublic; import static net.bytebuddy.matcher.ElementMatchers.named; @@ -18,6 +18,7 @@ import io.opentelemetry.context.Scope; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import io.opentelemetry.javaagent.instrumentation.asynchttpclient.common.AsyncHandlerData; import javax.annotation.Nullable; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; @@ -53,7 +54,8 @@ public static Scope onEnter( } Context context = instrumenter().start(parentContext, request); - ASYNC_HANDLER_DATA.set(handler, AsyncHandlerData.create(parentContext, context, request)); + ASYNC_HANDLER_DATA.set( + handler, AsyncHandlerData.create(parentContext, context, request, instrumenter())); return context.makeCurrent(); } diff --git a/instrumentation/async-http-client/async-http-client-1.9/metadata.yaml b/instrumentation/async-http-client/async-http-client-1.9/metadata.yaml index 6fbd5e9706db..636bfd91269b 100644 --- a/instrumentation/async-http-client/async-http-client-1.9/metadata.yaml +++ b/instrumentation/async-http-client/async-http-client-1.9/metadata.yaml @@ -1,4 +1,4 @@ -description: This instrumentation enables HTTP client spans and HTTP client metrics for version 1 of the AsyncHttpClient (AHC) HTTP client. +description: This instrumentation enables HTTP client spans and HTTP client metrics for version 1.9 of the AsyncHttpClient (AHC) HTTP client. library_link: https://github.com/AsyncHttpClient/async-http-client semantic_conventions: - HTTP_CLIENT_SPANS diff --git a/instrumentation/async-http-client/async-http-client-2.0/javaagent/build.gradle.kts b/instrumentation/async-http-client/async-http-client-2.0/javaagent/build.gradle.kts index 79f139d21432..7bb4e4c415ea 100644 --- a/instrumentation/async-http-client/async-http-client-2.0/javaagent/build.gradle.kts +++ b/instrumentation/async-http-client/async-http-client-2.0/javaagent/build.gradle.kts @@ -16,6 +16,7 @@ dependencies { testInstrumentation(project(":instrumentation:netty:netty-4.0:javaagent")) testInstrumentation(project(":instrumentation:netty:netty-4.1:javaagent")) + testInstrumentation(project(":instrumentation:async-http-client:async-http-client-1.8:javaagent")) testInstrumentation(project(":instrumentation:async-http-client:async-http-client-1.9:javaagent")) } diff --git a/settings.gradle.kts b/settings.gradle.kts index 19e88b3d412e..d8a01b2abeea 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -189,7 +189,9 @@ include(":instrumentation:armeria:armeria-1.3:javaagent") include(":instrumentation:armeria:armeria-1.3:library") include(":instrumentation:armeria:armeria-1.3:testing") include(":instrumentation:armeria:armeria-grpc-1.14:javaagent") +include(":instrumentation:async-http-client:async-http-client-1.8:javaagent") include(":instrumentation:async-http-client:async-http-client-1.9:javaagent") +include(":instrumentation:async-http-client:async-http-client-1-common:javaagent") include(":instrumentation:async-http-client:async-http-client-2.0:javaagent") include(":instrumentation:avaje-jex-3.0:javaagent") include(":instrumentation:aws-lambda:aws-lambda-core-1.0:javaagent")