Fix AWS version ranges
ADD
dd-java-agent/instrumentation/aws-java-sdk-1.11.0/aws-java-sdk-1.11.0.gradle
+apply plugin: 'version-scan'
+
+// Commented out because version scan doesn't catch the combination of tests.
+// HttpClientFactory is only present starting in 1.11.0
+// HandlerContextAware is added in 1.11.106
+// The combination of the two allow us to filter the ranges.
+//
+
+//versionScan {
+// group = "com.amazonaws"
+// module = "aws-java-sdk-core"
+// versions = "[1.11.0,1.11.106)"
+// verifyPresent = [
+// "com.amazonaws.http.client.HttpClientFactory": null,
+// ]
+// verifyMissing = [
+// "com.amazonaws.HandlerContextAware",
+// ]
+//}
+
+//versionScan {
+// group = "com.amazonaws"
+// module = "aws-java-sdk-core"
+// versions = "[1.11.0,)"
+// verifyPresent = [
+// "com.amazonaws.http.client.HttpClientFactory": null,
+// ]
+//}
+
+versionScan {
+ group = "com.amazonaws"
+ module = "aws-java-sdk-core"
+ versions = "[,1.11.106)"
+ verifyMissing = [
+ "com.amazonaws.HandlerContextAware",
+ ]
+}
+
+apply from: "${rootDir}/gradle/java.gradle"
+
+apply plugin: 'org.unbroken-dome.test-sets'
+
+testSets {
+ latestDepTest {
+ dirName = 'test'
+ }
+}
+
+// These classes use Ratpack which requires Java 8. (Currently also incompatible with Java 9.)
+testJava8Only += '**/AWSClientTest.class'
+
+dependencies {
+ compileOnly group: 'com.amazonaws', name: 'aws-java-sdk-core', version: '1.11.0'
+
+ compile project(':dd-java-agent:agent-tooling')
+
+ compile deps.bytebuddy
+ compile deps.opentracing
+ annotationProcessor deps.autoservice
+ implementation deps.autoservice
+
+ testCompile project(':dd-java-agent:testing')
+ // Include httpclient instrumentation for testing because it is a dependency for aws-sdk.
+ testCompile project(':dd-java-agent:instrumentation:apache-httpclient-4.3')
+ testCompile group: 'com.amazonaws', name: 'aws-java-sdk', version: '1.11.0'
+}
+
+configurations.latestDepTestCompile {
+ resolutionStrategy {
+ force group: 'com.amazonaws', name: 'aws-java-sdk', version: '1.11.105'
+ }
+}

