Skip to content

Commit

Permalink
Handles not running http processor instrumentation for server kind an…
Browse files Browse the repository at this point in the history
…d when instrumentation is suppressed
  • Loading branch information
anuragagarwal561994 committed Jan 28, 2023
1 parent 022796d commit dcafa57
Show file tree
Hide file tree
Showing 8 changed files with 130 additions and 81 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.apachehttpclient.commons.BytesTransferMetrics;
import io.opentelemetry.javaagent.instrumentation.apachehttpclient.v4_0.commons.ApacheHttpClientInternalEntityStorage;
import io.opentelemetry.javaagent.instrumentation.apachehttpclient.v4_0.commons.ApacheHttpClientEntityStorage;
import io.opentelemetry.javaagent.instrumentation.apachehttpclient.v4_0.commons.ApacheHttpClientRequest;
import io.opentelemetry.javaagent.instrumentation.apachehttpclient.v4_0.commons.HttpOtelContext;
import java.io.IOException;
Expand Down Expand Up @@ -85,10 +85,10 @@ public static void methodEnter(
httpOtelContext.markAsyncClient();

WrappedFutureCallback<?> wrappedFutureCallback =
new WrappedFutureCallback<>(parentContext, httpOtelContext, futureCallback);
new WrappedFutureCallback<>(parentContext, httpContext, futureCallback);
requestProducer =
new WrappedRequestProducer(
parentContext, httpOtelContext, requestProducer, wrappedFutureCallback);
parentContext, httpContext, requestProducer, wrappedFutureCallback);
responseConsumer = new WrappedResponseConsumer<>(parentContext, responseConsumer);
futureCallback = wrappedFutureCallback;
}
Expand Down Expand Up @@ -152,17 +152,17 @@ public boolean cancel() {

public static class WrappedRequestProducer implements HttpAsyncRequestProducer {
private final Context parentContext;
private final HttpOtelContext httpOtelContext;
private final HttpContext httpContext;
private final HttpAsyncRequestProducer delegate;
private final WrappedFutureCallback<?> wrappedFutureCallback;

public WrappedRequestProducer(
Context parentContext,
HttpOtelContext httpOtelContext,
HttpContext httpContext,
HttpAsyncRequestProducer delegate,
WrappedFutureCallback<?> wrappedFutureCallback) {
this.parentContext = parentContext;
this.httpOtelContext = httpOtelContext;
this.httpContext = httpContext;
this.delegate = delegate;
this.wrappedFutureCallback = wrappedFutureCallback;
}
Expand All @@ -188,7 +188,7 @@ public HttpRequest generateRequest() throws IOException, HttpException {
// As the http processor instrumentation is going to be called asynchronously,
// we will need to store the otel context variables in http context for the
// http processor instrumentation to use
httpOtelContext.setContext(context);
ApacheHttpClientEntityStorage.setCurrentContext(httpContext, context);
}

return request;
Expand Down Expand Up @@ -281,16 +281,16 @@ public static class WrappedFutureCallback<T> implements FutureCallback<T> {
private static final Logger logger = Logger.getLogger(WrappedFutureCallback.class.getName());

private final Context parentContext;
private final HttpOtelContext httpOtelContext;
private final HttpContext httpContext;
private final FutureCallback<T> delegate;

private volatile Context context;
private volatile ApacheHttpClientRequest otelRequest;

public WrappedFutureCallback(
Context parentContext, HttpOtelContext httpOtelContext, FutureCallback<T> delegate) {
Context parentContext, HttpContext httpContext, FutureCallback<T> delegate) {
this.parentContext = parentContext;
this.httpOtelContext = httpOtelContext;
this.httpContext = httpContext;
// Note: this can be null in real life, so we have to handle this carefully
this.delegate = delegate;
}
Expand Down Expand Up @@ -383,19 +383,19 @@ private void cancelDelegate() {
}

private void removeOtelAttributes() {
httpOtelContext.clear();
ApacheHttpClientEntityStorage.clearOtelAttributes(httpContext);
}

private HttpResponse getFinalResponse() {
return getFinalResponse(null);
}

private HttpResponse getFinalResponse(T result) {
return ApacheHttpClientInternalEntityStorage.getFinalResponse(result, context);
return ApacheHttpClientEntityStorage.getFinalResponse(result, context);
}

private ApacheHttpClientRequest getFinalRequest() {
return ApacheHttpClientInternalEntityStorage.getFinalRequest(otelRequest, context);
return ApacheHttpClientEntityStorage.getFinalRequest(otelRequest, context);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@

import static io.opentelemetry.javaagent.instrumentation.apachehttpclient.commons.BytesTransferMetrics.createOrGetWithParentContext;
import static io.opentelemetry.javaagent.instrumentation.apachehttpclient.v4_0.ApacheHttpClientSingletons.instrumenter;
import static io.opentelemetry.javaagent.instrumentation.apachehttpclient.v4_0.commons.ApacheHttpClientInternalEntityStorage.getFinalRequest;
import static io.opentelemetry.javaagent.instrumentation.apachehttpclient.v4_0.commons.ApacheHttpClientInternalEntityStorage.getFinalResponse;
import static io.opentelemetry.javaagent.instrumentation.apachehttpclient.v4_0.commons.ApacheHttpClientEntityStorage.getFinalRequest;
import static io.opentelemetry.javaagent.instrumentation.apachehttpclient.v4_0.commons.ApacheHttpClientEntityStorage.getFinalResponse;

import io.opentelemetry.context.Context;
import io.opentelemetry.javaagent.instrumentation.apachehttpclient.commons.BytesTransferMetrics;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,9 @@ public static void methodEnter(
httpOtelContext.markAsyncClient();

WrappedFutureCallback<?> wrappedFutureCallback =
new WrappedFutureCallback<>(parentContext, httpOtelContext, futureCallback);
new WrappedFutureCallback<>(parentContext, httpContext, futureCallback);
requestProducer =
new WrappedRequestProducer(
parentContext, httpOtelContext, requestProducer, wrappedFutureCallback);
new WrappedRequestProducer(parentContext, requestProducer, wrappedFutureCallback);
responseConsumer = new WrappedResponseConsumer<>(parentContext, responseConsumer);
futureCallback = wrappedFutureCallback;
}
Expand Down Expand Up @@ -153,17 +152,12 @@ public void releaseResources() {

public static class WrappedRequestProducer implements AsyncRequestProducer {
private final Context parentContext;
private final HttpOtelContext httpOtelContext;
private final AsyncRequestProducer delegate;
private final WrappedFutureCallback<?> callback;

public WrappedRequestProducer(
Context parentContext,
HttpOtelContext httpOtelContext,
AsyncRequestProducer delegate,
WrappedFutureCallback<?> callback) {
Context parentContext, AsyncRequestProducer delegate, WrappedFutureCallback<?> callback) {
this.parentContext = parentContext;
this.httpOtelContext = httpOtelContext;
this.delegate = delegate;
this.callback = callback;
}
Expand All @@ -177,7 +171,7 @@ public void failed(Exception ex) {
public void sendRequest(RequestChannel channel, HttpContext context)
throws HttpException, IOException {
RequestChannel requestChannel;
requestChannel = new WrappedRequestChannel(channel, httpOtelContext, parentContext, callback);
requestChannel = new WrappedRequestChannel(channel, parentContext, callback);
delegate.sendRequest(requestChannel, context);
}

Expand Down Expand Up @@ -236,17 +230,14 @@ public void endStream(List<? extends Header> list) throws IOException {

public static class WrappedRequestChannel implements RequestChannel {
private final RequestChannel delegate;
private final HttpOtelContext httpOtelContext;
private final Context parentContext;
private final WrappedFutureCallback<?> wrappedFutureCallback;

public WrappedRequestChannel(
RequestChannel requestChannel,
HttpOtelContext httpOtelContext,
Context parentContext,
WrappedFutureCallback<?> wrappedFutureCallback) {
this.delegate = requestChannel;
this.httpOtelContext = httpOtelContext;
this.parentContext = parentContext;
this.wrappedFutureCallback = wrappedFutureCallback;
}
Expand All @@ -265,7 +256,7 @@ public void sendRequest(
// As the http processor instrumentation is going to be called asynchronously,
// we will need to store the otel context variables in http context for the
// http processor instrumentation to use
httpOtelContext.setContext(context);
ApacheHttpClientEntityStorage.setCurrentContext(httpContext, context);
}

delegate.sendRequest(request, entityDetails, httpContext);
Expand All @@ -276,16 +267,16 @@ public static class WrappedFutureCallback<T> implements FutureCallback<T> {
private static final Logger logger = Logger.getLogger(WrappedFutureCallback.class.getName());

private final Context parentContext;
private final HttpOtelContext httpOtelContext;
private final HttpContext httpContext;
private final FutureCallback<T> delegate;

private volatile Context context;
private volatile ApacheHttpClientRequest otelRequest;

public WrappedFutureCallback(
Context parentContext, HttpOtelContext httpOtelContext, FutureCallback<T> delegate) {
Context parentContext, HttpContext httpContext, FutureCallback<T> delegate) {
this.parentContext = parentContext;
this.httpOtelContext = httpOtelContext;
this.httpContext = httpContext;
// Note: this can be null in real life, so we have to handle this carefully
this.delegate = delegate;
}
Expand Down Expand Up @@ -378,7 +369,7 @@ private void cancelDelegate() {
}

private void removeOtelAttributes() {
httpOtelContext.clear();
ApacheHttpClientEntityStorage.clearOtelAttributes(httpContext);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,22 @@

package io.opentelemetry.javaagent.instrumentation.apachehttpclient.v5_0;

import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext;

import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import io.opentelemetry.instrumentation.api.internal.SpanKey;
import io.opentelemetry.instrumentation.api.util.VirtualField;
import javax.annotation.Nullable;
import org.apache.hc.core5.http.HttpRequest;
import org.apache.hc.core5.http.HttpResponse;
import org.apache.hc.core5.http.protocol.HttpContext;

public class ApacheHttpClientInternalEntityStorage {
public class ApacheHttpClientEntityStorage {
private static final VirtualField<Context, HttpRequest> httpRequestByOtelContext;
private static final VirtualField<Context, HttpResponse> httpResponseByOtelContext;

public ApacheHttpClientInternalEntityStorage() {}
public ApacheHttpClientEntityStorage() {}

static {
httpRequestByOtelContext = VirtualField.find(Context.class, HttpRequest.class);
Expand Down Expand Up @@ -56,4 +62,42 @@ public static <T> HttpResponse getFinalResponse(T response, Context context) {
}
return null;
}

public static void setCurrentContext(HttpContext httpContext, Context context) {
if (httpContext != null) {
HttpOtelContext httpOtelContext = HttpOtelContext.adapt(httpContext);
httpOtelContext.setContext(context);
}
}

public static void clearOtelAttributes(HttpContext httpContext) {
if (httpContext != null) {
HttpOtelContext.adapt(httpContext).clear();
}
}

@Nullable
public static Context getCurrentContext(HttpContext httpContext) {
if (httpContext == null) {
return null;
}
HttpOtelContext httpOtelContext = HttpOtelContext.adapt(httpContext);
Context otelContext = httpOtelContext.getContext();
if (otelContext == null) {
// for async clients, the contexts should always be set by their instrumentation
if (httpOtelContext.isAsyncClient()) {
return null;
}
// for classic clients, context will remain same as the caller
otelContext = currentContext();
}
// verifying if the current context is a http client context
// this eliminates suppressed contexts and http processor cases which ran for
// apache http server also present in the library
Span span = SpanKey.HTTP_CLIENT.fromContextOrNull(otelContext);
if (span == null) {
return null;
}
return otelContext;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
package io.opentelemetry.javaagent.instrumentation.apachehttpclient.v5_0;

import static io.opentelemetry.javaagent.instrumentation.apachehttpclient.commons.BytesTransferMetrics.createOrGetWithParentContext;
import static io.opentelemetry.javaagent.instrumentation.apachehttpclient.v5_0.ApacheHttpClientInternalEntityStorage.getFinalRequest;
import static io.opentelemetry.javaagent.instrumentation.apachehttpclient.v5_0.ApacheHttpClientInternalEntityStorage.getFinalResponse;
import static io.opentelemetry.javaagent.instrumentation.apachehttpclient.v5_0.ApacheHttpClientEntityStorage.getFinalRequest;
import static io.opentelemetry.javaagent.instrumentation.apachehttpclient.v5_0.ApacheHttpClientEntityStorage.getFinalResponse;
import static io.opentelemetry.javaagent.instrumentation.apachehttpclient.v5_0.ApacheHttpClientSingletons.instrumenter;

import io.opentelemetry.context.Context;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

package io.opentelemetry.javaagent.instrumentation.apachehttpclient.v5_0;

import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface;
import static net.bytebuddy.matcher.ElementMatchers.isAbstract;
Expand Down Expand Up @@ -75,17 +74,10 @@ public static void methodEnter(
@Advice.Argument(value = 0) HttpRequest httpRequest,
@Advice.Argument(value = 1) EntityDetails entityDetails,
@Advice.Argument(value = 2) HttpContext httpContext) {
HttpOtelContext httpOtelContext = HttpOtelContext.adapt(httpContext);
Context otelContext = httpOtelContext.getContext();
if (otelContext == null) {
// for async clients, the contexts should always be set by their instrumentation
if (httpOtelContext.isAsyncClient()) {
return;
}
// for classic clients, context will remain same as the caller
otelContext = currentContext();
Context context = ApacheHttpClientEntityStorage.getCurrentContext(httpContext);
if (context != null) {
ApacheHttpClientEntityStorage.storeHttpRequest(context, httpRequest);
}
ApacheHttpClientInternalEntityStorage.storeHttpRequest(otelContext, httpRequest);
}
}

Expand All @@ -96,17 +88,10 @@ public static void methodEnter(
@Advice.Argument(value = 0) HttpResponse httpResponse,
@Advice.Argument(value = 1) EntityDetails entityDetails,
@Advice.Argument(value = 2) HttpContext httpContext) {
HttpOtelContext httpOtelContext = HttpOtelContext.adapt(httpContext);
Context otelContext = httpOtelContext.getContext();
if (otelContext == null) {
// for async clients, the contexts should always be set by their instrumentation
if (httpOtelContext.isAsyncClient()) {
return;
}
// for classic clients, context will remain same as the caller
otelContext = currentContext();
Context context = ApacheHttpClientEntityStorage.getCurrentContext(httpContext);
if (context != null) {
ApacheHttpClientEntityStorage.storeHttpResponse(context, httpResponse);
}
ApacheHttpClientInternalEntityStorage.storeHttpResponse(otelContext, httpResponse);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,22 @@

package io.opentelemetry.javaagent.instrumentation.apachehttpclient.v4_0.commons;

import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext;

import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import io.opentelemetry.instrumentation.api.internal.SpanKey;
import io.opentelemetry.instrumentation.api.util.VirtualField;
import javax.annotation.Nullable;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.protocol.HttpContext;

public final class ApacheHttpClientInternalEntityStorage {
public final class ApacheHttpClientEntityStorage {
private static final VirtualField<Context, HttpRequest> httpRequestByOtelContext;
private static final VirtualField<Context, HttpResponse> httpResponseByOtelContext;

public ApacheHttpClientInternalEntityStorage() {}
public ApacheHttpClientEntityStorage() {}

static {
httpRequestByOtelContext = VirtualField.find(Context.class, HttpRequest.class);
Expand Down Expand Up @@ -56,4 +62,42 @@ public static <T> HttpResponse getFinalResponse(T response, Context context) {
}
return null;
}

public static void setCurrentContext(HttpContext httpContext, Context context) {
if (httpContext != null) {
HttpOtelContext httpOtelContext = HttpOtelContext.adapt(httpContext);
httpOtelContext.setContext(context);
}
}

public static void clearOtelAttributes(HttpContext httpContext) {
if (httpContext != null) {
HttpOtelContext.adapt(httpContext).clear();
}
}

@Nullable
public static Context getCurrentContext(HttpContext httpContext) {
if (httpContext == null) {
return null;
}
HttpOtelContext httpOtelContext = HttpOtelContext.adapt(httpContext);
Context otelContext = httpOtelContext.getContext();
if (otelContext == null) {
// for async clients, the contexts should always be set by their instrumentation
if (httpOtelContext.isAsyncClient()) {
return null;
}
// for classic clients, context will remain same as the caller
otelContext = currentContext();
}
// verifying if the current context is a http client context
// this eliminates suppressed contexts and http processor cases which ran for
// apache http server also present in the library
Span span = SpanKey.HTTP_CLIENT.fromContextOrNull(otelContext);
if (span == null) {
return null;
}
return otelContext;
}
}
Loading

0 comments on commit dcafa57

Please sign in to comment.