Apache http client 4.3 low level instrumentation (#10253)
This commit is contained in:
parent
4f0baff3f8
commit
32b3326190
|
@ -9,30 +9,22 @@ import io.opentelemetry.context.Context;
|
||||||
import io.opentelemetry.context.Scope;
|
import io.opentelemetry.context.Scope;
|
||||||
import io.opentelemetry.context.propagation.ContextPropagators;
|
import io.opentelemetry.context.propagation.ContextPropagators;
|
||||||
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
|
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
|
||||||
|
import io.opentelemetry.instrumentation.api.semconv.http.HttpClientRequestResendCount;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import org.apache.http.HttpException;
|
import org.apache.http.HttpException;
|
||||||
import org.apache.http.HttpHost;
|
import org.apache.http.HttpHost;
|
||||||
import org.apache.http.HttpResponse;
|
import org.apache.http.HttpResponse;
|
||||||
import org.apache.http.ProtocolException;
|
|
||||||
import org.apache.http.client.ClientProtocolException;
|
|
||||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||||
import org.apache.http.client.methods.HttpExecutionAware;
|
import org.apache.http.client.methods.HttpExecutionAware;
|
||||||
import org.apache.http.client.methods.HttpRequestWrapper;
|
import org.apache.http.client.methods.HttpRequestWrapper;
|
||||||
import org.apache.http.client.protocol.HttpClientContext;
|
import org.apache.http.client.protocol.HttpClientContext;
|
||||||
import org.apache.http.conn.routing.HttpRoute;
|
import org.apache.http.conn.routing.HttpRoute;
|
||||||
import org.apache.http.impl.client.DefaultRedirectStrategy;
|
|
||||||
import org.apache.http.impl.client.RedirectLocations;
|
|
||||||
import org.apache.http.impl.execchain.ClientExecChain;
|
import org.apache.http.impl.execchain.ClientExecChain;
|
||||||
|
|
||||||
final class TracingProtocolExec implements ClientExecChain {
|
final class TracingProtocolExec implements ClientExecChain {
|
||||||
|
|
||||||
private static final String REQUEST_CONTEXT_ATTRIBUTE_ID =
|
private static final String REQUEST_PARENT_CONTEXT_ATTRIBUTE_ID =
|
||||||
TracingProtocolExec.class.getName() + ".context";
|
TracingProtocolExec.class.getName() + ".context";
|
||||||
private static final String REQUEST_WRAPPER_ATTRIBUTE_ID =
|
|
||||||
TracingProtocolExec.class.getName() + ".requestWrapper";
|
|
||||||
private static final String REDIRECT_COUNT_ATTRIBUTE_ID =
|
|
||||||
TracingProtocolExec.class.getName() + ".redirectCount";
|
|
||||||
|
|
||||||
private final Instrumenter<ApacheHttpClientRequest, HttpResponse> instrumenter;
|
private final Instrumenter<ApacheHttpClientRequest, HttpResponse> instrumenter;
|
||||||
private final ContextPropagators propagators;
|
private final ContextPropagators propagators;
|
||||||
|
@ -54,14 +46,12 @@ final class TracingProtocolExec implements ClientExecChain {
|
||||||
HttpClientContext httpContext,
|
HttpClientContext httpContext,
|
||||||
HttpExecutionAware httpExecutionAware)
|
HttpExecutionAware httpExecutionAware)
|
||||||
throws IOException, HttpException {
|
throws IOException, HttpException {
|
||||||
Context context = httpContext.getAttribute(REQUEST_CONTEXT_ATTRIBUTE_ID, Context.class);
|
|
||||||
if (context != null) {
|
Context parentContext =
|
||||||
ApacheHttpClientRequest instrumenterRequest =
|
httpContext.getAttribute(REQUEST_PARENT_CONTEXT_ATTRIBUTE_ID, Context.class);
|
||||||
httpContext.getAttribute(REQUEST_WRAPPER_ATTRIBUTE_ID, ApacheHttpClientRequest.class);
|
if (parentContext == null) {
|
||||||
// Request already had a context so a redirect. Don't create a new span just inject and
|
parentContext = HttpClientRequestResendCount.initialize(Context.current());
|
||||||
// execute.
|
httpContext.setAttribute(REQUEST_PARENT_CONTEXT_ATTRIBUTE_ID, parentContext);
|
||||||
propagators.getTextMapPropagator().inject(context, request, HttpHeaderSetter.INSTANCE);
|
|
||||||
return execute(route, request, instrumenterRequest, httpContext, httpExecutionAware, context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpHost host = null;
|
HttpHost host = null;
|
||||||
|
@ -81,16 +71,11 @@ final class TracingProtocolExec implements ClientExecChain {
|
||||||
}
|
}
|
||||||
ApacheHttpClientRequest instrumenterRequest = new ApacheHttpClientRequest(host, request);
|
ApacheHttpClientRequest instrumenterRequest = new ApacheHttpClientRequest(host, request);
|
||||||
|
|
||||||
Context parentContext = Context.current();
|
|
||||||
if (!instrumenter.shouldStart(parentContext, instrumenterRequest)) {
|
if (!instrumenter.shouldStart(parentContext, instrumenterRequest)) {
|
||||||
return exec.execute(route, request, httpContext, httpExecutionAware);
|
return exec.execute(route, request, httpContext, httpExecutionAware);
|
||||||
}
|
}
|
||||||
|
|
||||||
context = instrumenter.start(parentContext, instrumenterRequest);
|
Context context = instrumenter.start(parentContext, instrumenterRequest);
|
||||||
httpContext.setAttribute(REQUEST_CONTEXT_ATTRIBUTE_ID, context);
|
|
||||||
httpContext.setAttribute(REQUEST_WRAPPER_ATTRIBUTE_ID, instrumenterRequest);
|
|
||||||
httpContext.setAttribute(REDIRECT_COUNT_ATTRIBUTE_ID, 0);
|
|
||||||
|
|
||||||
propagators.getTextMapPropagator().inject(context, request, HttpHeaderSetter.INSTANCE);
|
propagators.getTextMapPropagator().inject(context, request, HttpHeaderSetter.INSTANCE);
|
||||||
|
|
||||||
return execute(route, request, instrumenterRequest, httpContext, httpExecutionAware, context);
|
return execute(route, request, instrumenterRequest, httpContext, httpExecutionAware, context);
|
||||||
|
@ -113,63 +98,7 @@ final class TracingProtocolExec implements ClientExecChain {
|
||||||
error = e;
|
error = e;
|
||||||
throw e;
|
throw e;
|
||||||
} finally {
|
} finally {
|
||||||
if (!pendingRedirect(context, httpContext, request, instrumenterRequest, response)) {
|
instrumenter.end(context, instrumenterRequest, response, error);
|
||||||
instrumenter.end(context, instrumenterRequest, response, error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean pendingRedirect(
|
|
||||||
Context context,
|
|
||||||
HttpClientContext httpContext,
|
|
||||||
HttpRequestWrapper request,
|
|
||||||
ApacheHttpClientRequest instrumenterRequest,
|
|
||||||
@Nullable CloseableHttpResponse response) {
|
|
||||||
if (response == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!httpContext.getRequestConfig().isRedirectsEnabled()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(anuraaga): Support redirect strategies other than the default. There is no way to get
|
|
||||||
// the user defined redirect strategy without some tricks, but it's very rare to override
|
|
||||||
// the strategy, usually it is either on or off as checked above. We can add support for this
|
|
||||||
// later if needed.
|
|
||||||
try {
|
|
||||||
if (!DefaultRedirectStrategy.INSTANCE.isRedirected(request, response, httpContext)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} catch (ProtocolException e) {
|
|
||||||
// DefaultRedirectStrategy.isRedirected cannot throw this so just return a default.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Very hacky and a bit slow, but the only way to determine whether the client will fail with
|
|
||||||
// a circular redirect, which happens before exec decorators run.
|
|
||||||
RedirectLocations redirectLocations =
|
|
||||||
(RedirectLocations) httpContext.getAttribute(HttpClientContext.REDIRECT_LOCATIONS);
|
|
||||||
if (redirectLocations != null) {
|
|
||||||
RedirectLocations copy = new RedirectLocations();
|
|
||||||
copy.addAll(redirectLocations);
|
|
||||||
|
|
||||||
try {
|
|
||||||
DefaultRedirectStrategy.INSTANCE.getLocationURI(request, response, httpContext);
|
|
||||||
} catch (ProtocolException e) {
|
|
||||||
// We will not be returning to the Exec, finish the span.
|
|
||||||
instrumenter.end(context, instrumenterRequest, response, new ClientProtocolException(e));
|
|
||||||
return true;
|
|
||||||
} finally {
|
|
||||||
httpContext.setAttribute(HttpClientContext.REDIRECT_LOCATIONS, copy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int redirectCount = httpContext.getAttribute(REDIRECT_COUNT_ATTRIBUTE_ID, Integer.class);
|
|
||||||
if (++redirectCount > httpContext.getRequestConfig().getMaxRedirects()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
httpContext.setAttribute(REDIRECT_COUNT_ATTRIBUTE_ID, redirectCount);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ package io.opentelemetry.instrumentation.apachehttpclient.v4_3;
|
||||||
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
|
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
|
||||||
import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpClientTest;
|
import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpClientTest;
|
||||||
import io.opentelemetry.instrumentation.testing.junit.http.HttpClientResult;
|
import io.opentelemetry.instrumentation.testing.junit.http.HttpClientResult;
|
||||||
|
import io.opentelemetry.instrumentation.testing.junit.http.HttpClientTestOptions;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.UncheckedIOException;
|
import java.io.UncheckedIOException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
@ -54,8 +55,15 @@ public abstract class AbstractApacheHttpClientTest {
|
||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract static class ApacheHttpClientTest<T> extends AbstractHttpClientTest<T> {
|
||||||
|
@Override
|
||||||
|
protected void configure(HttpClientTestOptions.Builder optionsBuilder) {
|
||||||
|
optionsBuilder.markAsLowLevelInstrumentation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Nested
|
@Nested
|
||||||
class ApacheClientHostRequestTest extends AbstractHttpClientTest<BasicHttpRequest> {
|
class ApacheClientHostRequestTest extends ApacheHttpClientTest<BasicHttpRequest> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BasicHttpRequest buildRequest(String method, URI uri, Map<String, String> headers) {
|
public BasicHttpRequest buildRequest(String method, URI uri, Map<String, String> headers) {
|
||||||
|
@ -92,7 +100,7 @@ public abstract class AbstractApacheHttpClientTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nested
|
@Nested
|
||||||
class ApacheClientHostRequestContextTest extends AbstractHttpClientTest<BasicHttpRequest> {
|
class ApacheClientHostRequestContextTest extends ApacheHttpClientTest<BasicHttpRequest> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BasicHttpRequest buildRequest(String method, URI uri, Map<String, String> headers) {
|
public BasicHttpRequest buildRequest(String method, URI uri, Map<String, String> headers) {
|
||||||
|
@ -133,7 +141,7 @@ public abstract class AbstractApacheHttpClientTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nested
|
@Nested
|
||||||
class ApacheClientHostAbsoluteUriRequestTest extends AbstractHttpClientTest<BasicHttpRequest> {
|
class ApacheClientHostAbsoluteUriRequestTest extends ApacheHttpClientTest<BasicHttpRequest> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BasicHttpRequest buildRequest(String method, URI uri, Map<String, String> headers) {
|
public BasicHttpRequest buildRequest(String method, URI uri, Map<String, String> headers) {
|
||||||
|
@ -170,7 +178,7 @@ public abstract class AbstractApacheHttpClientTest {
|
||||||
|
|
||||||
@Nested
|
@Nested
|
||||||
class ApacheClientHostAbsoluteUriRequestContextTest
|
class ApacheClientHostAbsoluteUriRequestContextTest
|
||||||
extends AbstractHttpClientTest<BasicHttpRequest> {
|
extends ApacheHttpClientTest<BasicHttpRequest> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BasicHttpRequest buildRequest(String method, URI uri, Map<String, String> headers) {
|
public BasicHttpRequest buildRequest(String method, URI uri, Map<String, String> headers) {
|
||||||
|
@ -210,7 +218,7 @@ public abstract class AbstractApacheHttpClientTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nested
|
@Nested
|
||||||
class ApacheClientUriRequestTest extends AbstractHttpClientTest<HttpUriRequest> {
|
class ApacheClientUriRequestTest extends ApacheHttpClientTest<HttpUriRequest> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HttpUriRequest buildRequest(String method, URI uri, Map<String, String> headers) {
|
public HttpUriRequest buildRequest(String method, URI uri, Map<String, String> headers) {
|
||||||
|
@ -241,7 +249,7 @@ public abstract class AbstractApacheHttpClientTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nested
|
@Nested
|
||||||
class ApacheClientUriRequestContextTest extends AbstractHttpClientTest<HttpUriRequest> {
|
class ApacheClientUriRequestContextTest extends ApacheHttpClientTest<HttpUriRequest> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HttpUriRequest buildRequest(String method, URI uri, Map<String, String> headers) {
|
public HttpUriRequest buildRequest(String method, URI uri, Map<String, String> headers) {
|
||||||
|
|
Loading…
Reference in New Issue