ADD
dd-java-agent/instrumentation/aws-java-sdk-1.11.0/src/main/java/datadog/trace/instrumentation/aws/v0/AWSClientInstrumentation.java
+package datadog.trace.instrumentation.aws.v0;
+
+import static datadog.trace.agent.tooling.ClassLoaderMatcher.classLoaderHasClasses;
+import static net.bytebuddy.matcher.ElementMatchers.declaresField;
+import static net.bytebuddy.matcher.ElementMatchers.isAbstract;
+import static net.bytebuddy.matcher.ElementMatchers.isConstructor;
+import static net.bytebuddy.matcher.ElementMatchers.named;
+import static net.bytebuddy.matcher.ElementMatchers.not;
+
+import com.amazonaws.handlers.RequestHandler2;
+import com.google.auto.service.AutoService;
+import datadog.trace.agent.tooling.DDAdvice;
+import datadog.trace.agent.tooling.DDTransformers;
+import datadog.trace.agent.tooling.HelperInjector;
+import datadog.trace.agent.tooling.Instrumenter;
+import io.opentracing.util.GlobalTracer;
+import java.util.List;
+import net.bytebuddy.agent.builder.AgentBuilder;
+import net.bytebuddy.asm.Advice;
+
+/**
+ * This instrumentation might work with versions before 1.11.0, but this was the first version that
+ * is tested. It could possibly be extended earlier.
+ */
+@AutoService(Instrumenter.class)
+public final class AWSClientInstrumentation extends Instrumenter.Configurable {
+
+ public AWSClientInstrumentation() {
+ super("aws-sdk");
+ }
+
+ @Override
+ public AgentBuilder apply(final AgentBuilder agentBuilder) {
+ return agentBuilder
+ .type(
+ isAbstract()
+ .and(
+ named("com.amazonaws.AmazonWebServiceClient")
+ .and(declaresField(named("requestHandler2s")))),
+ classLoaderHasClasses("com.amazonaws.http.client.HttpClientFactory")
+ .and(
+ not(
+ classLoaderHasClasses(
+ "com.amazonaws.client.builder.AwsClientBuilder$EndpointConfiguration"))))
+ .transform(
+ new HelperInjector(
+ "datadog.trace.instrumentation.aws.v0.TracingRequestHandler",
+ "datadog.trace.instrumentation.aws.v0.SpanDecorator"))
+ .transform(DDTransformers.defaultTransformers())
+ .transform(DDAdvice.create().advice(isConstructor(), AWSClientAdvice.class.getName()))
+ .asDecorator();
+ }
+
+ public static class AWSClientAdvice {
+ // Since we're instrumenting the constructor, we can't add onThrowable.
+ @Advice.OnMethodExit(suppress = Throwable.class)
+ public static void addHandler(
+ @Advice.FieldValue("requestHandler2s") final List<RequestHandler2> handlers) {
+ boolean hasDDHandler = false;
+ for (final RequestHandler2 handler : handlers) {
+ if (handler instanceof TracingRequestHandler) {
+ hasDDHandler = true;
+ break;
+ }
+ }
+ if (!hasDDHandler) {
+ handlers.add(new TracingRequestHandler(GlobalTracer.get()));
+ }
+ }
+ }
+}

RENAME
dd-java-agent/instrumentation/aws-sdk/src/main/java/datadog/trace/instrumentation/aws/SpanDecorator.java
dd-java-agent/instrumentation/aws-java-sdk-1.11.0/src/main/java/datadog/trace/instrumentation/aws/v0/SpanDecorator.java
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
-package datadog.trace.instrumentation.aws;
+package datadog.trace.instrumentation.aws.v0;
import com.amazonaws.AmazonWebServiceResponse;
import com.amazonaws.Request;

