Update apache-httpclient-5.0 to Instrumenter API
MODIFY
instrumentation/apache-httpclient/apache-httpclient-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v4_0/ApacheHttpClientInstrumentationModule.java
instrumentation/apache-httpclient/apache-httpclient-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v4_0/ApacheHttpClientInstrumentationModule.java
@@ -190,7 +190,7 @@ public class ApacheHttpClientInstrumentationModule extends InstrumentationModule
// Wrap the handler so we capture the status code
if (handler != null) {
handler =
- new WrappingStatusSettingResponseHandler<>(context, parentContext, handler, request);
+ new WrappingStatusSettingResponseHandler<>(context, parentContext, request, handler);
}
}
@@ -215,18 +215,18 @@ public class ApacheHttpClientInstrumentationModule extends InstrumentationModule
public static void methodEnter(
@Advice.Argument(0) HttpHost host,
@Advice.Argument(1) HttpRequest request,
- @Advice.Local("otelHttpUriRequest") HttpUriRequest httpUriRequest,
+ @Advice.Local("fullRequest") HttpUriRequest fullRequest,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
Context parentContext = currentContext();
- httpUriRequest = new HostAndRequestAsHttpUriRequest(host, request);
+ fullRequest = new HostAndRequestAsHttpUriRequest(host, request);
- if (!instrumenter().shouldStart(parentContext, httpUriRequest)) {
+ if (!instrumenter().shouldStart(parentContext, fullRequest)) {
return;
}
- context = instrumenter().start(parentContext, httpUriRequest);
+ context = instrumenter().start(parentContext, fullRequest);
scope = context.makeCurrent();
}
@@ -234,7 +234,7 @@ public class ApacheHttpClientInstrumentationModule extends InstrumentationModule
public static void methodExit(
@Advice.Return Object result,
@Advice.Thrown Throwable throwable,
- @Advice.Local("otelHttpUriRequest") HttpUriRequest httpUriRequest,
+ @Advice.Local("fullRequest") HttpUriRequest fullRequest,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
if (scope == null) {
@@ -242,7 +242,7 @@ public class ApacheHttpClientInstrumentationModule extends InstrumentationModule
}
scope.close();
- ApacheHttpClientHelper.doMethodExit(context, httpUriRequest, result, throwable);
+ ApacheHttpClientHelper.doMethodExit(context, fullRequest, result, throwable);
}
}
@@ -253,22 +253,22 @@ public class ApacheHttpClientInstrumentationModule extends InstrumentationModule
@Advice.Argument(0) HttpHost host,
@Advice.Argument(1) HttpRequest request,
@Advice.Argument(value = 2, readOnly = false) ResponseHandler<?> handler,
- @Advice.Local("otelHttpUriRequest") HttpUriRequest httpUriRequest,
+ @Advice.Local("fullRequest") HttpUriRequest fullRequest,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
Context parentContext = currentContext();
- if (!instrumenter().shouldStart(parentContext, httpUriRequest)) {
+ if (!instrumenter().shouldStart(parentContext, fullRequest)) {
return;
}
- context = instrumenter().start(parentContext, httpUriRequest);
+ context = instrumenter().start(parentContext, fullRequest);
scope = context.makeCurrent();
// Wrap the handler so we capture the status code
if (handler != null) {
handler =
new WrappingStatusSettingResponseHandler<>(
- context, parentContext, handler, httpUriRequest);
+ context, parentContext, fullRequest, handler);
}
}
@@ -276,7 +276,7 @@ public class ApacheHttpClientInstrumentationModule extends InstrumentationModule
public static void methodExit(
@Advice.Return Object result,
@Advice.Thrown Throwable throwable,
- @Advice.Local("otelHttpUriRequest") HttpUriRequest httpUriRequest,
+ @Advice.Local("fullRequest") HttpUriRequest fullRequest,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
if (scope == null) {
@@ -284,7 +284,7 @@ public class ApacheHttpClientInstrumentationModule extends InstrumentationModule
}
scope.close();
- ApacheHttpClientHelper.doMethodExit(context, httpUriRequest, result, throwable);
+ ApacheHttpClientHelper.doMethodExit(context, fullRequest, result, throwable);
}
}
}

