Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add library instrumentation for Apache HTTPClient 4.3 #3623

Merged
merged 10 commits into from
Jul 28, 2021
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
plugins {
id("otel.library-instrumentation")
id("otel.nullaway-conventions")
}

dependencies {
library("org.apache.httpcomponents:httpclient:4.3")

implementation("org.slf4j:slf4j-api")

testImplementation(project(":instrumentation:apache-httpclient:apache-httpclient-4.3:testing"))

latestDepTestLibrary("org.apache.httpcomponents:httpclient:4.+")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.apachehttpclient.v4_3;

import io.opentelemetry.instrumentation.api.instrumenter.http.HttpAttributesExtractor;
import org.apache.http.HttpResponse;
import org.checkerframework.checker.nullness.qual.Nullable;

final class ApacheHttpClientHttpAttributesExtractor
extends HttpAttributesExtractor<ApacheHttpClientRequest, HttpResponse> {

@Override
protected String method(ApacheHttpClientRequest request) {
return request.getMethod();
}

@Override
@Nullable
protected String url(ApacheHttpClientRequest request) {
return request.getUrl();
}

@Override
@Nullable
protected String target(ApacheHttpClientRequest request) {
return request.getTarget();
}

@Override
@Nullable
protected String host(ApacheHttpClientRequest request) {
return request.getHeader("Host");
}

@Override
@Nullable
protected String scheme(ApacheHttpClientRequest request) {
return request.getScheme();
}

@Override
@Nullable
protected String userAgent(ApacheHttpClientRequest request) {
return request.getHeader("User-Agent");
}

@Override
@Nullable
protected Long requestContentLength(
ApacheHttpClientRequest request, @Nullable HttpResponse response) {
return null;
}

@Override
@Nullable
protected Long requestContentLengthUncompressed(
ApacheHttpClientRequest request, @Nullable HttpResponse response) {
return null;
}

@Override
protected Integer statusCode(ApacheHttpClientRequest request, HttpResponse response) {
return response.getStatusLine().getStatusCode();
}

@Override
@Nullable
protected String flavor(ApacheHttpClientRequest request, @Nullable HttpResponse response) {
return request.getFlavor();
}

@Override
@Nullable
protected Long responseContentLength(ApacheHttpClientRequest request, HttpResponse response) {
return null;
}

@Override
@Nullable
protected Long responseContentLengthUncompressed(
ApacheHttpClientRequest request, HttpResponse response) {
return null;
}

@Override
@Nullable
protected String serverName(ApacheHttpClientRequest request, @Nullable HttpResponse response) {
return null;
}

@Override
@Nullable
protected String route(ApacheHttpClientRequest request) {
return null;
}

@Override
@Nullable
protected String clientIp(ApacheHttpClientRequest request, @Nullable HttpResponse response) {
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.apachehttpclient.v4_3;

import io.opentelemetry.instrumentation.api.instrumenter.net.NetAttributesExtractor;
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
import org.apache.http.HttpResponse;
import org.checkerframework.checker.nullness.qual.Nullable;

final class ApacheHttpClientNetAttributesExtractor
extends NetAttributesExtractor<ApacheHttpClientRequest, HttpResponse> {

@Override
public String transport(ApacheHttpClientRequest request) {
return SemanticAttributes.NetTransportValues.IP_TCP;
}

@Override
@Nullable
public String peerName(ApacheHttpClientRequest request, @Nullable HttpResponse response) {
return request.getPeerName();
}

@Override
@Nullable
public Integer peerPort(ApacheHttpClientRequest request, @Nullable HttpResponse response) {
return request.getPeerPort();
}

@Override
@Nullable
public String peerIp(ApacheHttpClientRequest request, @Nullable HttpResponse response) {
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.apachehttpclient.v4_3;

import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
import java.net.URI;
import java.net.URISyntaxException;
import org.apache.http.Header;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.ProtocolVersion;
import org.apache.http.client.methods.HttpUriRequest;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public final class ApacheHttpClientRequest {

private static final Logger logger = LoggerFactory.getLogger(ApacheHttpClientRequest.class);

@Nullable private final URI uri;

private final HttpRequest delegate;

ApacheHttpClientRequest(@Nullable HttpHost httpHost, HttpRequest httpRequest) {
URI calculatedUri = null;
if (httpRequest instanceof HttpUriRequest) {
calculatedUri = ((HttpUriRequest) httpRequest).getURI();
}
if (calculatedUri == null && httpHost != null) {
try {
calculatedUri = new URI(httpHost.toURI() + httpRequest.getRequestLine().getUri());
} catch (URISyntaxException e) {
// Ignore
}
}
uri = calculatedUri;
delegate = httpRequest;
}

/** Returns the actual {@link HttpRequest} being executed by the client. */
public HttpRequest getDelegate() {
return delegate;
}

@Nullable
String getHeader(String name) {
Header header = delegate.getFirstHeader(name);
return header != null ? header.getValue() : null;
}

void setHeader(String name, String value) {
delegate.setHeader(name, value);
}

String getMethod() {
return delegate.getRequestLine().getMethod();
}

@Nullable
String getUrl() {
return uri != null ? uri.toString() : null;
}

@Nullable
String getTarget() {
if (uri == null) {
return null;
}
String pathString = uri.getPath();
String queryString = uri.getQuery();
if (pathString != null && queryString != null) {
return pathString + "?" + queryString;
} else if (queryString != null) {
return "?" + queryString;
} else {
return pathString;
}
}

@Nullable
String getScheme() {
return uri != null ? uri.getScheme() : null;
}

@Nullable
String getFlavor() {
ProtocolVersion protocolVersion = delegate.getProtocolVersion();
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;
}

@Nullable
String getPeerName() {
return uri != null ? uri.getHost() : null;
}

@Nullable
Integer getPeerPort() {
if (uri == null) {
return null;
}
int port = uri.getPort();
if (port != -1) {
return port;
}
switch (uri.getScheme()) {
case "http":
return 80;
case "https":
return 443;
default:
logger.debug("no default port mapping for scheme: {}", uri.getScheme());
return null;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.apachehttpclient.v4_3;

import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.context.propagation.ContextPropagators;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import org.apache.http.HttpResponse;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;

/** Entrypoint for tracing Apache HTTP Client. */
public final class ApacheHttpClientTracing {

/**
* Returns a new {@link ApacheHttpClientTracing} configured with the given {@link OpenTelemetry}.
*/
public static ApacheHttpClientTracing create(OpenTelemetry openTelemetry) {
return newBuilder(openTelemetry).build();
}

/**
* Returns a new {@link ApacheHttpClientTracingBuilder} configured with the given {@link
* OpenTelemetry}.
*/
public static ApacheHttpClientTracingBuilder newBuilder(OpenTelemetry openTelemetry) {
return new ApacheHttpClientTracingBuilder(openTelemetry);
}

private final Instrumenter<ApacheHttpClientRequest, HttpResponse> instrumenter;
private final ContextPropagators propagators;

ApacheHttpClientTracing(
Instrumenter<ApacheHttpClientRequest, HttpResponse> instrumenter,
ContextPropagators propagators) {
this.instrumenter = instrumenter;
this.propagators = propagators;
}

/** Returns a new {@link CloseableHttpClient} with tracing configured. */
public CloseableHttpClient newHttpClient() {
return newHttpClientBuilder().build();
}

/** Returns a new {@link HttpClientBuilder} to create a client with tracing configured. */
public HttpClientBuilder newHttpClientBuilder() {
return new TracingHttpClientBuilder(instrumenter, propagators);
}
}
Loading