Add additional tags for aws-sdk spans

s3 -> bucket name
dynamodb -> table name
sqs -> queue name/url
kinesis -> stream name
This commit is contained in:
Tyler Benson 2019-09-04 12:41:45 -07:00
parent b20a32b44d
commit c410f5b78b
4 changed files with 134 additions and 36 deletions

View File

@ -32,12 +32,19 @@ dependencies {
testCompile group: 'software.amazon.awssdk', name: 's3', version: '2.2.0'
testCompile group: 'software.amazon.awssdk', name: 'rds', version: '2.2.0'
testCompile group: 'software.amazon.awssdk', name: 'ec2', version: '2.2.0'
testCompile group: 'software.amazon.awssdk', name: 'sqs', version: '2.2.0'
testCompile group: 'software.amazon.awssdk', name: 'dynamodb', version: '2.2.0'
testCompile group: 'software.amazon.awssdk', name: 'kinesis', version: '2.2.0'
latestDepTestCompile project(':dd-java-agent:instrumentation:apache-httpclient-4')
latestDepTestCompile project(':dd-java-agent:instrumentation:netty-4.1')
latestDepTestCompile project(':dd-java-agent:instrumentation:java-concurrent')
latestDepTestCompile group: 'software.amazon.awssdk', name: 'apache-client', version: '+'
latestDepTestCompile group: 'software.amazon.awssdk', name: 's3', version: '+'
latestDepTestCompile group: 'software.amazon.awssdk', name: 'rds', version: '+'
latestDepTestCompile group: 'software.amazon.awssdk', name: 'ec2', version: '+'
latestDepTestCompile group: 'software.amazon.awssdk', name: 'sqs', version: '+'
latestDepTestCompile group: 'software.amazon.awssdk', name: 'dynamodb', version: '+'
latestDepTestCompile group: 'software.amazon.awssdk', name: 'kinesis', version: '+'
}

View File