MODIFY
instrumentation/apache-httpclient/apache-httpclient-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v4_0/ApacheHttpClientNetAttributesExtractor.java
instrumentation/apache-httpclient/apache-httpclient-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v4_0/ApacheHttpClientNetAttributesExtractor.java
@@ -11,10 +11,15 @@ import java.net.URI;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpUriRequest;
import org.checkerframework.checker.nullness.qual.Nullable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
final class ApacheHttpClientNetAttributesExtractor
extends NetAttributesExtractor<HttpUriRequest, HttpResponse> {
+ private static final Logger logger =
+ LoggerFactory.getLogger(ApacheHttpClientNetAttributesExtractor.class);
+
@Override
protected String transport(HttpUriRequest request) {
return SemanticAttributes.NetTransportValues.IP_TCP;
@@ -38,6 +43,7 @@ final class ApacheHttpClientNetAttributesExtractor
case "https":
return 443;
default:
+ logger.debug("no default port mapping for scheme: {}", uri.getScheme());
return null;
}
}

MODIFY
instrumentation/apache-httpclient/apache-httpclient-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v4_0/WrappingStatusSettingResponseHandler.java
instrumentation/apache-httpclient/apache-httpclient-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v4_0/WrappingStatusSettingResponseHandler.java
@@ -17,23 +17,20 @@ import org.apache.http.client.methods.HttpUriRequest;
public final class WrappingStatusSettingResponseHandler<T> implements ResponseHandler<T> {
private final Context context;
private final Context parentContext;
+ private final HttpUriRequest request;
private final ResponseHandler<T> handler;
- private final HttpUriRequest httpUriRequest;
public WrappingStatusSettingResponseHandler(
- Context context,
- Context parentContext,
- ResponseHandler<T> handler,
- HttpUriRequest httpUriRequest) {
+ Context context, Context parentContext, HttpUriRequest request, ResponseHandler<T> handler) {
this.context = context;
this.parentContext = parentContext;
+ this.request = request;
this.handler = handler;
- this.httpUriRequest = httpUriRequest;
}
@Override
public T handleResponse(HttpResponse response) throws IOException {
- instrumenter().end(context, httpUriRequest, response, null);
+ instrumenter().end(context, request, response, null);
// ending the span before executing the callback handler (and scoping the callback handler to
// the parent context), even though we are inside of a synchronous http client callback
// underneath HttpClient.execute(..), in order to not attribute other CLIENT span timings that

MODIFY
instrumentation/apache-httpclient/apache-httpclient-4.0/javaagent/src/test/groovy/ApacheHttpClientTest.groovy
instrumentation/apache-httpclient/apache-httpclient-4.0/javaagent/src/test/groovy/ApacheHttpClientTest.groovy
@@ -45,7 +45,6 @@ abstract class ApacheHttpClientTest<T extends HttpRequest> extends HttpClientTes
@Override
List<AttributeKey<?>> extraAttributes() {
[
- SemanticAttributes.HTTP_SCHEME,
SemanticAttributes.HTTP_SCHEME,
SemanticAttributes.HTTP_TARGET,
]

MODIFY
instrumentation/apache-httpclient/apache-httpclient-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v5_0/ApacheHttpClientHelper.java
instrumentation/apache-httpclient/apache-httpclient-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v5_0/ApacheHttpClientHelper.java
package io.opentelemetry.javaagent.instrumentation.apachehttpclient.v5_0;
-import static io.opentelemetry.javaagent.instrumentation.apachehttpclient.v5_0.ApacheHttpClientTracer.tracer;
+import static io.opentelemetry.javaagent.instrumentation.apachehttpclient.v5_0.ApacheHttpClientInstrumenters.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, Object result, Throwable throwable) {
+ public static void doMethodExit(
+ Context context, ClassicHttpRequest request, Object result, Throwable throwable) {
if (throwable != null) {
- tracer().endExceptionally(context, throwable);
+ instrumenter().end(context, request, null, throwable);
} else if (result instanceof HttpResponse) {
- tracer().end(context, (HttpResponse) result);
+ instrumenter().end(context, request, (HttpResponse) result, null);
} else {
// ended in WrappingStatusSettingResponseHandler
}

ADD
instrumentation/apache-httpclient/apache-httpclient-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v5_0/ApacheHttpClientHttpAttributesExtractor.java
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.apachehttpclient.v5_0;
+
+import io.opentelemetry.instrumentation.api.instrumenter.http.HttpAttributesExtractor;
+import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
+import org.apache.hc.core5.http.ClassicHttpRequest;
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.HttpResponse;
+import org.apache.hc.core5.http.ProtocolVersion;
+import org.apache.hc.core5.net.URIAuthority;
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class ApacheHttpClientHttpAttributesExtractor
+ extends HttpAttributesExtractor<ClassicHttpRequest, HttpResponse> {
+
+ private static final Logger logger =
+ LoggerFactory.getLogger(ApacheHttpClientHttpAttributesExtractor.class);
+
+ @Override
+ protected String method(ClassicHttpRequest request) {
+ return request.getMethod();
+ }
+
+ @Override
+ protected String url(ClassicHttpRequest request) {
+ // similar to org.apache.hc.core5.http.message.BasicHttpRequest.getUri()
+ // not calling getUri() to avoid unnecessary conversion
+ StringBuilder url = new StringBuilder();
+ URIAuthority authority = request.getAuthority();
+ if (authority != null) {
+ String scheme = request.getScheme();
+ if (scheme != null) {
+ url.append(scheme);
+ url.append("://");
+ } else {
+ url.append("http://");
+ }
+ url.append(authority.getHostName());
+ int port = authority.getPort();
+ if (port >= 0) {
+ url.append(":");
+ url.append(port);
+ }
+ }
+ String path = request.getPath();
+ if (path != null) {
+ if (url.length() > 0 && !path.startsWith("/")) {
+ url.append("/");
+ }
+ url.append(path);
+ } else {
+ url.append("/");
+ }
+ return url.toString();
+ }
+
+ @Override
+ protected String target(ClassicHttpRequest request) {
+ return request.getRequestUri();
+ }
+
+ @Override
+ @Nullable
+ protected String host(ClassicHttpRequest request) {
+ Header header = request.getFirstHeader("Host");
+ if (header != null) {
+ return header.getValue();
+ }
+ return null;
+ }
+
+ @Override
+ protected String scheme(ClassicHttpRequest request) {
+ String scheme = request.getScheme();
+ return scheme != null ? scheme : "http";
+ }
+
+ @Override
+ @Nullable
+ protected String userAgent(ClassicHttpRequest request) {
+ Header header = request.getFirstHeader("User-Agent");
+ return header != null ? header.getValue() : null;
+ }
+
+ @Override
+ @Nullable
+ protected Long requestContentLength(ClassicHttpRequest request, @Nullable HttpResponse response) {
+ return null;
+ }
+
+ @Override
+ @Nullable
+ protected Long requestContentLengthUncompressed(
+ ClassicHttpRequest request, @Nullable HttpResponse response) {
+ return null;
+ }
+
+ @Override
+ protected Integer statusCode(ClassicHttpRequest request, HttpResponse response) {
+ return response.getCode();
+ }
+
+ @Override
+ @Nullable
+ protected String flavor(ClassicHttpRequest request, @Nullable HttpResponse response) {
+ ProtocolVersion protocolVersion = request.getVersion();
+ if (protocolVersion == null) {
+ return SemanticAttributes.HttpFlavorValues.HTTP_1_1;
+ }
+ String protocol = protocolVersion.getProtocol();
+ if (!protocol.equals("HTTP")) {
+ return null;
+ }
+ int major = protocolVersion.getMajor();
+ int minor = protocolVersion.getMinor();
+ if (major == 1 && minor == 0) {
+ return SemanticAttributes.HttpFlavorValues.HTTP_1_0;
+ }
+ if (major == 1 && minor == 1) {
+ return SemanticAttributes.HttpFlavorValues.HTTP_1_1;
+ }
+ if (major == 2 && minor == 0) {
+ return SemanticAttributes.HttpFlavorValues.HTTP_2_0;
+ }
+ logger.debug("unexpected http protocol version: " + protocolVersion);
+ return null;
+ }
+
+ @Override
+ @Nullable
+ protected Long responseContentLength(ClassicHttpRequest request, HttpResponse response) {
+ return null;
+ }
+
+ @Override
+ @Nullable
+ protected Long responseContentLengthUncompressed(
+ ClassicHttpRequest request, HttpResponse response) {
+ return null;
+ }
+
+ @Override
+ @Nullable
+ protected String serverName(ClassicHttpRequest request, @Nullable HttpResponse response) {
+ return null;
+ }
+
+ @Override
+ @Nullable
+ protected String route(ClassicHttpRequest request) {
+ return null;
+ }
+
+ @Override
+ @Nullable
+ protected String clientIp(ClassicHttpRequest request, @Nullable HttpResponse response) {
+ return null;
+ }
+}

MODIFY
instrumentation/apache-httpclient/apache-httpclient-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v5_0/ApacheHttpClientInstrumentationModule.java
instrumentation/apache-httpclient/apache-httpclient-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v5_0/ApacheHttpClientInstrumentationModule.java
@@ -7,7 +7,7 @@ package io.opentelemetry.javaagent.instrumentation.apachehttpclient.v5_0;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface;
import static io.opentelemetry.javaagent.extension.matcher.ClassLoaderMatcher.hasClassesNamed;
-import static io.opentelemetry.javaagent.instrumentation.apachehttpclient.v5_0.ApacheHttpClientTracer.tracer;
+import static io.opentelemetry.javaagent.instrumentation.apachehttpclient.v5_0.ApacheHttpClientInstrumenters.instrumenter;
import static io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge.currentContext;
import static java.util.Collections.singletonList;
import static net.bytebuddy.matcher.ElementMatchers.isAbstract;
@@ -152,16 +152,17 @@ public class ApacheHttpClientInstrumentationModule extends InstrumentationModule
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
Context parentContext = currentContext();
- if (!tracer().shouldStartSpan(parentContext)) {
+ if (!instrumenter().shouldStart(parentContext, request)) {
return;
}
- context = tracer().startSpan(parentContext, request);
+ context = instrumenter().start(parentContext, request);
scope = context.makeCurrent();
}
@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,
@@ -171,7 +172,7 @@ public class ApacheHttpClientInstrumentationModule extends InstrumentationModule
}
scope.close();
- ApacheHttpClientHelper.doMethodExit(context, result, throwable);
+ ApacheHttpClientHelper.doMethodExit(context, request, result, throwable);
}
}
@@ -184,21 +185,23 @@ public class ApacheHttpClientInstrumentationModule extends InstrumentationModule
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
Context parentContext = currentContext();
- if (!tracer().shouldStartSpan(parentContext)) {
+ if (!instrumenter().shouldStart(parentContext, request)) {
return;
}
- context = tracer().startSpan(parentContext, request);
+ context = instrumenter().start(parentContext, request);
scope = context.makeCurrent();
// Wrap the handler so we capture the status code
if (handler != null) {
- handler = new WrappingStatusSettingResponseHandler(context, parentContext, handler);
+ handler =
+ new WrappingStatusSettingResponseHandler(context, parentContext, request, 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,
@@ -208,7 +211,7 @@ public class ApacheHttpClientInstrumentationModule extends InstrumentationModule
}
scope.close();
- ApacheHttpClientHelper.doMethodExit(context, result, throwable);
+ ApacheHttpClientHelper.doMethodExit(context, request, result, throwable);
}
}
@@ -221,21 +224,23 @@ public class ApacheHttpClientInstrumentationModule extends InstrumentationModule
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
Context parentContext = currentContext();
- if (!tracer().shouldStartSpan(parentContext)) {
+ if (!instrumenter().shouldStart(parentContext, request)) {
return;
}
- context = tracer().startSpan(parentContext, request);
+ context = instrumenter().start(parentContext, request);
scope = context.makeCurrent();
// Wrap the handler so we capture the status code
if (handler != null) {
- handler = new WrappingStatusSettingResponseHandler(context, parentContext, handler);
+ handler =
+ new WrappingStatusSettingResponseHandler(context, parentContext, request, 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,
@@ -245,7 +250,7 @@ public class ApacheHttpClientInstrumentationModule extends InstrumentationModule
}
scope.close();
- ApacheHttpClientHelper.doMethodExit(context, result, throwable);
+ ApacheHttpClientHelper.doMethodExit(context, request, result, throwable);
}
}
@@ -254,14 +259,16 @@ public class ApacheHttpClientInstrumentationModule extends InstrumentationModule
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();
- if (!tracer().shouldStartSpan(parentContext)) {
+ fullRequest = new RequestWithHost(host, request);
+ if (!instrumenter().shouldStart(parentContext, fullRequest)) {
return;
}
- context = tracer().startSpan(parentContext, host, request);
+ context = instrumenter().start(parentContext, fullRequest);
scope = context.makeCurrent();
}
@@ -269,6 +276,7 @@ public class ApacheHttpClientInstrumentationModule extends InstrumentationModule
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) {
@@ -276,7 +284,7 @@ public class ApacheHttpClientInstrumentationModule extends InstrumentationModule
}
scope.close();
- ApacheHttpClientHelper.doMethodExit(context, result, throwable);
+ ApacheHttpClientHelper.doMethodExit(context, fullRequest, result, throwable);
}
}
@@ -287,19 +295,23 @@ public class ApacheHttpClientInstrumentationModule extends InstrumentationModule
@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) {
+
Context parentContext = currentContext();
- if (!tracer().shouldStartSpan(parentContext)) {
+ fullRequest = new RequestWithHost(host, request);
+ if (!instrumenter().shouldStart(parentContext, fullRequest)) {
return;
}
- context = tracer().startSpan(parentContext, host, request);
+ context = instrumenter().start(parentContext, fullRequest);
scope = context.makeCurrent();
// Wrap the handler so we capture the status code
if (handler != null) {
- handler = new WrappingStatusSettingResponseHandler(context, parentContext, handler);
+ handler =
+ new WrappingStatusSettingResponseHandler(context, parentContext, fullRequest, handler);
}
}
@@ -307,6 +319,7 @@ public class ApacheHttpClientInstrumentationModule extends InstrumentationModule
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) {
@@ -314,7 +327,7 @@ public class ApacheHttpClientInstrumentationModule extends InstrumentationModule
}
scope.close();
- ApacheHttpClientHelper.doMethodExit(context, result, throwable);
+ ApacheHttpClientHelper.doMethodExit(context, fullRequest, result, throwable);
}
}
@@ -325,19 +338,23 @@ public class ApacheHttpClientInstrumentationModule extends InstrumentationModule
@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) {
+
Context parentContext = currentContext();
- if (!tracer().shouldStartSpan(parentContext)) {
+ fullRequest = new RequestWithHost(host, request);
+ if (!instrumenter().shouldStart(parentContext, fullRequest)) {
return;
}
- context = tracer().startSpan(parentContext, host, request);
+ context = instrumenter().start(parentContext, fullRequest);
scope = context.makeCurrent();
// Wrap the handler so we capture the status code
if (handler != null) {
- handler = new WrappingStatusSettingResponseHandler(context, parentContext, handler);
+ handler =
+ new WrappingStatusSettingResponseHandler(context, parentContext, fullRequest, handler);
}
}
@@ -345,6 +362,7 @@ public class ApacheHttpClientInstrumentationModule extends InstrumentationModule
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) {
@@ -352,7 +370,7 @@ public class ApacheHttpClientInstrumentationModule extends InstrumentationModule
}
scope.close();
- ApacheHttpClientHelper.doMethodExit(context, result, throwable);
+ ApacheHttpClientHelper.doMethodExit(context, fullRequest, result, throwable);
}
}
}

ADD
instrumentation/apache-httpclient/apache-httpclient-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v5_0/ApacheHttpClientInstrumenters.java
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.apachehttpclient.v5_0;
+
+import io.opentelemetry.api.GlobalOpenTelemetry;
+import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
+import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor;
+import io.opentelemetry.instrumentation.api.instrumenter.SpanStatusExtractor;
+import io.opentelemetry.instrumentation.api.instrumenter.http.HttpAttributesExtractor;
+import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor;
+import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor;
+import org.apache.hc.core5.http.ClassicHttpRequest;
+import org.apache.hc.core5.http.HttpResponse;
+
+public final class ApacheHttpClientInstrumenters {
+ private static final String INSTRUMENTATION_NAME =
+ "io.opentelemetry.javaagent.apache-httpclient-5.0";
+
+ private static final Instrumenter<ClassicHttpRequest, HttpResponse> INSTRUMENTER;
+
+ static {
+ HttpAttributesExtractor<ClassicHttpRequest, HttpResponse> httpAttributesExtractor =
+ new ApacheHttpClientHttpAttributesExtractor();
+ SpanNameExtractor<? super ClassicHttpRequest> spanNameExtractor =
+ HttpSpanNameExtractor.create(httpAttributesExtractor);
+ SpanStatusExtractor<? super ClassicHttpRequest, ? super HttpResponse> spanStatusExtractor =
+ HttpSpanStatusExtractor.create(httpAttributesExtractor);
+
+ INSTRUMENTER =
+ Instrumenter.<ClassicHttpRequest, HttpResponse>newBuilder(
+ GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME, spanNameExtractor)
+ .setSpanStatusExtractor(spanStatusExtractor)
+ .addAttributesExtractor(httpAttributesExtractor)
+ .addAttributesExtractor(new ApacheHttpClientNetAttributesExtractor())
+ .newClientInstrumenter(new HttpHeaderSetter());
+ }
+
+ public static Instrumenter<ClassicHttpRequest, HttpResponse> instrumenter() {
+ return INSTRUMENTER;
+ }
+
+ private ApacheHttpClientInstrumenters() {}
+}

