Skip to content

Commit

Permalink
Automatic AWS library instrumentor (open-telemetry#4607)
Browse files Browse the repository at this point in the history
Like AWS X-Ray, provide an instrumentor which automatically registers
opentelemetry instrumentation in the AWS SDK without any code changes.
Those instrumentors are separate libraries published as
opentelemetry-aws-sdk-1.11-instrumentor and opentelemetry-aws-sdk-2.2-instrumentor
  • Loading branch information
steven-aerts authored and RashmiRam committed May 23, 2022
1 parent 6204f5d commit e259ba2
Show file tree
Hide file tree
Showing 15 changed files with 192 additions and 48 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
plugins {
id("otel.library-instrumentation")
}

base.archivesName.set("${base.archivesName.get()}-autoconfigure")

dependencies {
implementation(project(":instrumentation:aws-sdk:aws-sdk-1.11:library"))

library("com.amazonaws:aws-java-sdk-core:1.11.0")

testImplementation(project(":instrumentation:aws-sdk:aws-sdk-1.11:testing"))

testLibrary("com.amazonaws:aws-java-sdk-s3:1.11.106")
testLibrary("com.amazonaws:aws-java-sdk-rds:1.11.106")
testLibrary("com.amazonaws:aws-java-sdk-ec2:1.11.106")
testLibrary("com.amazonaws:aws-java-sdk-kinesis:1.11.106")
testLibrary("com.amazonaws:aws-java-sdk-dynamodb:1.11.106")
testLibrary("com.amazonaws:aws-java-sdk-sns:1.11.106")
testLibrary("com.amazonaws:aws-java-sdk-sqs:1.11.106")
}

tasks.test {
systemProperty("otel.instrumentation.aws-sdk.experimental-span-attributes", "true")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.awssdk.v1_11.autoconfigure;

import com.amazonaws.AmazonWebServiceRequest;
import com.amazonaws.Request;
import com.amazonaws.Response;
import com.amazonaws.handlers.RequestHandler2;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.instrumentation.api.config.Config;
import io.opentelemetry.instrumentation.awssdk.v1_11.AwsSdkTracing;

/**
* A {@link RequestHandler2} for use as an SPI by the AWS SDK to automatically trace all requests.
*/
public class TracingRequestHandler extends RequestHandler2 {

private static final RequestHandler2 DELEGATE =
AwsSdkTracing.builder(GlobalOpenTelemetry.get())
.setCaptureExperimentalSpanAttributes(
Config.get()
.getBoolean("otel.instrumentation.aws-sdk.experimental-span-attributes", false))
.build()
.newRequestHandler();

@Override
public void beforeRequest(Request<?> request) {
DELEGATE.beforeRequest(request);
}

@Override
public AmazonWebServiceRequest beforeMarshalling(AmazonWebServiceRequest request) {
return DELEGATE.beforeMarshalling(request);
}

@Override
public void afterResponse(Request<?> request, Response<?> response) {
DELEGATE.afterResponse(request, response);
}

@Override
public void afterError(Request<?> request, Response<?> response, Exception e) {
DELEGATE.afterError(request, response, e);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
io.opentelemetry.instrumentation.awssdk.v1_11.autoconfigure.TracingRequestHandler
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.awssdk.v1_11.instrumentor

import io.opentelemetry.instrumentation.awssdk.v1_11.AbstractAws1ClientTest
import io.opentelemetry.instrumentation.test.LibraryTestTrait

class Aws1ClientTest extends AbstractAws1ClientTest implements LibraryTestTrait {
@Override
def configureClient(def client) {
return client
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.awssdk.v1_11.instrumentor

import com.amazonaws.services.sqs.AmazonSQSAsyncClientBuilder
import io.opentelemetry.instrumentation.awssdk.v1_11.AbstractSqsTracingTest
import io.opentelemetry.instrumentation.test.LibraryTestTrait

class SqsTracingTest extends AbstractSqsTracingTest implements LibraryTestTrait {
@Override
AmazonSQSAsyncClientBuilder configureClient(AmazonSQSAsyncClientBuilder client) {
return client
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ abstract class AbstractAws1ClientTest extends InstrumentationSpecification {
response != null

client.requestHandler2s != null
client.requestHandler2s.get(0).getClass().getSimpleName() == "TracingRequestHandler"
client.requestHandler2s.find{it.getClass().getSimpleName() == "TracingRequestHandler"} != null

assertTraces(1) {
trace(0, 1) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ muzzle {
}

dependencies {
implementation(project(":instrumentation:aws-sdk:aws-sdk-2.2:library"))
implementation(project(":instrumentation:aws-sdk:aws-sdk-2.2:library-autoconfigure"))

library("software.amazon.awssdk:aws-core:2.2.0")

Expand All @@ -31,3 +31,9 @@ tasks.withType<Test>().configureEach {
// TODO run tests both with and without experimental span attributes
jvmArgs("-Dotel.instrumentation.aws-sdk.experimental-span-attributes=true")
}

tasks.withType<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar>().configureEach {
mergeServiceFiles {
include("software/amazon/awssdk/global/handlers/execution.interceptors")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import static net.bytebuddy.matcher.ElementMatchers.named;

import com.google.auto.service.AutoService;
import io.opentelemetry.instrumentation.awssdk.v2_2.autoconfigure.TracingExecutionInterceptor;
import io.opentelemetry.javaagent.extension.instrumentation.HelperResourceBuilder;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
plugins {
id("otel.library-instrumentation")
}

base.archivesName.set("${base.archivesName.get()}-autoconfigure")

dependencies {
implementation(project(":instrumentation:aws-sdk:aws-sdk-2.2:library"))

library("software.amazon.awssdk:aws-core:2.2.0")
library("software.amazon.awssdk:aws-json-protocol:2.2.0")

testImplementation(project(":instrumentation:aws-sdk:aws-sdk-2.2:testing"))

latestDepTestLibrary("software.amazon.awssdk:kinesis:+")
}

tasks {
test {
systemProperty("otel.instrumentation.aws-sdk.experimental-span-attributes", true)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.instrumentation.awssdk.v2_2;
package io.opentelemetry.instrumentation.awssdk.v2_2.autoconfigure;

import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.instrumentation.api.config.Config;
Expand All @@ -15,145 +15,133 @@
import software.amazon.awssdk.core.SdkRequest;
import software.amazon.awssdk.core.SdkResponse;
import software.amazon.awssdk.core.async.AsyncRequestBody;
import software.amazon.awssdk.core.interceptor.Context.AfterExecution;
import software.amazon.awssdk.core.interceptor.Context.AfterMarshalling;
import software.amazon.awssdk.core.interceptor.Context.AfterTransmission;
import software.amazon.awssdk.core.interceptor.Context.AfterUnmarshalling;
import software.amazon.awssdk.core.interceptor.Context.BeforeExecution;
import software.amazon.awssdk.core.interceptor.Context.BeforeMarshalling;
import software.amazon.awssdk.core.interceptor.Context.BeforeTransmission;
import software.amazon.awssdk.core.interceptor.Context.BeforeUnmarshalling;
import software.amazon.awssdk.core.interceptor.Context.FailedExecution;
import software.amazon.awssdk.core.interceptor.Context.ModifyHttpRequest;
import software.amazon.awssdk.core.interceptor.Context.ModifyHttpResponse;
import software.amazon.awssdk.core.interceptor.Context.ModifyRequest;
import software.amazon.awssdk.core.interceptor.Context.ModifyResponse;
import software.amazon.awssdk.core.interceptor.Context;
import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
import software.amazon.awssdk.core.interceptor.ExecutionInterceptor;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.http.SdkHttpRequest;
import software.amazon.awssdk.http.SdkHttpResponse;

/**
* {@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.
* A {@link ExecutionInterceptor} for use as an SPI by the AWS SDK to automatically trace all
* requests.
*/
public class TracingExecutionInterceptor implements ExecutionInterceptor {

private static final boolean CAPTURE_EXPERIMENTAL_SPAN_ATTRIBUTES =
Config.get().getBoolean("otel.instrumentation.aws-sdk.experimental-span-attributes", false);

private final ExecutionInterceptor delegate;

public TracingExecutionInterceptor() {
delegate =
AwsSdkTracing.builder(GlobalOpenTelemetry.get())
.setCaptureExperimentalSpanAttributes(CAPTURE_EXPERIMENTAL_SPAN_ATTRIBUTES)
.build()
.newExecutionInterceptor();
}
private final ExecutionInterceptor delegate =
AwsSdkTracing.builder(GlobalOpenTelemetry.get())
.setCaptureExperimentalSpanAttributes(CAPTURE_EXPERIMENTAL_SPAN_ATTRIBUTES)
.build()
.newExecutionInterceptor();

@Override
public void beforeExecution(BeforeExecution context, ExecutionAttributes executionAttributes) {
public void beforeExecution(
Context.BeforeExecution context, ExecutionAttributes executionAttributes) {
delegate.beforeExecution(context, executionAttributes);
}

@Override
public SdkRequest modifyRequest(ModifyRequest context, ExecutionAttributes executionAttributes) {
public SdkRequest modifyRequest(
Context.ModifyRequest context, ExecutionAttributes executionAttributes) {
return delegate.modifyRequest(context, executionAttributes);
}

@Override
public void beforeMarshalling(
BeforeMarshalling context, ExecutionAttributes executionAttributes) {
Context.BeforeMarshalling context, ExecutionAttributes executionAttributes) {
delegate.beforeMarshalling(context, executionAttributes);
}

@Override
public void afterMarshalling(AfterMarshalling context, ExecutionAttributes executionAttributes) {
public void afterMarshalling(
Context.AfterMarshalling context, ExecutionAttributes executionAttributes) {
delegate.afterMarshalling(context, executionAttributes);
}

@Override
public SdkHttpRequest modifyHttpRequest(
ModifyHttpRequest context, ExecutionAttributes executionAttributes) {
Context.ModifyHttpRequest context, ExecutionAttributes executionAttributes) {
return delegate.modifyHttpRequest(context, executionAttributes);
}

@Override
public Optional<RequestBody> modifyHttpContent(
ModifyHttpRequest context, ExecutionAttributes executionAttributes) {
Context.ModifyHttpRequest context, ExecutionAttributes executionAttributes) {
return delegate.modifyHttpContent(context, executionAttributes);
}

@Override
public Optional<AsyncRequestBody> modifyAsyncHttpContent(
ModifyHttpRequest context, ExecutionAttributes executionAttributes) {
Context.ModifyHttpRequest context, ExecutionAttributes executionAttributes) {
return delegate.modifyAsyncHttpContent(context, executionAttributes);
}

@Override
public void beforeTransmission(
BeforeTransmission context, ExecutionAttributes executionAttributes) {
Context.BeforeTransmission context, ExecutionAttributes executionAttributes) {
delegate.beforeTransmission(context, executionAttributes);
}

@Override
public void afterTransmission(
AfterTransmission context, ExecutionAttributes executionAttributes) {
Context.AfterTransmission context, ExecutionAttributes executionAttributes) {
delegate.afterTransmission(context, executionAttributes);
}

@Override
public SdkHttpResponse modifyHttpResponse(
ModifyHttpResponse context, ExecutionAttributes executionAttributes) {
Context.ModifyHttpResponse context, ExecutionAttributes executionAttributes) {
return delegate.modifyHttpResponse(context, executionAttributes);
}

@Override
public Optional<Publisher<ByteBuffer>> modifyAsyncHttpResponseContent(
ModifyHttpResponse context, ExecutionAttributes executionAttributes) {
Context.ModifyHttpResponse context, ExecutionAttributes executionAttributes) {
return delegate.modifyAsyncHttpResponseContent(context, executionAttributes);
}

@Override
public Optional<InputStream> modifyHttpResponseContent(
ModifyHttpResponse context, ExecutionAttributes executionAttributes) {
Context.ModifyHttpResponse context, ExecutionAttributes executionAttributes) {
return delegate.modifyHttpResponseContent(context, executionAttributes);
}

@Override
public void beforeUnmarshalling(
BeforeUnmarshalling context, ExecutionAttributes executionAttributes) {
Context.BeforeUnmarshalling context, ExecutionAttributes executionAttributes) {
delegate.beforeUnmarshalling(context, executionAttributes);
}

@Override
public void afterUnmarshalling(
AfterUnmarshalling context, ExecutionAttributes executionAttributes) {
Context.AfterUnmarshalling context, ExecutionAttributes executionAttributes) {
delegate.afterUnmarshalling(context, executionAttributes);
}

@Override
public SdkResponse modifyResponse(
ModifyResponse context, ExecutionAttributes executionAttributes) {
Context.ModifyResponse context, ExecutionAttributes executionAttributes) {
return delegate.modifyResponse(context, executionAttributes);
}

@Override
public void afterExecution(AfterExecution context, ExecutionAttributes executionAttributes) {
public void afterExecution(
Context.AfterExecution context, ExecutionAttributes executionAttributes) {
delegate.afterExecution(context, executionAttributes);
}

@Override
public Throwable modifyException(
FailedExecution context, ExecutionAttributes executionAttributes) {
Context.FailedExecution context, ExecutionAttributes executionAttributes) {
return delegate.modifyException(context, executionAttributes);
}

@Override
public void onExecutionFailure(FailedExecution context, ExecutionAttributes executionAttributes) {
public void onExecutionFailure(
Context.FailedExecution context, ExecutionAttributes executionAttributes) {
delegate.onExecutionFailure(context, executionAttributes);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
io.opentelemetry.instrumentation.awssdk.v2_2.autoconfigure.TracingExecutionInterceptor
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package v2_2

import io.opentelemetry.instrumentation.awssdk.v2_2.AbstractAws2ClientTest
import io.opentelemetry.instrumentation.test.LibraryTestTrait
import software.amazon.awssdk.core.client.builder.SdkClientBuilder

class Aws2ClientTest extends AbstractAws2ClientTest implements LibraryTestTrait {
@Override
void configureSdkClient(SdkClientBuilder builder) {
}
}
4 changes: 3 additions & 1 deletion instrumentation/aws-sdk/aws-sdk-2.2/library/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ Instrumentation for [AWS Java SDK v2](https://github.com/aws/aws-sdk-java-v2).

## Usage

To register instrumentation on an SDK client, register the interceptor when creating it.
To instrument all AWS SDK clients include the `opentelemetry-aws-sdk-2.2-autoconfigure` submodule in your classpath.

To register instrumentation only on a specific SDK client, register the interceptor when creating it.

```java
DynamoDbClient client = DynamoDbClient.builder()
Expand Down
Loading

0 comments on commit e259ba2

Please sign in to comment.