Update Apache HttpClient 2.0 to Instrumenter API
ADD
instrumentation/apache-httpclient/apache-httpclient-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v2_0/ApacheHttpClientHttpAttributesExtractor.java
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.apachehttpclient.v2_0;
+
+import io.opentelemetry.instrumentation.api.instrumenter.http.HttpAttributesExtractor;
+import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
+import org.apache.commons.httpclient.Header;
+import org.apache.commons.httpclient.HostConfiguration;
+import org.apache.commons.httpclient.HttpMethod;
+import org.apache.commons.httpclient.HttpMethodBase;
+import org.apache.commons.httpclient.StatusLine;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+final class ApacheHttpClientHttpAttributesExtractor
+ extends HttpAttributesExtractor<HttpMethod, Void> {
+
+ @Override
+ protected String method(HttpMethod httpMethod) {
+ return httpMethod.getName();
+ }
+
+ @Override
+ protected String url(HttpMethod httpMethod) {
+ return getUrl(httpMethod);
+ }
+
+ @Override
+ protected String target(HttpMethod httpMethod) {
+ String queryString = httpMethod.getQueryString();
+ return queryString != null ? httpMethod.getPath() + "?" + queryString : httpMethod.getPath();
+ }
+
+ @Override
+ @Nullable
+ protected String host(HttpMethod httpMethod) {
+ Header header = httpMethod.getRequestHeader("Host");
+ if (header != null) {
+ return header.getValue();
+ }
+ HostConfiguration hostConfiguration = httpMethod.getHostConfiguration();
+ if (hostConfiguration != null) {
+ return hostConfiguration.getVirtualHost();
+ }
+ return null;
+ }
+
+ @Override
+ @Nullable
+ protected String scheme(HttpMethod httpMethod) {
+ HostConfiguration hostConfiguration = httpMethod.getHostConfiguration();
+ return hostConfiguration != null ? hostConfiguration.getProtocol().getScheme() : null;
+ }
+
+ @Override
+ @Nullable
+ protected String userAgent(HttpMethod httpMethod) {
+ Header header = httpMethod.getRequestHeader("User-Agent");
+ return header != null ? header.getValue() : null;
+ }
+
+ @Override
+ @Nullable
+ protected Long requestContentLength(HttpMethod httpMethod, Void unused) {
+ return null;
+ }
+
+ @Override
+ @Nullable
+ protected Long requestContentLengthUncompressed(HttpMethod httpMethod, Void unused) {
+ return null;
+ }
+
+ @Override
+ @Nullable
+ protected Long statusCode(HttpMethod httpMethod, Void unused) {
+ StatusLine statusLine = httpMethod.getStatusLine();
+ return statusLine == null ? null : (long) statusLine.getStatusCode();
+ }
+
+ @Override
+ @Nullable
+ protected String flavor(HttpMethod httpMethod, Void unused) {
+ if (httpMethod instanceof HttpMethodBase) {
+ return ((HttpMethodBase) httpMethod).isHttp11()
+ ? SemanticAttributes.HttpFlavorValues.HTTP_1_1
+ : SemanticAttributes.HttpFlavorValues.HTTP_1_0;
+ }
+ return null;
+ }
+
+ @Override
+ @Nullable
+ protected Long responseContentLength(HttpMethod httpMethod, Void unused) {
+ return null;
+ }
+
+ @Override
+ @Nullable
+ protected Long responseContentLengthUncompressed(HttpMethod httpMethod, Void unused) {
+ return null;
+ }
+
+ @Override
+ @Nullable
+ protected String serverName(HttpMethod httpMethod, Void unused) {
+ return null;
+ }
+
+ @Override
+ @Nullable
+ protected String route(HttpMethod httpMethod) {
+ return null;
+ }
+
+ @Override
+ @Nullable
+ protected String clientIp(HttpMethod httpMethod, Void unused) {
+ return null;
+ }
+
+ // mirroring implementation HttpMethodBase.getURI(), to avoid converting to URI and back to String
+ private static String getUrl(HttpMethod httpMethod) {
+ HostConfiguration hostConfiguration = httpMethod.getHostConfiguration();
+ if (hostConfiguration == null) {
+ String queryString = httpMethod.getQueryString();
+ if (queryString == null) {
+ return httpMethod.getPath();
+ } else {
+ return httpMethod.getPath() + "?" + httpMethod.getQueryString();
+ }
+ } else {
+ StringBuilder url = new StringBuilder();
+ url.append(hostConfiguration.getProtocol().getScheme());
+ url.append("://");
+ url.append(hostConfiguration.getHost());
+ int port = hostConfiguration.getPort();
+ if (port != hostConfiguration.getProtocol().getDefaultPort()) {
+ url.append(":");
+ url.append(port);
+ }
+ url.append(httpMethod.getPath());
+ String queryString = httpMethod.getQueryString();
+ if (queryString != null) {
+ url.append("?");
+ url.append(httpMethod.getQueryString());
+ }
+ return url.toString();
+ }
+ }
+}

MODIFY
instrumentation/apache-httpclient/apache-httpclient-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v2_0/ApacheHttpClientInstrumentationModule.java
instrumentation/apache-httpclient/apache-httpclient-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v2_0/ApacheHttpClientInstrumentationModule.java
@@ -7,7 +7,7 @@ package io.opentelemetry.javaagent.instrumentation.apachehttpclient.v2_0;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.extendsClass;
import static io.opentelemetry.javaagent.extension.matcher.ClassLoaderMatcher.hasClassesNamed;
-import static io.opentelemetry.javaagent.instrumentation.apachehttpclient.v2_0.CommonsHttpClientTracer.tracer;
+import static io.opentelemetry.javaagent.instrumentation.apachehttpclient.v2_0.ApacheHttpClientInstrumenters.instrumenter;
import static io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge.currentContext;
import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap;
@@ -70,11 +70,11 @@ 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, httpMethod)) {
return;
}
- context = tracer().startSpan(parentContext, httpMethod);
+ context = instrumenter().start(parentContext, httpMethod);
scope = context.makeCurrent();
}
@@ -89,7 +89,7 @@ public class ApacheHttpClientInstrumentationModule extends InstrumentationModule
}
scope.close();
- tracer().endMaybeExceptionally(context, httpMethod, throwable);
+ instrumenter().end(context, httpMethod, null, throwable);
}
}
}

ADD
instrumentation/apache-httpclient/apache-httpclient-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v2_0/ApacheHttpClientInstrumenters.java
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.apachehttpclient.v2_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.commons.httpclient.HttpMethod;
+
+public final class ApacheHttpClientInstrumenters {
+ private static final String INSTRUMENTATION_NAME =
+ "io.opentelemetry.javaagent.apache-httpclient-2.0";
+
+ private static final Instrumenter<HttpMethod, Void> INSTRUMENTER;
+
+ static {
+ HttpAttributesExtractor<HttpMethod, Void> httpAttributesExtractor =
+ new ApacheHttpClientHttpAttributesExtractor();
+ SpanNameExtractor<? super HttpMethod> spanNameExtractor =
+ HttpSpanNameExtractor.create(httpAttributesExtractor);
+ SpanStatusExtractor<? super HttpMethod, ? super Void> spanStatusExtractor =
+ HttpSpanStatusExtractor.create(httpAttributesExtractor);
+
+ INSTRUMENTER =
+ Instrumenter.<HttpMethod, Void>newBuilder(
+ GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME, spanNameExtractor)
+ .setSpanStatusExtractor(spanStatusExtractor)
+ .addAttributesExtractor(httpAttributesExtractor)
+ .addAttributesExtractor(new ApacheHttpClientNetAttributesExtractor())
+ .newClientInstrumenter(HttpHeaderSetter.INSTANCE);
+ }
+
+ public static Instrumenter<HttpMethod, Void> instrumenter() {
+ return INSTRUMENTER;
+ }
+
+ private ApacheHttpClientInstrumenters() {}
+}