ADD
instrumentation/apache-httpclient/apache-httpclient-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v5_0/ApacheHttpClientNetAttributesExtractor.java
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.apachehttpclient.v5_0;
+
+import io.opentelemetry.instrumentation.api.instrumenter.net.NetAttributesExtractor;
+import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
+import org.apache.hc.core5.http.ClassicHttpRequest;
+import org.apache.hc.core5.http.HttpResponse;
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class ApacheHttpClientNetAttributesExtractor
+ extends NetAttributesExtractor<ClassicHttpRequest, HttpResponse> {
+
+ private static final Logger logger =
+ LoggerFactory.getLogger(ApacheHttpClientNetAttributesExtractor.class);
+
+ @Override
+ protected String transport(ClassicHttpRequest request) {
+ return SemanticAttributes.NetTransportValues.IP_TCP;
+ }
+
+ @Override
+ protected @Nullable String peerName(ClassicHttpRequest request, @Nullable HttpResponse response) {
+ return request.getAuthority().getHostName();
+ }
+
+ @Override
+ protected Integer peerPort(ClassicHttpRequest request, @Nullable HttpResponse response) {
+ int port = request.getAuthority().getPort();
+ if (port != -1) {
+ return port;
+ }
+ String scheme = request.getScheme();
+ if (scheme == null) {
+ return 80;
+ }
+ switch (scheme) {
+ case "http":
+ return 80;
+ case "https":
+ return 443;
+ default:
+ logger.debug("no default port mapping for scheme: {}", scheme);
+ return null;
+ }
+ }
+
+ @Override
+ protected @Nullable String peerIp(ClassicHttpRequest request, @Nullable HttpResponse response) {
+ return null;
+ }
+}

