Migrate AWS SDK v2 to new library API pattern. (#2487)
This commit is contained in:
parent
8242a01b3a
commit
926a1fb621
|
@ -5,7 +5,9 @@
|
|||
|
||||
package io.opentelemetry.javaagent.instrumentation.awssdk.v2_2;
|
||||
|
||||
import io.opentelemetry.instrumentation.awssdk.v2_2.AwsSdk;
|
||||
import io.opentelemetry.api.GlobalOpenTelemetry;
|
||||
import io.opentelemetry.instrumentation.api.config.Config;
|
||||
import io.opentelemetry.instrumentation.awssdk.v2_2.AwsSdkTracing;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Optional;
|
||||
|
@ -33,7 +35,7 @@ import software.amazon.awssdk.http.SdkHttpRequest;
|
|||
import software.amazon.awssdk.http.SdkHttpResponse;
|
||||
|
||||
/**
|
||||
* {@link ExecutionInterceptor} that delegates to {@link AwsSdk}, augmenting {@link
|
||||
* {@link ExecutionInterceptor} that delegates to {@link AwsSdkTracing}, augmenting {@link
|
||||
* #beforeTransmission(BeforeTransmission, ExecutionAttributes)} to make sure the span is set to the
|
||||
* current context to allow downstream instrumentation like Netty to pick it up.
|
||||
*/
|
||||
|
@ -42,7 +44,14 @@ public class TracingExecutionInterceptor implements ExecutionInterceptor {
|
|||
private final ExecutionInterceptor delegate;
|
||||
|
||||
public TracingExecutionInterceptor() {
|
||||
delegate = AwsSdk.newInterceptor();
|
||||
delegate =
|
||||
AwsSdkTracing.newBuilder(GlobalOpenTelemetry.get())
|
||||
.setCaptureExperimentalSpanAttributes(
|
||||
Config.get()
|
||||
.getBooleanProperty(
|
||||
"otel.instrumentation.aws-sdk.experimental-span-attributes", false))
|
||||
.build()
|
||||
.newExecutionInterceptor();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.awssdk.v2_2;
|
||||
|
||||
import static io.opentelemetry.instrumentation.awssdk.v2_2.TracingExecutionInterceptor.CONTEXT_ATTRIBUTE;
|
||||
|
||||
import io.opentelemetry.api.GlobalOpenTelemetry;
|
||||
import io.opentelemetry.api.trace.Tracer;
|
||||
import io.opentelemetry.context.Context;
|
||||
import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
|
||||
import software.amazon.awssdk.core.interceptor.ExecutionInterceptor;
|
||||
|
||||
/**
|
||||
* Entrypoint to OpenTelemetry instrumentation of the AWS SDK. Register the {@link
|
||||
* ExecutionInterceptor} returned by {@link #newInterceptor()} with an SDK client to have all
|
||||
* requests traced.
|
||||
*
|
||||
* <pre>{@code
|
||||
* DynamoDbClient dynamoDb = DynamoDbClient.builder()
|
||||
* .overrideConfiguration(ClientOverrideConfiguration.builder()
|
||||
* .addExecutionInterceptor(AwsSdk.newInterceptor())
|
||||
* .build())
|
||||
* .build();
|
||||
* }</pre>
|
||||
*/
|
||||
public class AwsSdk {
|
||||
|
||||
private static final Tracer tracer =
|
||||
GlobalOpenTelemetry.getTracer(AwsSdkHttpClientTracer.tracer().getInstrumentationName());
|
||||
|
||||
/** Returns the {@link Tracer} used to instrument the AWS SDK. */
|
||||
public static Tracer tracer() {
|
||||
return tracer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an {@link ExecutionInterceptor} that can be used with an {@link
|
||||
* software.amazon.awssdk.http.SdkHttpClient} to trace SDK requests.
|
||||
*/
|
||||
public static ExecutionInterceptor newInterceptor() {
|
||||
return new TracingExecutionInterceptor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Context} stored in the {@link ExecutionAttributes}, or {@code null} if there
|
||||
* is no operation set.
|
||||
*/
|
||||
public static Context getContext(ExecutionAttributes attributes) {
|
||||
return attributes.getAttribute(CONTEXT_ATTRIBUTE);
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@ package io.opentelemetry.instrumentation.awssdk.v2_2;
|
|||
|
||||
import static io.opentelemetry.api.trace.SpanKind.CLIENT;
|
||||
|
||||
import io.opentelemetry.api.OpenTelemetry;
|
||||
import io.opentelemetry.api.trace.Span;
|
||||
import io.opentelemetry.context.Context;
|
||||
import io.opentelemetry.context.propagation.TextMapSetter;
|
||||
|
@ -22,10 +23,8 @@ import software.amazon.awssdk.http.SdkHttpResponse;
|
|||
final class AwsSdkHttpClientTracer
|
||||
extends HttpClientTracer<SdkHttpRequest, SdkHttpRequest.Builder, SdkHttpResponse> {
|
||||
|
||||
private static final AwsSdkHttpClientTracer TRACER = new AwsSdkHttpClientTracer();
|
||||
|
||||
static AwsSdkHttpClientTracer tracer() {
|
||||
return TRACER;
|
||||
AwsSdkHttpClientTracer(OpenTelemetry openTelemetry) {
|
||||
super(openTelemetry);
|
||||
}
|
||||
|
||||
public Context startSpan(Context parentContext, ExecutionAttributes attributes) {
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.awssdk.v2_2;
|
||||
|
||||
import io.opentelemetry.api.OpenTelemetry;
|
||||
import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration;
|
||||
import software.amazon.awssdk.core.interceptor.ExecutionInterceptor;
|
||||
|
||||
/**
|
||||
* Entrypoint to OpenTelemetry instrumentation of the AWS SDK. Register the {@link
|
||||
* ExecutionInterceptor} returned by {@link #newExecutionInterceptor()} with an SDK client to have
|
||||
* all requests traced.
|
||||
*
|
||||
* <pre>{@code
|
||||
* DynamoDbClient dynamoDb = DynamoDbClient.builder()
|
||||
* .overrideConfiguration(ClientOverrideConfiguration.builder()
|
||||
* .addExecutionInterceptor(AwsSdkTracing.create(openTelemetry).newExecutionInterceptor())
|
||||
* .build())
|
||||
* .build();
|
||||
* }</pre>
|
||||
*/
|
||||
public class AwsSdkTracing {
|
||||
|
||||
/** Returns a new {@link AwsSdkTracing} configured with the given {@link OpenTelemetry}. */
|
||||
public static AwsSdkTracing create(OpenTelemetry openTelemetry) {
|
||||
return newBuilder(openTelemetry).build();
|
||||
}
|
||||
|
||||
/** Returns a new {@link AwsSdkTracingBuilder} configured with the given {@link OpenTelemetry}. */
|
||||
public static AwsSdkTracingBuilder newBuilder(OpenTelemetry openTelemetry) {
|
||||
return new AwsSdkTracingBuilder(openTelemetry);
|
||||
}
|
||||
|
||||
private final AwsSdkHttpClientTracer tracer;
|
||||
private final boolean captureExperimentalSpanAttributes;
|
||||
|
||||
AwsSdkTracing(OpenTelemetry openTelemetry, boolean captureExperimentalSpanAttributes) {
|
||||
this.tracer = new AwsSdkHttpClientTracer(openTelemetry);
|
||||
this.captureExperimentalSpanAttributes = captureExperimentalSpanAttributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new {@link ExecutionInterceptor} that can be used with methods like {@link
|
||||
* ClientOverrideConfiguration.Builder#addExecutionInterceptor(ExecutionInterceptor)}.
|
||||
*/
|
||||
public ExecutionInterceptor newExecutionInterceptor() {
|
||||
return new TracingExecutionInterceptor(tracer, captureExperimentalSpanAttributes);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.awssdk.v2_2;
|
||||
|
||||
import io.opentelemetry.api.OpenTelemetry;
|
||||
|
||||
/** A builder of {@link AwsSdkTracing}. */
|
||||
public final class AwsSdkTracingBuilder {
|
||||
|
||||
private final OpenTelemetry openTelemetry;
|
||||
|
||||
private boolean captureExperimentalSpanAttributes;
|
||||
|
||||
AwsSdkTracingBuilder(OpenTelemetry openTelemetry) {
|
||||
this.openTelemetry = openTelemetry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether experimental attributes should be set to spans. These attributes may be changed or
|
||||
* removed in the future, so only enable this if you know you do not require attributes filled by
|
||||
* this instrumentation to be stable across versions
|
||||
*/
|
||||
public AwsSdkTracingBuilder setCaptureExperimentalSpanAttributes(
|
||||
boolean captureExperimentalSpanAttributes) {
|
||||
this.captureExperimentalSpanAttributes = captureExperimentalSpanAttributes;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Returns a new {@link AwsSdkTracing} with the settings of this {@link AwsSdkTracingBuilder}. */
|
||||
public AwsSdkTracing build() {
|
||||
return new AwsSdkTracing(openTelemetry, captureExperimentalSpanAttributes);
|
||||
}
|
||||
}
|
|
@ -5,13 +5,10 @@
|
|||
|
||||
package io.opentelemetry.instrumentation.awssdk.v2_2;
|
||||
|
||||
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.AwsSdkRequestType.DynamoDB;
|
||||
|
||||
import io.opentelemetry.api.trace.Span;
|
||||
import io.opentelemetry.context.Scope;
|
||||
import io.opentelemetry.instrumentation.api.config.Config;
|
||||
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
|
||||
import software.amazon.awssdk.awscore.AwsResponse;
|
||||
import software.amazon.awssdk.core.ClientType;
|
||||
|
@ -27,10 +24,6 @@ import software.amazon.awssdk.http.SdkHttpRequest;
|
|||
/** AWS request execution interceptor. */
|
||||
final class TracingExecutionInterceptor implements ExecutionInterceptor {
|
||||
|
||||
private static final boolean CAPTURE_EXPERIMENTAL_SPAN_ATTRIBUTES =
|
||||
Config.get()
|
||||
.getBooleanProperty("otel.instrumentation.aws-sdk.experimental-span-attributes", false);
|
||||
|
||||
// the class name is part of the attribute name, so that it will be shaded when used in javaagent
|
||||
// instrumentation, and won't conflict with usage outside javaagent instrumentation
|
||||
static final ExecutionAttribute<io.opentelemetry.context.Context> CONTEXT_ATTRIBUTE =
|
||||
|
@ -42,17 +35,26 @@ final class TracingExecutionInterceptor implements ExecutionInterceptor {
|
|||
|
||||
static final String COMPONENT_NAME = "java-aws-sdk";
|
||||
|
||||
private final FieldMapper fieldMapper = new FieldMapper();
|
||||
private final AwsSdkHttpClientTracer tracer;
|
||||
private final boolean captureExperimentalSpanAttributes;
|
||||
private final FieldMapper fieldMapper;
|
||||
|
||||
TracingExecutionInterceptor(
|
||||
AwsSdkHttpClientTracer tracer, boolean captureExperimentalSpanAttributes) {
|
||||
this.tracer = tracer;
|
||||
this.captureExperimentalSpanAttributes = captureExperimentalSpanAttributes;
|
||||
fieldMapper = new FieldMapper();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeExecution(
|
||||
Context.BeforeExecution context, ExecutionAttributes executionAttributes) {
|
||||
io.opentelemetry.context.Context parentOtelContext = io.opentelemetry.context.Context.current();
|
||||
if (!tracer().shouldStartSpan(parentOtelContext)) {
|
||||
if (!tracer.shouldStartSpan(parentOtelContext)) {
|
||||
return;
|
||||
}
|
||||
io.opentelemetry.context.Context otelContext =
|
||||
tracer().startSpan(parentOtelContext, executionAttributes);
|
||||
tracer.startSpan(parentOtelContext, executionAttributes);
|
||||
executionAttributes.putAttribute(CONTEXT_ATTRIBUTE, otelContext);
|
||||
if (executionAttributes
|
||||
.getAttribute(SdkExecutionAttribute.CLIENT_TYPE)
|
||||
|
@ -72,7 +74,7 @@ final class TracingExecutionInterceptor implements ExecutionInterceptor {
|
|||
}
|
||||
|
||||
SdkHttpRequest.Builder builder = context.httpRequest().toBuilder();
|
||||
tracer().inject(otelContext, builder);
|
||||
tracer.inject(otelContext, builder);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
|
@ -85,7 +87,7 @@ final class TracingExecutionInterceptor implements ExecutionInterceptor {
|
|||
}
|
||||
|
||||
Span span = Span.fromContext(otelContext);
|
||||
tracer().onRequest(span, context.httpRequest());
|
||||
tracer.onRequest(span, context.httpRequest());
|
||||
|
||||
AwsSdkRequest awsSdkRequest = AwsSdkRequest.ofSdkRequest(context.request());
|
||||
if (awsSdkRequest != null) {
|
||||
|
@ -113,7 +115,7 @@ final class TracingExecutionInterceptor implements ExecutionInterceptor {
|
|||
}
|
||||
|
||||
private void populateGenericAttributes(Span span, ExecutionAttributes attributes) {
|
||||
if (CAPTURE_EXPERIMENTAL_SPAN_ATTRIBUTES) {
|
||||
if (captureExperimentalSpanAttributes) {
|
||||
String awsServiceName = attributes.getAttribute(SdkExecutionAttribute.SERVICE_NAME);
|
||||
String awsOperation = attributes.getAttribute(SdkExecutionAttribute.OPERATION_NAME);
|
||||
|
||||
|
@ -135,18 +137,18 @@ final class TracingExecutionInterceptor implements ExecutionInterceptor {
|
|||
Span span = Span.fromContext(otelContext);
|
||||
onUserAgentHeaderAvailable(span, context.httpRequest());
|
||||
onSdkResponse(span, context.response(), executionAttributes);
|
||||
tracer().end(otelContext, context.httpResponse());
|
||||
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"));
|
||||
SemanticAttributes.HTTP_USER_AGENT, tracer.requestHeader(request, "User-Agent"));
|
||||
}
|
||||
|
||||
private void onSdkResponse(
|
||||
Span span, SdkResponse response, ExecutionAttributes executionAttributes) {
|
||||
if (CAPTURE_EXPERIMENTAL_SPAN_ATTRIBUTES) {
|
||||
if (captureExperimentalSpanAttributes) {
|
||||
if (response instanceof AwsResponse) {
|
||||
span.setAttribute("aws.requestId", ((AwsResponse) response).responseMetadata().requestId());
|
||||
}
|
||||
|
@ -162,11 +164,19 @@ final class TracingExecutionInterceptor implements ExecutionInterceptor {
|
|||
Context.FailedExecution context, ExecutionAttributes executionAttributes) {
|
||||
io.opentelemetry.context.Context otelContext = getContext(executionAttributes);
|
||||
clearAttributes(executionAttributes);
|
||||
tracer().endExceptionally(otelContext, context.exception());
|
||||
tracer.endExceptionally(otelContext, context.exception());
|
||||
}
|
||||
|
||||
private void clearAttributes(ExecutionAttributes executionAttributes) {
|
||||
executionAttributes.putAttribute(CONTEXT_ATTRIBUTE, null);
|
||||
executionAttributes.putAttribute(AWS_SDK_REQUEST_ATTRIBUTE, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Context} stored in the {@link ExecutionAttributes}, or {@code null} if there
|
||||
* is no operation set.
|
||||
*/
|
||||
private static io.opentelemetry.context.Context getContext(ExecutionAttributes attributes) {
|
||||
return attributes.getAttribute(CONTEXT_ATTRIBUTE);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,11 @@ class Aws2ClientTest extends AbstractAws2ClientTest implements LibraryTestTrait
|
|||
@Override
|
||||
void configureSdkClient(SdkClientBuilder builder) {
|
||||
builder.overrideConfiguration(ClientOverrideConfiguration.builder()
|
||||
.addExecutionInterceptor(AwsSdk.newInterceptor())
|
||||
.addExecutionInterceptor(
|
||||
AwsSdkTracing.newBuilder(getOpenTelemetry())
|
||||
.setCaptureExperimentalSpanAttributes(true)
|
||||
.build()
|
||||
.newExecutionInterceptor())
|
||||
.build())
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue