HTTP client instrumentation cleanup: aws-sdk (#1911)

This commit is contained in:
Trask Stalnaker 2020-12-14 23:06:28 -08:00 committed by GitHub
parent deda1af9c0
commit 49c2a36811
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 69 additions and 87 deletions

View File

@ -11,7 +11,7 @@ import com.amazonaws.Request;
import com.amazonaws.Response;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.propagation.TextMapPropagator.Setter;
import io.opentelemetry.context.propagation.TextMapPropagator;
import io.opentelemetry.instrumentation.api.tracer.HttpClientTracer;
import java.net.URI;
import java.util.concurrent.ConcurrentHashMap;
@ -31,7 +31,7 @@ public class AwsSdkClientTracer extends HttpClientTracer<Request<?>, Request<?>,
public AwsSdkClientTracer() {}
@Override
public String spanNameForRequest(Request<?> request) {
protected String spanNameForRequest(Request<?> request) {
if (request == null) {
return DEFAULT_SPAN_NAME;
}
@ -111,7 +111,7 @@ public class AwsSdkClientTracer extends HttpClientTracer<Request<?>, Request<?>,
}
@Override
protected Setter<Request<?>> getSetter() {
protected TextMapPropagator.Setter<Request<?>> getSetter() {
return AwsSdkInjectAdapter.INSTANCE;
}

View File

@ -12,7 +12,7 @@ public class RequestMeta {
// Note: aws1.x sdk doesn't have any truly async clients so we can store scope in request context
// safely.
public static final HandlerContextKey<ContextScopePair> CONTEXT_SCOPE_PAIR_CONTEXT_KEY =
new HandlerContextKey<>(RequestMeta.class.getName() + ".ContextSpanPair");
new HandlerContextKey<>(RequestMeta.class.getName() + ".ContextScopePair");
private String bucketName;
private String queueUrl;

View File

@ -30,31 +30,33 @@ public class TracingRequestHandler extends RequestHandler2 {
AmazonWebServiceRequest originalRequest = request.getOriginalRequest();
RequestMeta requestMeta = contextStore.get(originalRequest);
Context parentContext = Context.current();
if (tracer().shouldStartSpan(parentContext)) {
Context context = tracer().startSpan(parentContext, request, requestMeta);
Scope scope = context.makeCurrent();
request.addHandlerContext(
CONTEXT_SCOPE_PAIR_CONTEXT_KEY, new ContextScopePair(context, scope));
if (!tracer().shouldStartSpan(parentContext)) {
return;
}
Context context = tracer().startSpan(parentContext, request, requestMeta);
Scope scope = context.makeCurrent();
request.addHandlerContext(CONTEXT_SCOPE_PAIR_CONTEXT_KEY, new ContextScopePair(context, scope));
}
@Override
public void afterResponse(Request<?> request, Response<?> response) {
ContextScopePair scope = request.getHandlerContext(CONTEXT_SCOPE_PAIR_CONTEXT_KEY);
if (scope != null) {
request.addHandlerContext(CONTEXT_SCOPE_PAIR_CONTEXT_KEY, null);
scope.closeScope();
tracer().end(scope.getContext(), response);
if (scope == null) {
return;
}
request.addHandlerContext(CONTEXT_SCOPE_PAIR_CONTEXT_KEY, null);
scope.closeScope();
tracer().end(scope.getContext(), response);
}
@Override
public void afterError(Request<?> request, Response<?> response, Exception e) {
ContextScopePair scope = request.getHandlerContext(CONTEXT_SCOPE_PAIR_CONTEXT_KEY);
if (scope != null) {
request.addHandlerContext(CONTEXT_SCOPE_PAIR_CONTEXT_KEY, null);
scope.closeScope();
tracer().endExceptionally(scope.getContext(), response, e);
if (scope == null) {
return;
}
request.addHandlerContext(CONTEXT_SCOPE_PAIR_CONTEXT_KEY, null);
scope.closeScope();
tracer().endExceptionally(scope.getContext(), e);
}
}

View File

@ -8,8 +8,6 @@ package io.opentelemetry.instrumentation.awssdk.v2_2;
import static io.opentelemetry.instrumentation.awssdk.v2_2.TracingExecutionInterceptor.CONTEXT_ATTRIBUTE;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Span.Kind;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Context;
import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
@ -40,29 +38,17 @@ public class AwsSdk {
/**
* Returns an {@link ExecutionInterceptor} that can be used with an {@link
* software.amazon.awssdk.http.SdkHttpClient} to trace SDK requests. Spans are created with the
* kind {@link Kind#CLIENT}. If you also instrument the HTTP calls made by the SDK, e.g., by
* adding Apache HTTP client or Netty instrumentation, you may want to use {@link
* #newInterceptor(Kind)} with {@link Kind#INTERNAL} instead.
* software.amazon.awssdk.http.SdkHttpClient} to trace SDK requests.
*/
public static ExecutionInterceptor newInterceptor() {
return newInterceptor(Kind.CLIENT);
return new TracingExecutionInterceptor();
}
/**
* Returns an {@link ExecutionInterceptor} that can be used with an {@link
* software.amazon.awssdk.http.SdkHttpClient} to trace SDK requests. Spans are created with the
* provided {@link Kind}.
* Returns the {@link Context} stored in the {@link ExecutionAttributes}, or {@code null} if there
* is no operation set.
*/
public static ExecutionInterceptor newInterceptor(Kind kind) {
return new TracingExecutionInterceptor(kind);
}
/**
* Returns the {@link Span} stored in the {@link ExecutionAttributes}, or {@code null} if there is
* no span set.
*/
public static Context getContextFromAttributes(ExecutionAttributes attributes) {
public static Context getContext(ExecutionAttributes attributes) {
return attributes.getAttribute(CONTEXT_ATTRIBUTE);
}
}

View File

@ -5,21 +5,22 @@
package io.opentelemetry.instrumentation.awssdk.v2_2;
import static io.opentelemetry.api.trace.Span.Kind.CLIENT;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Span.Kind;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.api.trace.attributes.SemanticAttributes;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.propagation.TextMapPropagator.Setter;
import io.opentelemetry.instrumentation.api.tracer.BaseTracer;
import io.opentelemetry.context.propagation.TextMapPropagator;
import io.opentelemetry.extension.trace.propagation.AwsXRayPropagator;
import io.opentelemetry.instrumentation.api.tracer.HttpClientTracer;
import java.net.URI;
import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
import software.amazon.awssdk.core.interceptor.SdkExecutionAttribute;
import software.amazon.awssdk.http.SdkHttpHeaders;
import software.amazon.awssdk.http.SdkHttpRequest;
import software.amazon.awssdk.http.SdkHttpResponse;
final class AwsSdkHttpClientTracer
extends HttpClientTracer<SdkHttpRequest, SdkHttpRequest, SdkHttpResponse> {
extends HttpClientTracer<SdkHttpRequest, SdkHttpRequest.Builder, SdkHttpResponse> {
private static final AwsSdkHttpClientTracer TRACER = new AwsSdkHttpClientTracer();
@ -27,10 +28,15 @@ final class AwsSdkHttpClientTracer
return TRACER;
}
// Certain headers in the request like User-Agent are only available after execution.
Span afterExecution(Span span, SdkHttpRequest request) {
span.setAttribute(SemanticAttributes.HTTP_USER_AGENT, requestHeader(request, USER_AGENT));
return span;
public Context startSpan(Context parentContext, ExecutionAttributes attributes) {
String spanName = spanName(attributes);
Span span =
tracer.spanBuilder(spanName).setSpanKind(CLIENT).setParent(parentContext).startSpan();
return withClientSpan(parentContext, span);
}
public void inject(Context context, SdkHttpRequest.Builder builder) {
AwsXRayPropagator.getInstance().inject(context, builder, getSetter());
}
@Override
@ -59,8 +65,8 @@ final class AwsSdkHttpClientTracer
}
@Override
protected Setter<SdkHttpRequest> getSetter() {
return null;
protected TextMapPropagator.Setter<SdkHttpRequest.Builder> getSetter() {
return AwsSdkInjectAdapter.INSTANCE;
}
private static String header(SdkHttpHeaders headers, String name) {
@ -78,9 +84,9 @@ final class AwsSdkHttpClientTracer
super.onRequest(span, sdkHttpRequest);
}
public Context startSpan(Context parentContext, String name, Tracer tracer, Kind kind) {
Span clientSpan =
tracer.spanBuilder(name).setSpanKind(kind).setParent(parentContext).startSpan();
return parentContext.with(clientSpan).with(BaseTracer.CONTEXT_CLIENT_SPAN_KEY, clientSpan);
private static String spanName(ExecutionAttributes attributes) {
String awsServiceName = attributes.getAttribute(SdkExecutionAttribute.SERVICE_NAME);
String awsOperation = attributes.getAttribute(SdkExecutionAttribute.OPERATION_NAME);
return awsServiceName + "." + awsOperation;
}
}

View File

@ -5,14 +5,13 @@
package io.opentelemetry.instrumentation.awssdk.v2_2;
import static io.opentelemetry.instrumentation.awssdk.v2_2.AwsSdk.getContextFromAttributes;
import static io.opentelemetry.instrumentation.awssdk.v2_2.AwsSdk.getContext;
import static io.opentelemetry.instrumentation.awssdk.v2_2.AwsSdkHttpClientTracer.tracer;
import static io.opentelemetry.instrumentation.awssdk.v2_2.RequestType.ofSdkRequest;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Span.Kind;
import io.opentelemetry.api.trace.attributes.SemanticAttributes;
import io.opentelemetry.context.Scope;
import io.opentelemetry.extension.trace.propagation.AwsXRayPropagator;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;
@ -61,12 +60,6 @@ final class TracingExecutionInterceptor implements ExecutionInterceptor {
return result;
}
private final Kind kind;
TracingExecutionInterceptor(Kind kind) {
this.kind = kind;
}
@Nullable
private SdkRequestDecorator decorator(ExecutionAttributes executionAttributes) {
RequestType type = getTypeFromAttributes(executionAttributes);
@ -80,12 +73,12 @@ final class TracingExecutionInterceptor implements ExecutionInterceptor {
@Override
public void beforeExecution(
Context.BeforeExecution context, ExecutionAttributes executionAttributes) {
io.opentelemetry.context.Context parentContext = io.opentelemetry.context.Context.current();
if (!tracer().shouldStartSpan(parentContext)) {
io.opentelemetry.context.Context parentOtelContext = io.opentelemetry.context.Context.current();
if (!tracer().shouldStartSpan(parentOtelContext)) {
return;
}
io.opentelemetry.context.Context otelContext =
tracer().startSpan(parentContext, spanName(executionAttributes), AwsSdk.tracer(), kind);
tracer().startSpan(parentOtelContext, executionAttributes);
executionAttributes.putAttribute(CONTEXT_ATTRIBUTE, otelContext);
RequestType type = ofSdkRequest(context.request());
if (type != null) {
@ -103,20 +96,20 @@ final class TracingExecutionInterceptor implements ExecutionInterceptor {
@Override
public SdkHttpRequest modifyHttpRequest(
Context.ModifyHttpRequest context, ExecutionAttributes executionAttributes) {
io.opentelemetry.context.Context otelContext = getContextFromAttributes(executionAttributes);
io.opentelemetry.context.Context otelContext = getContext(executionAttributes);
if (otelContext == null) {
return context.httpRequest();
}
SdkHttpRequest.Builder builder = context.httpRequest().toBuilder();
AwsXRayPropagator.getInstance().inject(otelContext, builder, AwsSdkInjectAdapter.INSTANCE);
tracer().inject(otelContext, builder);
return builder.build();
}
@Override
public void afterMarshalling(
Context.AfterMarshalling context, ExecutionAttributes executionAttributes) {
io.opentelemetry.context.Context otelContext = getContextFromAttributes(executionAttributes);
io.opentelemetry.context.Context otelContext = getContext(executionAttributes);
if (otelContext == null) {
return;
}
@ -160,14 +153,18 @@ final class TracingExecutionInterceptor implements ExecutionInterceptor {
if (scope != null) {
scope.close();
}
io.opentelemetry.context.Context otelContext = getContextFromAttributes(executionAttributes);
if (otelContext != null) {
clearAttributes(executionAttributes);
Span span = Span.fromContext(otelContext);
tracer().afterExecution(span, context.httpRequest());
onSdkResponse(span, context.response());
tracer().end(otelContext, context.httpResponse());
}
io.opentelemetry.context.Context otelContext = getContext(executionAttributes);
clearAttributes(executionAttributes);
Span span = Span.fromContext(otelContext);
onUserAgentHeaderAvailable(span, context.httpRequest());
onSdkResponse(span, context.response());
tracer().end(otelContext, context.httpResponse());
}
// Certain headers in the request like User-Agent are only available after execution.
private void onUserAgentHeaderAvailable(Span span, SdkHttpRequest request) {
span.setAttribute(
SemanticAttributes.HTTP_USER_AGENT, tracer().requestHeader(request, "User-Agent"));
}
private void onSdkResponse(Span span, SdkResponse response) {
@ -179,22 +176,13 @@ final class TracingExecutionInterceptor implements ExecutionInterceptor {
@Override
public void onExecutionFailure(
Context.FailedExecution context, ExecutionAttributes executionAttributes) {
io.opentelemetry.context.Context otelContext = getContextFromAttributes(executionAttributes);
if (otelContext != null) {
clearAttributes(executionAttributes);
tracer().endExceptionally(otelContext, context.exception());
}
io.opentelemetry.context.Context otelContext = getContext(executionAttributes);
clearAttributes(executionAttributes);
tracer().endExceptionally(otelContext, context.exception());
}
private void clearAttributes(ExecutionAttributes executionAttributes) {
executionAttributes.putAttribute(CONTEXT_ATTRIBUTE, null);
executionAttributes.putAttribute(REQUEST_TYPE_ATTRIBUTE, null);
}
private String spanName(ExecutionAttributes attributes) {
String awsServiceName = attributes.getAttribute(SdkExecutionAttribute.SERVICE_NAME);
String awsOperation = attributes.getAttribute(SdkExecutionAttribute.OPERATION_NAME);
return awsServiceName + "." + awsOperation;
}
}