DELETE
instrumentation/apache-httpclient/apache-httpclient-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v5_0/ApacheHttpClientTracer.java
-/*
- * Copyright The OpenTelemetry Authors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package io.opentelemetry.javaagent.instrumentation.apachehttpclient.v5_0;
-
-import io.opentelemetry.context.Context;
-import io.opentelemetry.context.propagation.TextMapSetter;
-import io.opentelemetry.instrumentation.api.tracer.HttpClientTracer;
-import io.opentelemetry.instrumentation.api.tracer.net.NetPeerAttributes;
-import java.net.URI;
-import java.net.URISyntaxException;
-import org.apache.hc.core5.http.ClassicHttpRequest;
-import org.apache.hc.core5.http.Header;
-import org.apache.hc.core5.http.HttpHost;
-import org.apache.hc.core5.http.HttpMessage;
-import org.apache.hc.core5.http.HttpResponse;
-import org.apache.hc.core5.http.HttpVersion;
-import org.apache.hc.core5.http.ProtocolVersion;
-import org.checkerframework.checker.nullness.qual.Nullable;
-
-public class ApacheHttpClientTracer
- extends HttpClientTracer<ClassicHttpRequest, ClassicHttpRequest, HttpResponse> {
-
- private static final ApacheHttpClientTracer TRACER = new ApacheHttpClientTracer();
-
- private ApacheHttpClientTracer() {
- super(NetPeerAttributes.INSTANCE);
- }
-
- public static ApacheHttpClientTracer tracer() {
- return TRACER;
- }
-
- public Context startSpan(Context parentContext, HttpHost host, ClassicHttpRequest request) {
- return startSpan(parentContext, new RequestWithHost(host, request));
- }
-
- public Context startSpan(Context parentContext, ClassicHttpRequest request) {
- return startSpan(parentContext, request, request);
- }
-
- @Override
- protected String method(ClassicHttpRequest httpRequest) {
- return httpRequest.getMethod();
- }
-
- @Override
- @Nullable
- protected String flavor(ClassicHttpRequest request) {
- ProtocolVersion protocolVersion = request.getVersion();
- if (protocolVersion == null) {
- protocolVersion = HttpVersion.HTTP_1_1;
- }
- return protocolVersion.toString();
- }
-
- @Override
- protected URI url(ClassicHttpRequest request) throws URISyntaxException {
- return request.getUri();
- }
-
- @Override
- protected Integer status(HttpResponse httpResponse) {
- return httpResponse.getCode();
- }
-
- @Override
- protected String requestHeader(ClassicHttpRequest request, String name) {
- return header(request, name);
- }
-
- @Override
- protected String responseHeader(HttpResponse response, String name) {
- return header(response, name);
- }
-
- @Override
- protected TextMapSetter<ClassicHttpRequest> getSetter() {
- return HttpHeadersInjectAdapter.SETTER;
- }
-
- private static String header(HttpMessage message, String name) {
- Header header = message.getFirstHeader(name);
- return header != null ? header.getValue() : null;
- }
-
- @Override
- protected String getInstrumentationName() {
- return "io.opentelemetry.javaagent.apache-httpclient-5.0";
- }
-}

RENAME
instrumentation/apache-httpclient/apache-httpclient-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v5_0/HttpHeadersInjectAdapter.java
instrumentation/apache-httpclient/apache-httpclient-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v5_0/HttpHeaderSetter.java
@@ -8,9 +8,7 @@ package io.opentelemetry.javaagent.instrumentation.apachehttpclient.v5_0;
import io.opentelemetry.context.propagation.TextMapSetter;
import org.apache.hc.core5.http.ClassicHttpRequest;
-class HttpHeadersInjectAdapter implements TextMapSetter<ClassicHttpRequest> {
-
- public static final HttpHeadersInjectAdapter SETTER = new HttpHeadersInjectAdapter();
+final class HttpHeaderSetter implements TextMapSetter<ClassicHttpRequest> {
@Override
public void set(ClassicHttpRequest carrier, String key, String value) {

MODIFY
instrumentation/apache-httpclient/apache-httpclient-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v5_0/RequestWithHost.java
instrumentation/apache-httpclient/apache-httpclient-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v5_0/RequestWithHost.java
package io.opentelemetry.javaagent.instrumentation.apachehttpclient.v5_0;
import java.net.URI;
-import java.net.URISyntaxException;
import org.apache.hc.core5.http.ClassicHttpRequest;
import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.message.HttpRequestWrapper;
+import org.apache.hc.core5.net.URIAuthority;
public class RequestWithHost extends HttpRequestWrapper implements ClassicHttpRequest {
- private final ClassicHttpRequest httpRequest;
- private final URI uri;
+ private final String scheme;
+ private final URIAuthority authority;
public RequestWithHost(HttpHost httpHost, ClassicHttpRequest httpRequest) {
super(httpRequest);
- this.httpRequest = httpRequest;
-
- URI calculatedUri;
- try {
- // combine requested uri with host info
- URI requestUri = httpRequest.getUri();
- calculatedUri =
- new URI(
- httpHost.getSchemeName(),
- null,
- httpHost.getHostName(),
- httpHost.getPort(),
- requestUri.getPath(),
- requestUri.getQuery(),
- requestUri.getFragment());
- } catch (URISyntaxException e) {
- calculatedUri = null;
+ this.scheme = httpHost.getSchemeName();
+ this.authority = new URIAuthority(httpHost.getHostName(), httpHost.getPort());
}
- uri = calculatedUri;
+
+ @Override
+ public String getScheme() {
+ return scheme;
}
@Override
- public URI getUri() throws URISyntaxException {
- if (uri != null) {
- return uri;
+ public URIAuthority getAuthority() {
+ return authority;
}
- return super.getUri();
+
+ @Override
+ public URI getUri() {
+ // overriding super because it's not correct (doesn't incorporate authority)
+ // and isn't needed anyways
+ throw new UnsupportedOperationException();
}
@Override
public HttpEntity getEntity() {
- return httpRequest.getEntity();
+ throw new UnsupportedOperationException();
}
@Override
public void setEntity(HttpEntity entity) {
- httpRequest.setEntity(entity);
+ throw new UnsupportedOperationException();
}
}

MODIFY
instrumentation/apache-httpclient/apache-httpclient-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v5_0/WrappingStatusSettingResponseHandler.java
instrumentation/apache-httpclient/apache-httpclient-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v5_0/WrappingStatusSettingResponseHandler.java
package io.opentelemetry.javaagent.instrumentation.apachehttpclient.v5_0;
-import static io.opentelemetry.javaagent.instrumentation.apachehttpclient.v5_0.ApacheHttpClientTracer.tracer;
+import static io.opentelemetry.javaagent.instrumentation.apachehttpclient.v5_0.ApacheHttpClientInstrumenters.instrumenter;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import java.io.IOException;
+import org.apache.hc.core5.http.ClassicHttpRequest;
import org.apache.hc.core5.http.ClassicHttpResponse;
import org.apache.hc.core5.http.HttpException;
import org.apache.hc.core5.http.io.HttpClientResponseHandler;
@@ -17,18 +18,23 @@ import org.apache.hc.core5.http.io.HttpClientResponseHandler;
public class WrappingStatusSettingResponseHandler<T> implements HttpClientResponseHandler<T> {
final Context context;
final Context parentContext;
+ final ClassicHttpRequest request;
final HttpClientResponseHandler<T> handler;
public WrappingStatusSettingResponseHandler(
- Context context, Context parentContext, HttpClientResponseHandler<T> handler) {
+ Context context,
+ Context parentContext,
+ ClassicHttpRequest request,
+ HttpClientResponseHandler<T> handler) {
this.context = context;
this.parentContext = parentContext;
+ this.request = request;
this.handler = handler;
}
@Override
public T handleResponse(ClassicHttpResponse response) throws IOException, HttpException {
- tracer().end(context, response);
+ instrumenter().end(context, request, response, null);
// ending the span before executing the callback handler (and scoping the callback handler to
// the parent context), even though we are inside of a synchronous http client callback
// underneath HttpClient.execute(..), in order to not attribute other CLIENT span timings that

MODIFY
instrumentation/apache-httpclient/apache-httpclient-5.0/javaagent/src/test/groovy/ApacheHttpClientTest.groovy
instrumentation/apache-httpclient/apache-httpclient-5.0/javaagent/src/test/groovy/ApacheHttpClientTest.groovy
* SPDX-License-Identifier: Apache-2.0
*/
+import io.opentelemetry.api.common.AttributeKey
import io.opentelemetry.instrumentation.test.AgentTestTrait
import io.opentelemetry.instrumentation.test.base.HttpClientTest
+import io.opentelemetry.semconv.trace.attributes.SemanticAttributes
import java.util.concurrent.TimeUnit
import java.util.function.Consumer
import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase
@@ -45,6 +47,14 @@ abstract class ApacheHttpClientTest<T extends HttpRequest> extends HttpClientTes
return request
}
+ @Override
+ List<AttributeKey<?>> extraAttributes() {
+ [
+ SemanticAttributes.HTTP_SCHEME,
+ SemanticAttributes.HTTP_TARGET,
+ ]
+ }
+
// compilation fails with @Override annotation on this method (groovy quirk?)
int sendRequest(T request, String method, URI uri, Map<String, String> headers) {
def response = executeRequest(request, uri)

Update apache-httpclient-5.0 to Instrumenter API (#3062)