Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,17 @@ public void afterExecution(AfterExecution context, ExecutionAttributes execution
}

SdkRequest request = context.request();
String tableName = request.getValueForField("TableName", String.class).orElse(null);
Map<String, AttributeValue> keys = null;

if (request instanceof UpdateItemRequest) {
Map<String, AttributeValue> keys = ((UpdateItemRequest) request).key();
DynamoDbUtil.exportTagsWithKnownKeys(span, keys);
keys = ((UpdateItemRequest) request).key();
} else if (request instanceof DeleteItemRequest) {
Map<String, AttributeValue> keys = ((DeleteItemRequest) request).key();
DynamoDbUtil.exportTagsWithKnownKeys(span, keys);
keys = ((DeleteItemRequest) request).key();
}

if (keys != null) {
DynamoDbUtil.addSpanPointer(span, tableName, keys);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
package datadog.trace.instrumentation.aws.v2.dynamodb;

import static datadog.trace.bootstrap.instrumentation.api.InstrumentationTags.DYNAMO_PRIMARY_KEY_1;
import static datadog.trace.bootstrap.instrumentation.api.InstrumentationTags.DYNAMO_PRIMARY_KEY_1_VALUE;
import static datadog.trace.bootstrap.instrumentation.api.InstrumentationTags.DYNAMO_PRIMARY_KEY_2;
import static datadog.trace.bootstrap.instrumentation.api.InstrumentationTags.DYNAMO_PRIMARY_KEY_2_VALUE;

import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
import datadog.trace.bootstrap.instrumentation.api.SpanPointerUtils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
Expand Down Expand Up @@ -37,36 +33,42 @@ private static String extractValueAsString(AttributeValue value) {
}

/**
* Gets primary key/values and exports them as temporary tags on the span so that
* SpanPointersProcessor.java can complete the span pointer creation.
* Creates a DynamoDB span pointer link from the given primary keys and adds it to the span.
*
* @param span The span to set the temporary tags on
* @param span The span to add the pointer link to
* @param tableName The DynamoDB table name
* @param keys The primary key/values to extract from
*/
static void exportTagsWithKnownKeys(AgentSpan span, Map<String, AttributeValue> keys) {
if (keys == null || keys.isEmpty()) {
static void addSpanPointer(AgentSpan span, String tableName, Map<String, AttributeValue> keys) {
if (keys == null || keys.isEmpty() || tableName == null) {
return;
}

String primaryKey1Name;
String primaryKey1Value;
String primaryKey2Name = null;
String primaryKey2Value = null;

if (keys.size() == 1) {
// Single primary key case
Map.Entry<String, AttributeValue> entry = keys.entrySet().iterator().next();
span.setTag(DYNAMO_PRIMARY_KEY_1, entry.getKey());
span.setTag(DYNAMO_PRIMARY_KEY_1_VALUE, extractValueAsString(entry.getValue()));
primaryKey1Name = entry.getKey();
primaryKey1Value = extractValueAsString(entry.getValue());
} else {
// Sort keys alphabetically
List<String> keyNames = new ArrayList<>(keys.keySet());
Collections.sort(keyNames);

// First key (alphabetically)
String primaryKey1Name = keyNames.get(0);
span.setTag(DYNAMO_PRIMARY_KEY_1, primaryKey1Name);
span.setTag(DYNAMO_PRIMARY_KEY_1_VALUE, extractValueAsString(keys.get(primaryKey1Name)));
primaryKey1Name = keyNames.get(0);
primaryKey1Value = extractValueAsString(keys.get(primaryKey1Name));

// Second key
String primaryKey2Name = keyNames.get(1);
span.setTag(DYNAMO_PRIMARY_KEY_2, primaryKey2Name);
span.setTag(DYNAMO_PRIMARY_KEY_2_VALUE, extractValueAsString(keys.get(primaryKey2Name)));
primaryKey2Name = keyNames.get(1);
primaryKey2Value = extractValueAsString(keys.get(primaryKey2Name));
}

SpanPointerUtils.addDynamoDbSpanPointer(
span, tableName, primaryKey1Name, primaryKey1Value, primaryKey2Name, primaryKey2Value);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import datadog.trace.api.DDSpanTypes
import datadog.trace.api.DDTraceId
import datadog.trace.bootstrap.instrumentation.api.SpanAttributes
import datadog.trace.bootstrap.instrumentation.api.SpanLink
import datadog.trace.core.tagprocessor.SpanPointersProcessor
import datadog.trace.bootstrap.instrumentation.api.SpanPointerUtils
import org.testcontainers.containers.GenericContainer
import org.testcontainers.utility.DockerImageName
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials
Expand Down Expand Up @@ -193,11 +193,11 @@ class DynamoDbClientTest extends InstrumentationSpecification {
spanType DDSpanTypes.HTTP_CLIENT
links {
link(DDTraceId.ZERO, 0, SpanLink.DEFAULT_FLAGS, SpanAttributes.builder()
.put("ptr.kind", SpanPointersProcessor.DYNAMODB_PTR_KIND)
.put("ptr.dir", SpanPointersProcessor.DOWN_DIRECTION)
.put("ptr.kind", SpanPointerUtils.DYNAMODB_PTR_KIND)
.put("ptr.dir", SpanPointerUtils.DOWN_DIRECTION)
// First 32 chars of SHA256("dynamodb-one-key-table|id|test-id-1||")
.put("ptr.hash", "ca8daaa857b00545ed5186a915cf1ab5")
.put("link.kind", SpanPointersProcessor.LINK_KIND)
.put("link.kind", SpanPointerUtils.LINK_KIND)
.build())
}
tags {
Expand Down Expand Up @@ -295,11 +295,11 @@ class DynamoDbClientTest extends InstrumentationSpecification {
spanType DDSpanTypes.HTTP_CLIENT
links {
link(DDTraceId.ZERO, 0, SpanLink.DEFAULT_FLAGS, SpanAttributes.builder()
.put("ptr.kind", SpanPointersProcessor.DYNAMODB_PTR_KIND)
.put("ptr.dir", SpanPointersProcessor.DOWN_DIRECTION)
.put("ptr.kind", SpanPointerUtils.DYNAMODB_PTR_KIND)
.put("ptr.dir", SpanPointerUtils.DOWN_DIRECTION)
// First 32 chars of SHA256("dynamodb-two-key-table|primaryKey|customer-123|sortKey|order-456")
.put("ptr.hash", "90922c7899a82ea34406fdcdfb95161e")
.put("link.kind", SpanPointersProcessor.LINK_KIND)
.put("link.kind", SpanPointerUtils.LINK_KIND)
.build())
}
tags {
Expand Down Expand Up @@ -383,11 +383,11 @@ class DynamoDbClientTest extends InstrumentationSpecification {
spanType DDSpanTypes.HTTP_CLIENT
links {
link(DDTraceId.ZERO, 0, SpanLink.DEFAULT_FLAGS, SpanAttributes.builder()
.put("ptr.kind", SpanPointersProcessor.DYNAMODB_PTR_KIND)
.put("ptr.dir", SpanPointersProcessor.DOWN_DIRECTION)
.put("ptr.kind", SpanPointerUtils.DYNAMODB_PTR_KIND)
.put("ptr.dir", SpanPointerUtils.DOWN_DIRECTION)
// First 32 chars of SHA256("dynamodb-one-key-table|id|delete-test-id||")
.put("ptr.hash", "65031164be5e929fddd274a02cba3f9f")
.put("link.kind", SpanPointersProcessor.LINK_KIND)
.put("link.kind", SpanPointerUtils.LINK_KIND)
.build())
}
tags {
Expand Down Expand Up @@ -482,11 +482,11 @@ class DynamoDbClientTest extends InstrumentationSpecification {
spanType DDSpanTypes.HTTP_CLIENT
links {
link(DDTraceId.ZERO, 0, SpanLink.DEFAULT_FLAGS, SpanAttributes.builder()
.put("ptr.kind", SpanPointersProcessor.DYNAMODB_PTR_KIND)
.put("ptr.dir", SpanPointersProcessor.DOWN_DIRECTION)
.put("ptr.kind", SpanPointerUtils.DYNAMODB_PTR_KIND)
.put("ptr.dir", SpanPointerUtils.DOWN_DIRECTION)
// First 32 chars of SHA256("dynamodb-two-key-table|primaryKey|user-789|sortKey|profile")
.put("ptr.hash", "e5ce1148208c6f88041c73ceb9bbbf3a")
.put("link.kind", SpanPointersProcessor.LINK_KIND)
.put("link.kind", SpanPointerUtils.LINK_KIND)
.build())
}
tags {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,130 +1,164 @@
import datadog.trace.bootstrap.instrumentation.api.AgentSpan
import datadog.trace.bootstrap.instrumentation.api.InstrumentationTags
import datadog.trace.bootstrap.instrumentation.api.AgentSpanLink
import datadog.trace.bootstrap.instrumentation.api.SpanPointerUtils
import datadog.trace.instrumentation.aws.v2.dynamodb.DynamoDbUtil
import org.junit.jupiter.api.Test
import software.amazon.awssdk.core.SdkBytes
import software.amazon.awssdk.services.dynamodb.model.AttributeValue

class DynamoDbUtilTest {
static createMockSpan() {
def tags = [:]
def links = []

def mockSpan = [
setTag: { String key, String value ->
tags[key] = value
return null
addLink: { AgentSpanLink link ->
links.add(link)
}
] as AgentSpan

return [span: mockSpan, tags: tags]
return [span: mockSpan, links: links]
}

@Test
void testExportTagsWithNullKeys() {
void testAddSpanPointerWithNullKeys() {
def mockData = createMockSpan()
def mockSpan = mockData.span
def tags = mockData.tags
def links = mockData.links

DynamoDbUtil.exportTagsWithKnownKeys(mockSpan, null)
DynamoDbUtil.addSpanPointer(mockSpan, "table", null)

assert tags.isEmpty()
assert links.isEmpty()
}

@Test
void testExportTagsWithEmptyKeys() {
void testAddSpanPointerWithEmptyKeys() {
def mockData = createMockSpan()
def mockSpan = mockData.span
def tags = mockData.tags
def links = mockData.links

DynamoDbUtil.exportTagsWithKnownKeys(mockSpan, [:])
DynamoDbUtil.addSpanPointer(mockSpan, "table", [:])

assert tags.isEmpty()
assert links.isEmpty()
}

@Test
void testExportTagsWithSingleStringKey() {
void testAddSpanPointerWithNullTableName() {
def mockData = createMockSpan()
def mockSpan = mockData.span
def tags = mockData.tags
def links = mockData.links

def keys = [
"id": AttributeValue.builder().s("12345").build()
]

DynamoDbUtil.exportTagsWithKnownKeys(mockSpan, keys)
DynamoDbUtil.addSpanPointer(mockSpan, null, keys)

assert tags[InstrumentationTags.DYNAMO_PRIMARY_KEY_1] == "id"
assert tags[InstrumentationTags.DYNAMO_PRIMARY_KEY_1_VALUE] == "12345"
assert links.isEmpty()
}

@Test
void testExportTagsWithSingleNumberKey() {
void testAddSpanPointerWithSingleStringKey() {
def mockData = createMockSpan()
def mockSpan = mockData.span
def tags = mockData.tags
def links = mockData.links

def keys = [
"id": AttributeValue.builder().s("12345").build()
]

DynamoDbUtil.addSpanPointer(mockSpan, "my-table", keys)

assert links.size() == 1
def link = links[0]
assert link.attributes().asMap().get("ptr.kind") == SpanPointerUtils.DYNAMODB_PTR_KIND
assert link.attributes().asMap().get("ptr.dir") == SpanPointerUtils.DOWN_DIRECTION
assert link.attributes().asMap().get("link.kind") == SpanPointerUtils.LINK_KIND
assert link.attributes().asMap().get("ptr.hash") != null
}

@Test
void testAddSpanPointerWithSingleNumberKey() {
def mockData = createMockSpan()
def mockSpan = mockData.span
def links = mockData.links

def keys = [
"count": AttributeValue.builder().n("42").build()
]

DynamoDbUtil.exportTagsWithKnownKeys(mockSpan, keys)
DynamoDbUtil.addSpanPointer(mockSpan, "my-table", keys)

assert tags[InstrumentationTags.DYNAMO_PRIMARY_KEY_1] == "count"
assert tags[InstrumentationTags.DYNAMO_PRIMARY_KEY_1_VALUE] == "42"
assert links.size() == 1
def link = links[0]
assert link.attributes().asMap().get("ptr.kind") == SpanPointerUtils.DYNAMODB_PTR_KIND
}

@Test
void testExportTagsWithSingleBinaryKey() {
void testAddSpanPointerWithSingleBinaryKey() {
def mockData = createMockSpan()
def mockSpan = mockData.span
def tags = mockData.tags
def links = mockData.links

def binaryData = "binary-data".getBytes()
def keys = [
"data": AttributeValue.builder().b(SdkBytes.fromByteArray(binaryData)).build()
]

DynamoDbUtil.exportTagsWithKnownKeys(mockSpan, keys)
DynamoDbUtil.addSpanPointer(mockSpan, "my-table", keys)

assert tags[InstrumentationTags.DYNAMO_PRIMARY_KEY_1] == "data"
assert tags[InstrumentationTags.DYNAMO_PRIMARY_KEY_1_VALUE] == "binary-data"
assert links.size() == 1
def link = links[0]
assert link.attributes().asMap().get("ptr.kind") == SpanPointerUtils.DYNAMODB_PTR_KIND
}

@Test
void testExportTagsWithTwoKeys() {
void testAddSpanPointerWithTwoKeys() {
def mockData = createMockSpan()
def mockSpan = mockData.span
def tags = mockData.tags
def links = mockData.links

def keys = [
"id": AttributeValue.builder().s("12345").build(),
"name": AttributeValue.builder().s("item-name").build()
]

DynamoDbUtil.exportTagsWithKnownKeys(mockSpan, keys)
DynamoDbUtil.addSpanPointer(mockSpan, "my-table", keys)

assert tags[InstrumentationTags.DYNAMO_PRIMARY_KEY_1] == "id"
assert tags[InstrumentationTags.DYNAMO_PRIMARY_KEY_1_VALUE] == "12345"
assert tags[InstrumentationTags.DYNAMO_PRIMARY_KEY_2] == "name"
assert tags[InstrumentationTags.DYNAMO_PRIMARY_KEY_2_VALUE] == "item-name"
assert links.size() == 1
def link = links[0]
assert link.attributes().asMap().get("ptr.kind") == SpanPointerUtils.DYNAMODB_PTR_KIND
assert link.attributes().asMap().get("ptr.hash") != null
}

@Test
void testExportTagsWithTwoKeysSortsAlphabetically() {
void testAddSpanPointerWithTwoKeysSortsAlphabetically() {
def mockData = createMockSpan()
def mockSpan = mockData.span
def tags = mockData.tags
def mockSpan1 = mockData.span
def links1 = mockData.links

def keys = [
// Keys in order bKey, aKey
def keys1 = [
"bKey": AttributeValue.builder().s("abc").build(),
"aKey": AttributeValue.builder().s("zxy").build()
]

DynamoDbUtil.exportTagsWithKnownKeys(mockSpan, keys)
DynamoDbUtil.addSpanPointer(mockSpan1, "my-table", keys1)

// Reverse order: aKey, bKey — should produce the same hash
def mockData2 = createMockSpan()
def mockSpan2 = mockData2.span
def links2 = mockData2.links

def keys2 = [
"aKey": AttributeValue.builder().s("zxy").build(),
"bKey": AttributeValue.builder().s("abc").build()
]

DynamoDbUtil.addSpanPointer(mockSpan2, "my-table", keys2)

assert tags[InstrumentationTags.DYNAMO_PRIMARY_KEY_1] == "aKey"
assert tags[InstrumentationTags.DYNAMO_PRIMARY_KEY_1_VALUE] == "zxy"
assert tags[InstrumentationTags.DYNAMO_PRIMARY_KEY_2] == "bKey"
assert tags[InstrumentationTags.DYNAMO_PRIMARY_KEY_2_VALUE] == "abc"
assert links1.size() == 1
assert links2.size() == 1
// Both should produce the same hash regardless of input order
assert links1[0].attributes().asMap().get("ptr.hash") == links2[0].attributes().asMap().get("ptr.hash")
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
package datadog.trace.instrumentation.aws.v2.s3;

import static datadog.trace.bootstrap.instrumentation.api.InstrumentationTags.S3_ETAG;

import datadog.context.Context;
import datadog.trace.api.Config;
import datadog.trace.bootstrap.InstanceStore;
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
import datadog.trace.bootstrap.instrumentation.api.SpanPointerUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import software.amazon.awssdk.core.interceptor.Context.AfterExecution;
Expand Down Expand Up @@ -52,9 +51,9 @@ public void afterExecution(AfterExecution context, ExecutionAttributes execution
return;
}

// Store eTag as tag, then calculate hash + add span pointers in SpanPointersProcessor.
// Bucket and key are already stored as tags in AwsSdkClientDecorator, so need to make redundant
// tags.
span.setTag(S3_ETAG, eTag);
// Get bucket and key from the request to create the span pointer directly
String bucket = context.request().getValueForField("Bucket", String.class).orElse(null);
String key = context.request().getValueForField("Key", String.class).orElse(null);
SpanPointerUtils.addS3SpanPointer(span, bucket, key, eTag);
}
}
Loading
Loading