RENAME
dd-java-agent/instrumentation/aws-sdk/src/main/java/datadog/trace/instrumentation/aws/TracingRequestHandler.java
dd-java-agent/instrumentation/aws-java-sdk-1.11.0/src/main/java/datadog/trace/instrumentation/aws/v0/TracingRequestHandler.java
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
-package datadog.trace.instrumentation.aws;
+package datadog.trace.instrumentation.aws.v0;
import com.amazonaws.AmazonWebServiceRequest;
import com.amazonaws.Request;
@@ -48,11 +48,6 @@ public class TracingRequestHandler extends RequestHandler2 {
this.tracer = tracer;
}
- @Override
- public AmazonWebServiceRequest beforeExecution(final AmazonWebServiceRequest request) {
- return request;
- }
-
@Override
public AmazonWebServiceRequest beforeMarshalling(final AmazonWebServiceRequest request) {
return request;

ADD
dd-java-agent/instrumentation/aws-java-sdk-1.11.0/src/test/groovy/AWSClientTest.groovy
+import com.amazonaws.SDKGlobalConfiguration
+import com.amazonaws.auth.BasicAWSCredentials
+import com.amazonaws.handlers.RequestHandler2
+import com.amazonaws.services.ec2.AmazonEC2Client
+import com.amazonaws.services.rds.AmazonRDSClient
+import com.amazonaws.services.rds.model.DeleteOptionGroupRequest
+import com.amazonaws.services.s3.AmazonS3Client
+import com.amazonaws.services.s3.S3ClientOptions
+import datadog.trace.agent.test.AgentTestRunner
+import datadog.trace.api.DDTags
+import io.opentracing.tag.Tags
+import ratpack.http.Headers
+import spock.lang.Shared
+
+import java.util.concurrent.atomic.AtomicReference
+
+import static ratpack.groovy.test.embed.GroovyEmbeddedApp.ratpack
+
+class AWSClientTest extends AgentTestRunner {
+ def setupSpec() {
+ System.setProperty(SDKGlobalConfiguration.ACCESS_KEY_SYSTEM_PROPERTY, "my-access-key")
+ System.setProperty(SDKGlobalConfiguration.SECRET_KEY_SYSTEM_PROPERTY, "my-secret-key")
+ }
+
+ def cleanupSpec() {
+ System.clearProperty(SDKGlobalConfiguration.ACCESS_KEY_SYSTEM_PROPERTY)
+ System.clearProperty(SDKGlobalConfiguration.SECRET_KEY_SYSTEM_PROPERTY)
+ }
+
+ @Shared
+ def receivedHeaders = new AtomicReference<Headers>()
+ @Shared
+ def responseBody = new AtomicReference<String>()
+ @Shared
+ def server = ratpack {
+ handlers {
+ all {
+ receivedHeaders.set(request.headers)
+ response.status(200).send(responseBody.get())
+ }
+ }
+ }
+
+ def "request handler is hooked up with constructor"() {
+ setup:
+ String accessKey = "asdf"
+ String secretKey = "qwerty"
+ def credentials = new BasicAWSCredentials(accessKey, secretKey)
+ def client = new AmazonS3Client(credentials)
+ if (addHandler) {
+ client.addRequestHandler(new RequestHandler2() {})
+ }
+
+ expect:
+ client.requestHandler2s != null
+ client.requestHandler2s.size() == size
+ client.requestHandler2s.get(0).getClass().getSimpleName() == "TracingRequestHandler"
+
+ where:
+ addHandler | size
+ true | 2
+ false | 1
+ }
+
+ def "send #operation request with mocked response"() {
+ setup:
+ responseBody.set(body)
+ def response = call.call(client)
+
+ expect:
+ response != null
+
+ client.requestHandler2s != null
+ client.requestHandler2s.size() == handlerCount
+ client.requestHandler2s.get(0).getClass().getSimpleName() == "TracingRequestHandler"
+
+ TEST_WRITER.size() == 2
+
+ def trace = TEST_WRITER.get(0)
+ trace.size() == 2
+
+ and: // span 0 - from apache-httpclient instrumentation
+ def span1 = trace[0]
+
+ span1.context().operationName == "apache.http"
+ span1.serviceName == "unnamed-java-app"
+ span1.resourceName == "apache.http"
+ span1.type == null
+ !span1.context().getErrorFlag()
+ span1.context().parentId == 0
+
+
+ def tags1 = span1.context().tags
+ tags1["component"] == "apache-httpclient"
+ tags1["thread.name"] != null
+ tags1["thread.id"] != null
+ tags1.size() == 3
+
+ and: // span 1 - from apache-httpclient instrumentation
+ def span2 = trace[1]
+
+ span2.context().operationName == "http.request"
+ span2.serviceName == "unnamed-java-app"
+ span2.resourceName == "$method /$url"
+ span2.type == "http"
+ !span2.context().getErrorFlag()
+ span2.context().parentId == span1.spanId
+
+
+ def tags2 = span2.context().tags
+ tags2[Tags.SPAN_KIND.key] == Tags.SPAN_KIND_CLIENT
+ tags2[Tags.HTTP_METHOD.key] == "$method"
+ tags2[Tags.HTTP_URL.key] == "http://localhost:$server.address.port/$url"
+ tags2[Tags.PEER_HOSTNAME.key] == "localhost"
+ tags2[Tags.PEER_PORT.key] == server.address.port
+ tags2[DDTags.THREAD_NAME] != null
+ tags2[DDTags.THREAD_ID] != null
+ tags2.size() == 9
+
+ and:
+
+ def trace2 = TEST_WRITER.get(1)
+ trace2.size() == 1
+
+ and: // span 0 - from aws instrumentation
+ def span = trace2[0]
+
+ span.context().operationName == "aws.http"
+ span.serviceName == "java-aws-sdk"
+ span.resourceName == "$service.$operation"
+ span.type == "web"
+ !span.context().getErrorFlag()
+ span.context().parentId == 0
+
+ def tags = span.context().tags
+ tags[Tags.COMPONENT.key] == "java-aws-sdk"
+ tags[Tags.SPAN_KIND.key] == Tags.SPAN_KIND_CLIENT
+ tags[Tags.HTTP_METHOD.key] == "$method"
+ tags[Tags.HTTP_URL.key] == "http://localhost:$server.address.port"
+ tags[Tags.HTTP_STATUS.key] == 200
+ tags["aws.service"] == "Amazon $service" || tags["aws.service"] == "Amazon$service"
+ tags["aws.endpoint"] == "http://localhost:$server.address.port"
+ tags["aws.operation"] == "${operation}Request"
+ tags["aws.agent"] == "java-aws-sdk"
+ tags["params"] == params
+ tags["span.type"] == "web"
+ tags["thread.name"] != null
+ tags["thread.id"] != null
+ tags.size() == 13
+
+ receivedHeaders.get().get("x-datadog-trace-id") == "$span.traceId"
+ receivedHeaders.get().get("x-datadog-parent-id") == "$span.spanId"
+
+ where:
+ service | operation | method | url | handlerCount | call | body | params | client
+ "S3" | "CreateBucket" | "PUT" | "testbucket/" | 1 | { client -> client.setS3ClientOptions(S3ClientOptions.builder().setPathStyleAccess(true).build()); client.createBucket("testbucket") } | "" | "{}" | new AmazonS3Client().withEndpoint("http://localhost:$server.address.port")
+ "S3" | "GetObject" | "GET" | "someBucket/someKey" | 1 | { client -> client.getObject("someBucket", "someKey") } | "" | "{}" | new AmazonS3Client().withEndpoint("http://localhost:$server.address.port")
+ "EC2" | "AllocateAddress" | "POST" | "" | 4 | { client -> client.allocateAddress() } | """
+ <AllocateAddressResponse xmlns="http://ec2.amazonaws.com/doc/2016-11-15/">
+ <requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId>
+ <publicIp>192.0.2.1</publicIp>
+ <domain>standard</domain>
+ </AllocateAddressResponse>
+ """ | "{Action=[AllocateAddress],Version=[2015-10-01]}" | new AmazonEC2Client().withEndpoint("http://localhost:$server.address.port")
+ "RDS" | "DeleteOptionGroup" | "POST" | "" | 1 | { client -> client.deleteOptionGroup(new DeleteOptionGroupRequest()) } | """
+ <DeleteOptionGroupResponse xmlns="http://rds.amazonaws.com/doc/2014-09-01/">
+ <ResponseMetadata>
+ <RequestId>0ac9cda2-bbf4-11d3-f92b-31fa5e8dbc99</RequestId>
+ </ResponseMetadata>
+ </DeleteOptionGroupResponse>
+ """ | "{Action=[DeleteOptionGroup],Version=[2014-10-31]}" | new AmazonRDSClient().withEndpoint("http://localhost:$server.address.port")
+ }
+}

RENAME
dd-java-agent/instrumentation/aws-sdk/aws-sdk.gradle
dd-java-agent/instrumentation/aws-java-sdk-1.11.106/aws-java-sdk-1.11.106.gradle
-// This results in download a LOT of modules. commenting out for now...
-
apply plugin: 'version-scan'
versionScan {
group = "com.amazonaws"
module = "aws-java-sdk-core"
- versions = "[1.11.0,)"
+ versions = "[1.11.106,)"
verifyPresent = [
- "com.amazonaws.http.client.HttpClientFactory" : null,
- "com.amazonaws.http.apache.utils.ApacheUtils" : null,
- "com.amazonaws.http.request.HttpRequestFactory": null,
+ "com.amazonaws.HandlerContextAware": null,
]
}
@@ -27,7 +23,7 @@ testSets {
testJava8Only += '**/AWSClientTest.class'
dependencies {
- compileOnly group: 'com.amazonaws', name: 'aws-java-sdk-core', version: '1.11.119'
+ compileOnly group: 'com.amazonaws', name: 'aws-java-sdk-core', version: '1.11.106'
compile project(':dd-java-agent:agent-tooling')
@@ -39,7 +35,11 @@ dependencies {
testCompile project(':dd-java-agent:testing')
// Include httpclient instrumentation for testing because it is a dependency for aws-sdk.
testCompile project(':dd-java-agent:instrumentation:apache-httpclient-4.3')
- testCompile group: 'com.amazonaws', name: 'aws-java-sdk', version: '1.11.119'
+ testCompile group: 'com.amazonaws', name: 'aws-java-sdk', version: '1.11.106'
+}
- latestDepTestCompile group: 'com.amazonaws', name: 'aws-java-sdk', version: '+'
+configurations.latestDepTestCompile {
+ resolutionStrategy {
+ force group: 'com.amazonaws', name: 'aws-java-sdk', version: '+'
+ }
}

RENAME
dd-java-agent/instrumentation/aws-sdk/src/main/java/datadog/trace/instrumentation/aws/AWSClientInstrumentation.java
dd-java-agent/instrumentation/aws-java-sdk-1.11.106/src/main/java/datadog/trace/instrumentation/aws/v106/AWSClientInstrumentation.java
-package datadog.trace.instrumentation.aws;
+package datadog.trace.instrumentation.aws.v106;
import static datadog.trace.agent.tooling.ClassLoaderMatcher.classLoaderHasClasses;
import static net.bytebuddy.matcher.ElementMatchers.declaresField;
@@ -17,6 +17,11 @@ import java.util.List;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.Advice;
+/**
+ * The interface for com.amazonaws.Request changed in 106. The method addHandlerContext which is
+ * used in TracingRequestHandler moved to a parent interface, which makes it source, but not
+ * bytecode compatible. The instrumentation is the same, but the compiled output is different.
+ */
@AutoService(Instrumenter.class)
public final class AWSClientInstrumentation extends Instrumenter.Configurable {
@@ -32,16 +37,11 @@ public final class AWSClientInstrumentation extends Instrumenter.Configurable {
.and(
named("com.amazonaws.AmazonWebServiceClient")
.and(declaresField(named("requestHandler2s")))),
- classLoaderHasClasses(
- // aws classes used by opentracing contrib helpers
- "com.amazonaws.handlers.RequestHandler2",
- "com.amazonaws.Request",
- "com.amazonaws.Response",
- "com.amazonaws.handlers.HandlerContextKey"))
+ classLoaderHasClasses("com.amazonaws.HandlerContextAware"))
.transform(
new HelperInjector(
- "datadog.trace.instrumentation.aws.TracingRequestHandler",
- "datadog.trace.instrumentation.aws.SpanDecorator"))
+ "datadog.trace.instrumentation.aws.v106.TracingRequestHandler",
+ "datadog.trace.instrumentation.aws.v106.SpanDecorator"))
.transform(DDTransformers.defaultTransformers())
.transform(DDAdvice.create().advice(isConstructor(), AWSClientAdvice.class.getName()))
.asDecorator();

ADD
dd-java-agent/instrumentation/aws-java-sdk-1.11.106/src/main/java/datadog/trace/instrumentation/aws/v106/SpanDecorator.java
+/*
+ * Copyright 2017-2018 The OpenTracing Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package datadog.trace.instrumentation.aws.v106;
+
+import com.amazonaws.AmazonWebServiceResponse;
+import com.amazonaws.Request;
+import com.amazonaws.Response;
+import datadog.trace.api.DDTags;
+import io.opentracing.Span;
+import io.opentracing.tag.Tags;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.ConcurrentHashMap;
+
+class SpanDecorator {
+ static final String COMPONENT_NAME = "java-aws-sdk";
+
+ private static final Map<String, String> SERVICE_NAMES = new ConcurrentHashMap<>();
+ private static final Map<Class, String> OPERATION_NAMES = new ConcurrentHashMap<>();
+
+ static void onRequest(final Request<?> request, final Span span) {
+ Tags.COMPONENT.set(span, COMPONENT_NAME);
+ Tags.HTTP_METHOD.set(span, request.getHttpMethod().name());
+ Tags.HTTP_URL.set(span, request.getEndpoint().toString());
+
+ final String awsServiceName = request.getServiceName();
+ final Class<?> awsOperation = request.getOriginalRequest().getClass();
+
+ span.setTag("aws.agent", COMPONENT_NAME);
+ span.setTag("aws.service", awsServiceName);
+ span.setTag("aws.operation", awsOperation.getSimpleName());
+ span.setTag("aws.endpoint", request.getEndpoint().toString());
+
+ span.setTag(
+ DDTags.RESOURCE_NAME,
+ remapServiceName(awsServiceName) + "." + remapOperationName(awsOperation));
+
+ try {
+ final StringBuilder params = new StringBuilder("{");
+ final Map<String, List<String>> requestParams = request.getParameters();
+ boolean firstKey = true;
+ for (final Entry<String, List<String>> entry : requestParams.entrySet()) {
+ if (!firstKey) {
+ params.append(",");
+ }
+ params.append(entry.getKey()).append("=[");
+ for (int i = 0; i < entry.getValue().size(); ++i) {
+ if (i > 0) {
+ params.append(",");
+ }
+ params.append(entry.getValue().get(i));
+ }
+ params.append("]");
+ firstKey = false;
+ }
+ params.append("}");
+ span.setTag("params", params.toString());
+ } catch (final Exception e) {
+ try {
+ org.slf4j.LoggerFactory.getLogger(SpanDecorator.class)
+ .debug("Failed to decorate aws span", e);
+ } catch (final Exception e2) {
+ // can't reach logger. Silently eat excetpion.
+ }
+ }
+ }
+
+ static void onResponse(final Response response, final Span span) {
+ Tags.HTTP_STATUS.set(span, response.getHttpResponse().getStatusCode());
+ if (response.getAwsResponse() instanceof AmazonWebServiceResponse) {
+ final AmazonWebServiceResponse awsResp = (AmazonWebServiceResponse) response.getAwsResponse();
+ span.setTag("aws.requestId", awsResp.getRequestId());
+ }
+ }
+
+ static void onError(final Throwable throwable, final Span span) {
+ Tags.ERROR.set(span, Boolean.TRUE);
+ span.log(errorLogs(throwable));
+ }
+
+ private static Map<String, Object> errorLogs(final Throwable throwable) {
+ final Map<String, Object> errorLogs = new HashMap<>(4);
+ errorLogs.put("event", Tags.ERROR.getKey());
+ errorLogs.put("error.kind", throwable.getClass().getName());
+ errorLogs.put("error.object", throwable);
+
+ errorLogs.put("message", throwable.getMessage());
+
+ final StringWriter sw = new StringWriter();
+ throwable.printStackTrace(new PrintWriter(sw));
+ errorLogs.put("stack", sw.toString());
+
+ return errorLogs;
+ }
+
+ private static String remapServiceName(final String serviceName) {
+ if (!SERVICE_NAMES.containsKey(serviceName)) {
+ SERVICE_NAMES.put(serviceName, serviceName.replace("Amazon", "").trim());
+ }
+ return SERVICE_NAMES.get(serviceName);
+ }
+
+ private static String remapOperationName(final Class<?> awsOperation) {
+ if (!OPERATION_NAMES.containsKey(awsOperation)) {
+ OPERATION_NAMES.put(awsOperation, awsOperation.getSimpleName().replace("Request", ""));
+ }
+ return OPERATION_NAMES.get(awsOperation);
+ }
+}

ADD
dd-java-agent/instrumentation/aws-java-sdk-1.11.106/src/main/java/datadog/trace/instrumentation/aws/v106/TracingRequestHandler.java
+/*
+ * Copyright 2017-2018 The OpenTracing Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package datadog.trace.instrumentation.aws.v106;
+
+import com.amazonaws.AmazonWebServiceRequest;
+import com.amazonaws.Request;
+import com.amazonaws.Response;
+import com.amazonaws.handlers.HandlerContextKey;
+import com.amazonaws.handlers.RequestHandler2;
+import io.opentracing.Span;
+import io.opentracing.SpanContext;
+import io.opentracing.Tracer;
+import io.opentracing.propagation.Format;
+import io.opentracing.propagation.TextMapInjectAdapter;
+import io.opentracing.tag.Tags;
+
+/** Tracing Request Handler */
+public class TracingRequestHandler extends RequestHandler2 {
+
+ private final HandlerContextKey<Span> contextKey = new HandlerContextKey<>("span");
+ private final SpanContext parentContext; // for Async Client
+ private final Tracer tracer;
+
+ public TracingRequestHandler(final Tracer tracer) {
+ this.parentContext = null;
+ this.tracer = tracer;
+ }
+
+ /**
+ * In case of Async Client: beforeRequest runs in separate thread therefore we need to inject
+ * parent context to build chain
+ *
+ * @param parentContext parent context
+ */
+ public TracingRequestHandler(final SpanContext parentContext, final Tracer tracer) {
+ this.parentContext = parentContext;
+ this.tracer = tracer;
+ }
+
+ @Override
+ public AmazonWebServiceRequest beforeMarshalling(final AmazonWebServiceRequest request) {
+ return request;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void beforeRequest(final Request<?> request) {
+ final Tracer.SpanBuilder spanBuilder =
+ tracer.buildSpan("aws.command").withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT);
+
+ if (parentContext != null) {
+ spanBuilder.asChildOf(parentContext);
+ }
+
+ final Span span = spanBuilder.start();
+ SpanDecorator.onRequest(request, span);
+
+ tracer.inject(
+ span.context(),
+ Format.Builtin.HTTP_HEADERS,
+ new TextMapInjectAdapter(request.getHeaders()));
+
+ request.addHandlerContext(contextKey, span);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void afterResponse(final Request<?> request, final Response<?> response) {
+ final Span span = request.getHandlerContext(contextKey);
+ SpanDecorator.onResponse(response, span);
+ span.finish();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void afterError(final Request<?> request, final Response<?> response, final Exception e) {
+ final Span span = request.getHandlerContext(contextKey);
+ SpanDecorator.onError(e, span);
+ span.finish();
+ }
+}

RENAME
dd-java-agent/instrumentation/aws-sdk/src/test/groovy/AWSClientTest.groovy
dd-java-agent/instrumentation/aws-java-sdk-1.11.106/src/test/groovy/AWSClientTest.groovy
import com.amazonaws.AmazonWebServiceClient
+import com.amazonaws.SDKGlobalConfiguration
import com.amazonaws.auth.AWSStaticCredentialsProvider
import com.amazonaws.auth.AnonymousAWSCredentials
import com.amazonaws.auth.BasicAWSCredentials
@@ -21,6 +22,16 @@ import java.util.concurrent.atomic.AtomicReference
import static ratpack.groovy.test.embed.GroovyEmbeddedApp.ratpack
class AWSClientTest extends AgentTestRunner {
+ def setupSpec() {
+ System.setProperty(SDKGlobalConfiguration.ACCESS_KEY_SYSTEM_PROPERTY, "my-access-key")
+ System.setProperty(SDKGlobalConfiguration.SECRET_KEY_SYSTEM_PROPERTY, "my-secret-key")
+ }
+
+ def cleanupSpec() {
+ System.clearProperty(SDKGlobalConfiguration.ACCESS_KEY_SYSTEM_PROPERTY)
+ System.clearProperty(SDKGlobalConfiguration.SECRET_KEY_SYSTEM_PROPERTY)
+ }
+
@Shared
def credentialsProvider = new AWSStaticCredentialsProvider(new AnonymousAWSCredentials())
@Shared

MODIFY
settings.gradle
settings.gradle
@@ -10,7 +10,8 @@ include ':dd-trace-api'
// instrumentation:
include ':dd-java-agent:instrumentation:apache-httpclient-4.3'
-include ':dd-java-agent:instrumentation:aws-sdk'
+include ':dd-java-agent:instrumentation:aws-java-sdk-1.11.0'
+include ':dd-java-agent:instrumentation:aws-java-sdk-1.11.106'
include ':dd-java-agent:instrumentation:classloaders'
include ':dd-java-agent:instrumentation:datastax-cassandra-3.2'
include ':dd-java-agent:instrumentation:hystrix-1.4'

Fix AWS version ranges