ADD
instrumentation/apache-httpclient/apache-httpclient-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v2_0/ApacheHttpClientNetAttributesExtractor.java
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.apachehttpclient.v2_0;
+
+import io.opentelemetry.instrumentation.api.instrumenter.net.NetAttributesExtractor;
+import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
+import org.apache.commons.httpclient.HostConfiguration;
+import org.apache.commons.httpclient.HttpMethod;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+final class ApacheHttpClientNetAttributesExtractor
+ extends NetAttributesExtractor<HttpMethod, Void> {
+
+ @Override
+ protected String transport(HttpMethod httpMethod) {
+ return SemanticAttributes.NetTransportValues.IP_TCP;
+ }
+
+ @Override
+ protected @Nullable String peerName(HttpMethod httpMethod, @Nullable Void unused) {
+ HostConfiguration hostConfiguration = httpMethod.getHostConfiguration();
+ return hostConfiguration != null ? hostConfiguration.getHost() : null;
+ }
+
+ @Override
+ protected @Nullable Long peerPort(HttpMethod httpMethod, @Nullable Void unused) {
+ HostConfiguration hostConfiguration = httpMethod.getHostConfiguration();
+ return hostConfiguration != null ? (long) hostConfiguration.getPort() : null;
+ }
+
+ @Override
+ protected @Nullable String peerIp(HttpMethod httpMethod, @Nullable Void unused) {
+ return null;
+ }
+}