@ -5,6 +5,7 @@ import datadog.trace.api.DDTags;
import io.opentracing.Span;
import java.net.URI;
import software.amazon.awssdk.awscore.AwsResponse;
import software.amazon.awssdk.core.SdkRequest;
import software.amazon.awssdk.core.SdkResponse;
import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
import software.amazon.awssdk.core.interceptor.SdkExecutionAttribute;
@ -16,6 +17,29 @@ public class AwsSdkClientDecorator extends HttpClientDecorator<SdkHttpRequest, S
static final String COMPONENT_NAME = "java-aws-sdk";
public Span onSdkRequest(final Span span, final SdkRequest request) {
// S3
request
.getValueForField("Bucket", String.class)
.ifPresent(name -> span.setTag("aws.bucket.name", name));
// DynamoDB
request
.getValueForField("TableName", String.class)
.ifPresent(name -> span.setTag("aws.table.name", name));
// SQS
request
.getValueForField("QueueName", String.class)
.ifPresent(name -> span.setTag("aws.queue.name", name));
request
.getValueForField("QueueUrl", String.class)
.ifPresent(name -> span.setTag("aws.queue.url", name));
// Kinesis
request
.getValueForField("StreamName", String.class)
.ifPresent(name -> span.setTag("aws.stream.name", name));
return span;
}
public Span onAttributes(final Span span, final ExecutionAttributes attributes) {
final String awsServiceName = attributes.getAttribute(SdkExecutionAttribute.SERVICE_NAME);

View File

@ -44,9 +44,9 @@ public class TracingExecutionInterceptor implements ExecutionInterceptor {
public void afterMarshalling(
final Context.AfterMarshalling context, final ExecutionAttributes executionAttributes) {
final Span span = executionAttributes.getAttribute(SPAN_ATTRIBUTE);
final SdkHttpRequest httpRequest = context.httpRequest();
DECORATE.onRequest(span, httpRequest);
DECORATE.onRequest(span, context.httpRequest());
DECORATE.onSdkRequest(span, context.request());
DECORATE.onAttributes(span, executionAttributes);
}

View File

@ -3,12 +3,18 @@ import datadog.trace.api.DDSpanTypes
import io.opentracing.tag.Tags
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider
import software.amazon.awssdk.core.ResponseInputStream
import software.amazon.awssdk.core.async.AsyncResponseTransformer
import software.amazon.awssdk.core.exception.SdkClientException
import software.amazon.awssdk.http.apache.ApacheHttpClient
import software.amazon.awssdk.regions.Region
import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient
import software.amazon.awssdk.services.dynamodb.DynamoDbClient
import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest
import software.amazon.awssdk.services.ec2.Ec2AsyncClient
import software.amazon.awssdk.services.ec2.Ec2Client
import software.amazon.awssdk.services.kinesis.KinesisClient
import software.amazon.awssdk.services.kinesis.model.DeleteStreamRequest
import software.amazon.awssdk.services.rds.RdsAsyncClient
import software.amazon.awssdk.services.rds.RdsClient
import software.amazon.awssdk.services.rds.model.DeleteOptionGroupRequest
@ -16,6 +22,10 @@ import software.amazon.awssdk.services.s3.S3AsyncClient
import software.amazon.awssdk.services.s3.S3Client
import software.amazon.awssdk.services.s3.model.CreateBucketRequest
import software.amazon.awssdk.services.s3.model.GetObjectRequest
import software.amazon.awssdk.services.sqs.SqsAsyncClient
import software.amazon.awssdk.services.sqs.SqsClient
import software.amazon.awssdk.services.sqs.model.CreateQueueRequest
import software.amazon.awssdk.services.sqs.model.SendMessageRequest
import spock.lang.AutoCleanup
import spock.lang.Shared
@ -59,6 +69,7 @@ class AwsClientTest extends AgentTestRunner {
expect:
response != null
response.class.simpleName.startsWith(operation) || response instanceof ResponseInputStream
assertTraces(1) {
trace(0, 2) {
@ -81,6 +92,17 @@ class AwsClientTest extends AgentTestRunner {
"aws.operation" "${operation}"
"aws.agent" "java-aws-sdk"
"aws.requestId" "$requestId"
if (service == "S3") {
"aws.bucket.name" "somebucket"
} else if (service == "Sqs" && operation == "CreateQueue") {
"aws.queue.name" "somequeue"
} else if (service == "Sqs" && operation == "SendMessage") {
"aws.queue.url" "someurl"
} else if (service == "DynamoDb") {
"aws.table.name" "sometable"
} else if (service == "Kinesis") {
"aws.stream.name" "somestream"
}
defaultTags()
}
}
@ -107,23 +129,39 @@ class AwsClientTest extends AgentTestRunner {
server.lastRequest.headers.get("x-datadog-parent-id") == null
where:
service | operation | method | path | requestId | call | body | builder
"S3" | "CreateBucket" | "PUT" | "/testbucket" | "UNKNOWN" | { c -> c.createBucket(CreateBucketRequest.builder().bucket("testbucket").build()) } | "" | S3Client.builder()
"S3" | "GetObject" | "GET" | "/someBucket/someKey" | "UNKNOWN" | { c -> c.getObject(GetObjectRequest.builder().bucket("someBucket").key("someKey").build()) } | "" | S3Client.builder()
"Ec2" | "AllocateAddress" | "POST" | "/" | "59dbff89-35bd-4eac-99ed-be587EXAMPLE" | { c -> c.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>
""" | Ec2Client.builder()
"Rds" | "DeleteOptionGroup" | "POST" | "/" | "0ac9cda2-bbf4-11d3-f92b-31fa5e8dbc99" | { c -> c.deleteOptionGroup(DeleteOptionGroupRequest.builder().build()) } | """
service | operation | method | path | requestId | builder | call | body
"S3" | "CreateBucket" | "PUT" | "/somebucket" | "UNKNOWN" | S3Client.builder() | { c -> c.createBucket(CreateBucketRequest.builder().bucket("somebucket").build()) } | ""
"S3" | "GetObject" | "GET" | "/somebucket/somekey" | "UNKNOWN" | S3Client.builder() | { c -> c.getObject(GetObjectRequest.builder().bucket("somebucket").key("somekey").build()) } | ""
"DynamoDb" | "CreateTable" | "POST" | "/" | "UNKNOWN" | DynamoDbClient.builder() | { c -> c.createTable(CreateTableRequest.builder().tableName("sometable").build()) } | ""
"Kinesis" | "DeleteStream" | "POST" | "/" | "UNKNOWN" | KinesisClient.builder() | { c -> c.deleteStream(DeleteStreamRequest.builder().streamName("somestream").build()) } | ""
"Sqs" | "CreateQueue" | "POST" | "/" | "7a62c49f-347e-4fc4-9331-6e8e7a96aa73" | SqsClient.builder() | { c -> c.createQueue(CreateQueueRequest.builder().queueName("somequeue").build()) } | """
<CreateQueueResponse>
<CreateQueueResult><QueueUrl>https://queue.amazonaws.com/123456789012/MyQueue</QueueUrl></CreateQueueResult>
<ResponseMetadata><RequestId>7a62c49f-347e-4fc4-9331-6e8e7a96aa73</RequestId></ResponseMetadata>
</CreateQueueResponse>
"""
"Sqs" | "SendMessage" | "POST" | "/" | "27daac76-34dd-47df-bd01-1f6e873584a0" | SqsClient.builder() | { c -> c.sendMessage(SendMessageRequest.builder().queueUrl("someurl").messageBody("").build()) } | """
<SendMessageResponse>
<SendMessageResult>
<MD5OfMessageBody>d41d8cd98f00b204e9800998ecf8427e</MD5OfMessageBody>
<MD5OfMessageAttributes>3ae8f24a165a8cedc005670c81a27295</MD5OfMessageAttributes>
<MessageId>5fea7756-0ea4-451a-a703-a558b933e274</MessageId>
</SendMessageResult>
<ResponseMetadata><RequestId>27daac76-34dd-47df-bd01-1f6e873584a0</RequestId></ResponseMetadata>
</SendMessageResponse>
"""
"Ec2" | "AllocateAddress" | "POST" | "/" | "59dbff89-35bd-4eac-99ed-be587EXAMPLE" | Ec2Client.builder() | { c -> c.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>
"""
"Rds" | "DeleteOptionGroup" | "POST" | "/" | "0ac9cda2-bbf4-11d3-f92b-31fa5e8dbc99" | RdsClient.builder() | { c -> c.deleteOptionGroup(DeleteOptionGroupRequest.builder().build()) } | """
<DeleteOptionGroupResponse xmlns="http://rds.amazonaws.com/doc/2014-09-01/">
<ResponseMetadata>
<RequestId>0ac9cda2-bbf4-11d3-f92b-31fa5e8dbc99</RequestId>
</ResponseMetadata>
<ResponseMetadata><RequestId>0ac9cda2-bbf4-11d3-f92b-31fa5e8dbc99</RequestId></ResponseMetadata>
</DeleteOptionGroupResponse>
""" | RdsClient.builder()
"""
}
def "send #operation async request with builder {#builder.class.getName()} mocked response"() {
@ -171,6 +209,17 @@ class AwsClientTest extends AgentTestRunner {
"aws.operation" "${operation}"
"aws.agent" "java-aws-sdk"
"aws.requestId" "$requestId"
if (service == "S3") {
"aws.bucket.name" "somebucket"
} else if (service == "Sqs" && operation == "CreateQueue") {
"aws.queue.name" "somequeue"
} else if (service == "Sqs" && operation == "SendMessage") {
"aws.queue.url" "someurl"
} else if (service == "DynamoDb") {
"aws.table.name" "sometable"
} else if (service == "Kinesis") {
"aws.stream.name" "somestream"
}
defaultTags()
}
}
@ -201,23 +250,40 @@ class AwsClientTest extends AgentTestRunner {
server.lastRequest.headers.get("x-datadog-parent-id") == null
where:
service | operation | method | path | requestId | call | body | builder
"S3" | "CreateBucket" | "PUT" | "/testbucket" | "UNKNOWN" | { c -> c.createBucket(CreateBucketRequest.builder().bucket("testbucket").build()) } | "" | S3AsyncClient.builder()
"S3" | "GetObject" | "GET" | "/someBucket/someKey" | "UNKNOWN" | { c -> c.getObject(GetObjectRequest.builder().bucket("someBucket").key("someKey").build(), AsyncResponseTransformer.toBytes()) } | "1234567890" | S3AsyncClient.builder()
"Ec2" | "AllocateAddress" | "POST" | "/" | "59dbff89-35bd-4eac-99ed-be587EXAMPLE" | { c -> c.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>
""" | Ec2AsyncClient.builder()
"Rds" | "DeleteOptionGroup" | "POST" | "/" | "0ac9cda2-bbf4-11d3-f92b-31fa5e8dbc99" | { c -> c.deleteOptionGroup(DeleteOptionGroupRequest.builder().build()) } | """
service | operation | method | path | requestId | builder | call | body
"S3" | "CreateBucket" | "PUT" | "/somebucket" | "UNKNOWN" | S3AsyncClient.builder() | { c -> c.createBucket(CreateBucketRequest.builder().bucket("somebucket").build()) } | ""
"S3" | "GetObject" | "GET" | "/somebucket/somekey" | "UNKNOWN" | S3AsyncClient.builder() | { c -> c.getObject(GetObjectRequest.builder().bucket("somebucket").key("somekey").build(), AsyncResponseTransformer.toBytes()) } | "1234567890"
"DynamoDb" | "CreateTable" | "POST" | "/" | "UNKNOWN" | DynamoDbAsyncClient.builder() | { c -> c.createTable(CreateTableRequest.builder().tableName("sometable").build()) } | ""
// Kinesis seems to expect an http2 response which is incompatible with our test server.
// "Kinesis" | "DeleteStream" | "POST" | "/" | "UNKNOWN" | KinesisAsyncClient.builder() | { c -> c.deleteStream(DeleteStreamRequest.builder().streamName("somestream").build()) } | ""
"Sqs" | "CreateQueue" | "POST" | "/" | "7a62c49f-347e-4fc4-9331-6e8e7a96aa73" | SqsAsyncClient.builder() | { c -> c.createQueue(CreateQueueRequest.builder().queueName("somequeue").build()) } | """
<CreateQueueResponse>
<CreateQueueResult><QueueUrl>https://queue.amazonaws.com/123456789012/MyQueue</QueueUrl></CreateQueueResult>
<ResponseMetadata><RequestId>7a62c49f-347e-4fc4-9331-6e8e7a96aa73</RequestId></ResponseMetadata>
</CreateQueueResponse>
"""
"Sqs" | "SendMessage" | "POST" | "/" | "27daac76-34dd-47df-bd01-1f6e873584a0" | SqsAsyncClient.builder() | { c -> c.sendMessage(SendMessageRequest.builder().queueUrl("someurl").messageBody("").build()) } | """
<SendMessageResponse>
<SendMessageResult>
<MD5OfMessageBody>d41d8cd98f00b204e9800998ecf8427e</MD5OfMessageBody>
<MD5OfMessageAttributes>3ae8f24a165a8cedc005670c81a27295</MD5OfMessageAttributes>
<MessageId>5fea7756-0ea4-451a-a703-a558b933e274</MessageId>
</SendMessageResult>
<ResponseMetadata><RequestId>27daac76-34dd-47df-bd01-1f6e873584a0</RequestId></ResponseMetadata>
</SendMessageResponse>
"""
"Ec2" | "AllocateAddress" | "POST" | "/" | "59dbff89-35bd-4eac-99ed-be587EXAMPLE" | Ec2AsyncClient.builder() | { c -> c.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>
"""
"Rds" | "DeleteOptionGroup" | "POST" | "/" | "0ac9cda2-bbf4-11d3-f92b-31fa5e8dbc99" | RdsAsyncClient.builder() | { c -> c.deleteOptionGroup(DeleteOptionGroupRequest.builder().build()) } | """
<DeleteOptionGroupResponse xmlns="http://rds.amazonaws.com/doc/2014-09-01/">
<ResponseMetadata>
<RequestId>0ac9cda2-bbf4-11d3-f92b-31fa5e8dbc99</RequestId>
</ResponseMetadata>
<ResponseMetadata><RequestId>0ac9cda2-bbf4-11d3-f92b-31fa5e8dbc99</RequestId></ResponseMetadata>
</DeleteOptionGroupResponse>
""" | RdsAsyncClient.builder()
"""
}
def "timeout and retry errors captured"() {
@ -238,7 +304,7 @@ class AwsClientTest extends AgentTestRunner {
.build()
when:
client.getObject(GetObjectRequest.builder().bucket("someBucket").key("someKey").build())
client.getObject(GetObjectRequest.builder().bucket("somebucket").key("somekey").build())
then:
thrown SdkClientException
@ -254,7 +320,7 @@ class AwsClientTest extends AgentTestRunner {
parent()
tags {
"$Tags.COMPONENT.key" "java-aws-sdk"
"$Tags.HTTP_URL.key" "$server.address/someBucket/someKey"
"$Tags.HTTP_URL.key" "$server.address/somebucket/somekey"
"$Tags.HTTP_METHOD.key" "GET"
"$Tags.PEER_HOSTNAME.key" "localhost"
"$Tags.PEER_PORT.key" server.address.port
@ -262,6 +328,7 @@ class AwsClientTest extends AgentTestRunner {
"aws.service" "S3"
"aws.operation" "GetObject"
"aws.agent" "java-aws-sdk"
"aws.bucket.name" "somebucket"
errorTags SdkClientException, "Unable to execute HTTP request: Read timed out"
defaultTags()
}
@ -269,13 +336,13 @@ class AwsClientTest extends AgentTestRunner {
(1..4).each {
span(it) {
operationName "http.request"
resourceName "GET /someBucket/someKey"
resourceName "GET /somebucket/somekey"
spanType DDSpanTypes.HTTP_CLIENT
errored true
childOf(span(0))
tags {
"$Tags.COMPONENT.key" "apache-httpclient"
"$Tags.HTTP_URL.key" "$server.address/someBucket/someKey"
"$Tags.HTTP_URL.key" "$server.address/somebucket/somekey"
"$Tags.PEER_HOSTNAME.key" "localhost"
"$Tags.PEER_PORT.key" server.address.port
"$Tags.HTTP_METHOD.key" "GET"