DELETE
instrumentation/apache-httpclient/apache-httpclient-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v2_0/CommonsHttpClientTracer.java
-/*
- * Copyright The OpenTelemetry Authors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package io.opentelemetry.javaagent.instrumentation.apachehttpclient.v2_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.commons.httpclient.Header;
-import org.apache.commons.httpclient.HttpMethod;
-import org.apache.commons.httpclient.StatusLine;
-import org.apache.commons.httpclient.URIException;
-
-public class CommonsHttpClientTracer extends HttpClientTracer<HttpMethod, HttpMethod, HttpMethod> {
- private static final CommonsHttpClientTracer TRACER = new CommonsHttpClientTracer();
-
- private CommonsHttpClientTracer() {
- super(NetPeerAttributes.INSTANCE);
- }
-
- public static CommonsHttpClientTracer tracer() {
- return TRACER;
- }
-
- public Context startSpan(Context parentContext, HttpMethod request) {
- return super.startSpan(parentContext, request, request);
- }
-
- @Override
- protected String getInstrumentationName() {
- return "io.opentelemetry.javaagent.apache-httpclient-2.0";
- }
-
- @Override
- protected String method(HttpMethod httpMethod) {
- return httpMethod.getName();
- }
-
- @Override
- protected URI url(HttpMethod httpMethod) throws URISyntaxException {
- try {
- // org.apache.commons.httpclient.URI -> java.net.URI
- return new URI(httpMethod.getURI().toString());
- } catch (URIException e) {
- throw new URISyntaxException("", e.getMessage());
- }
- }
-
- @Override
- protected Integer status(HttpMethod httpMethod) {
- StatusLine statusLine = httpMethod.getStatusLine();
- return statusLine == null ? null : statusLine.getStatusCode();
- }
-
- @Override
- protected String requestHeader(HttpMethod httpMethod, String name) {
- Header header = httpMethod.getRequestHeader(name);
- return header != null ? header.getValue() : null;
- }
-
- @Override
- protected String responseHeader(HttpMethod httpMethod, String name) {
- Header header = httpMethod.getResponseHeader(name);
- return header != null ? header.getValue() : null;
- }
-
- @Override
- protected TextMapSetter<HttpMethod> getSetter() {
- return HttpHeadersInjectAdapter.SETTER;
- }
-}

RENAME
instrumentation/apache-httpclient/apache-httpclient-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v2_0/HttpHeadersInjectAdapter.java
instrumentation/apache-httpclient/apache-httpclient-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v2_0/HttpHeaderSetter.java
@@ -9,9 +9,8 @@ import io.opentelemetry.context.propagation.TextMapSetter;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpMethod;
-public class HttpHeadersInjectAdapter implements TextMapSetter<HttpMethod> {
-
- public static final HttpHeadersInjectAdapter SETTER = new HttpHeadersInjectAdapter();
+enum HttpHeaderSetter implements TextMapSetter<HttpMethod> {
+ INSTANCE;
@Override
public void set(HttpMethod carrier, String key, String value) {

MODIFY
instrumentation/apache-httpclient/apache-httpclient-2.0/javaagent/src/test/groovy/CommonsHttpClientTest.groovy
instrumentation/apache-httpclient/apache-httpclient-2.0/javaagent/src/test/groovy/CommonsHttpClientTest.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 org.apache.commons.httpclient.HttpClient
import org.apache.commons.httpclient.HttpMethod
import org.apache.commons.httpclient.methods.DeleteMethod
@@ -88,4 +90,12 @@ class CommonsHttpClientTest extends HttpClientTest<HttpMethod> implements AgentT
boolean testCallback() {
false
}
+
+ @Override
+ List<AttributeKey<?>> extraAttributes() {
+ [
+ SemanticAttributes.HTTP_SCHEME,
+ SemanticAttributes.HTTP_TARGET,
+ ]
+ }
}

MODIFY
instrumentation/jms-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jms/JmsInstrumenters.java
instrumentation/jms-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jms/JmsInstrumenters.java
@@ -30,7 +30,7 @@ public final class JmsInstrumenters {
Instrumenter.<MessageWithDestination, Void>newBuilder(
otel, INSTRUMENTATION_NAME, spanNameExtractor)
.addAttributesExtractor(attributesExtractor)
- .newProducerInstrumenter(new MessagePropertySetter());
+ .newProducerInstrumenter(MessagePropertySetter.INSTANCE);
// MessageConsumer does not do context propagation
CONSUMER_INSTRUMENTER =
Instrumenter.<MessageWithDestination, Void>newBuilder(

MODIFY
instrumentation/jms-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jms/MessagePropertySetter.java
instrumentation/jms-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jms/MessagePropertySetter.java
@@ -10,7 +10,9 @@ import javax.jms.JMSException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public final class MessagePropertySetter implements TextMapSetter<MessageWithDestination> {
+enum MessagePropertySetter implements TextMapSetter<MessageWithDestination> {
+ INSTANCE;
+
private static final Logger log = LoggerFactory.getLogger(MessagePropertySetter.class);
static final String DASH = "__dash__";

MODIFY
testing-common/src/main/groovy/io/opentelemetry/instrumentation/test/base/HttpClientTest.groovy
testing-common/src/main/groovy/io/opentelemetry/instrumentation/test/base/HttpClientTest.groovy
@@ -843,7 +843,7 @@ abstract class HttpClientTest<REQUEST> extends InstrumentationSpecification {
// filling in peer information but some instrumentation does so based on the URL itself
// which is present in HTTP attributes. We should fix this.
"${SemanticAttributes.NET_PEER_NAME.key}" { it == null || it == uri.host }
- "${SemanticAttributes.NET_PEER_PORT.key}" { it == null || it == uri.port }
+ "${SemanticAttributes.NET_PEER_PORT.key}" { it == null || it == uri.port || (uri.scheme == "https" && it == 443) }
} else {
"${SemanticAttributes.NET_PEER_NAME.key}" uri.host
"${SemanticAttributes.NET_PEER_PORT.key}" uri.port > 0 ? uri.port : { it == null || it == 443 }
@@ -852,7 +852,7 @@ abstract class HttpClientTest<REQUEST> extends InstrumentationSpecification {
// unpredictable IP address (or can be none if no connection is made, see comment above)
"${SemanticAttributes.NET_PEER_IP.key}" { it == null || it instanceof String }
} else {
- "${SemanticAttributes.NET_PEER_IP.key}" { it == null || it == "127.0.0.1" } // Optional
+ "${SemanticAttributes.NET_PEER_IP.key}" { it == null || it == "127.0.0.1" || it == uri.host } // Optional
}
"${SemanticAttributes.HTTP_URL.key}" { it == "${uri}" || it == "${removeFragment(uri)}" }
"${SemanticAttributes.HTTP_METHOD.key}" method

Update Apache HttpClient 2.0 to Instrumenter API (#3012)