From 48d5d9fb1ceb53fc76916c9954f4730189d6917f Mon Sep 17 00:00:00 2001 From: Nicholas Hulston Date: Tue, 10 Dec 2024 10:06:17 -0500 Subject: [PATCH 01/20] Add basic S3 instrumentation --- .../aws-java-s3-2.0/build.gradle | 29 +++++++++++ .../aws/v2/s3/S3ClientInstrumentation.java | 48 +++++++++++++++++++ .../aws/v2/s3/S3Interceptor.java | 13 +++++ .../aws/v2/s3/TextMapInjectAdapter.java | 13 +++++ settings.gradle | 1 + 5 files changed, 104 insertions(+) create mode 100644 dd-java-agent/instrumentation/aws-java-s3-2.0/build.gradle create mode 100644 dd-java-agent/instrumentation/aws-java-s3-2.0/src/main/java/datadog/trace/instrumentation/aws/v2/s3/S3ClientInstrumentation.java create mode 100644 dd-java-agent/instrumentation/aws-java-s3-2.0/src/main/java/datadog/trace/instrumentation/aws/v2/s3/S3Interceptor.java create mode 100644 dd-java-agent/instrumentation/aws-java-s3-2.0/src/main/java/datadog/trace/instrumentation/aws/v2/s3/TextMapInjectAdapter.java diff --git a/dd-java-agent/instrumentation/aws-java-s3-2.0/build.gradle b/dd-java-agent/instrumentation/aws-java-s3-2.0/build.gradle new file mode 100644 index 00000000000..017ba58f8a0 --- /dev/null +++ b/dd-java-agent/instrumentation/aws-java-s3-2.0/build.gradle @@ -0,0 +1,29 @@ +muzzle { + pass { + group = "software.amazon.awssdk" + module = "s3" + versions = "[2.0,3)" + assertInverse = true + } +} + +apply from: "$rootDir/gradle/java.gradle" + +addTestSuiteForDir('latestDepTest', 'test') +addTestSuiteExtendingForDir('latestDepForkedTest', 'latestDepTest', 'test') + +dependencies { + compileOnly group: 'software.amazon.awssdk', name: 's3', version: '2.29.26' + + // Include httpclient instrumentation for testing because it is a dependency for aws-sdk. + testImplementation project(':dd-java-agent:instrumentation:apache-httpclient-4') + testImplementation project(':dd-java-agent:instrumentation:aws-java-sdk-2.2') + testImplementation 'software.amazon.awssdk:s3:2.29.26' + testImplementation 'org.testcontainers:localstack:1.20.1' + + latestDepTestImplementation group: 'software.amazon.awssdk', name: 's3', version: '+' +} + +tasks.withType(Test).configureEach { + usesService(testcontainersLimit) +} diff --git a/dd-java-agent/instrumentation/aws-java-s3-2.0/src/main/java/datadog/trace/instrumentation/aws/v2/s3/S3ClientInstrumentation.java b/dd-java-agent/instrumentation/aws-java-s3-2.0/src/main/java/datadog/trace/instrumentation/aws/v2/s3/S3ClientInstrumentation.java new file mode 100644 index 00000000000..6ff1eada9c7 --- /dev/null +++ b/dd-java-agent/instrumentation/aws-java-s3-2.0/src/main/java/datadog/trace/instrumentation/aws/v2/s3/S3ClientInstrumentation.java @@ -0,0 +1,48 @@ +package datadog.trace.instrumentation.aws.v2.s3; + +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.isMethod; + +import com.google.auto.service.AutoService; +import datadog.trace.agent.tooling.Instrumenter; +import datadog.trace.agent.tooling.InstrumenterModule; +import java.util.List; +import net.bytebuddy.asm.Advice; +import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; + +@AutoService(InstrumenterModule.class) +public final class S3ClientInstrumentation extends InstrumenterModule.Tracing + implements Instrumenter.ForSingleType { + public S3ClientInstrumentation() { + super("s3"); + } + + @Override + public String instrumentedType() { + return "software.amazon.awssdk.core.client.builder.SdkDefaultClientBuilder"; + } + + @Override + public void methodAdvice(MethodTransformer transformer) { + transformer.applyAdvice( + isMethod().and(named("resolveExecutionInterceptors")), + S3ClientInstrumentation.class.getName() + "$AwsS3BuilderAdvice"); + } + + @Override + public String[] helperClassNames() { + return new String[] {packageName + ".S3Interceptor", packageName + ".TextMapInjectAdapter"}; + } + + public static class AwsS3BuilderAdvice { + @Advice.OnMethodExit(suppress = Throwable.class) + public static void addHandler(@Advice.Return final List interceptors) { + for (ExecutionInterceptor interceptor : interceptors) { + if (interceptor instanceof S3Interceptor) { + return; // list already has our interceptor, return to builder + } + } + interceptors.add(new S3Interceptor()); + } + } +} diff --git a/dd-java-agent/instrumentation/aws-java-s3-2.0/src/main/java/datadog/trace/instrumentation/aws/v2/s3/S3Interceptor.java b/dd-java-agent/instrumentation/aws-java-s3-2.0/src/main/java/datadog/trace/instrumentation/aws/v2/s3/S3Interceptor.java new file mode 100644 index 00000000000..2065a70f18f --- /dev/null +++ b/dd-java-agent/instrumentation/aws-java-s3-2.0/src/main/java/datadog/trace/instrumentation/aws/v2/s3/S3Interceptor.java @@ -0,0 +1,13 @@ +package datadog.trace.instrumentation.aws.v2.s3; + +import software.amazon.awssdk.core.interceptor.Context; +import software.amazon.awssdk.core.interceptor.ExecutionAttributes; +import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; + +public class S3Interceptor implements ExecutionInterceptor { + @Override + public void afterExecution( + Context.AfterExecution context, ExecutionAttributes executionAttributes) { + System.out.println("S3Interceptor afterExecution()"); + } +} diff --git a/dd-java-agent/instrumentation/aws-java-s3-2.0/src/main/java/datadog/trace/instrumentation/aws/v2/s3/TextMapInjectAdapter.java b/dd-java-agent/instrumentation/aws-java-s3-2.0/src/main/java/datadog/trace/instrumentation/aws/v2/s3/TextMapInjectAdapter.java new file mode 100644 index 00000000000..e3ccf485543 --- /dev/null +++ b/dd-java-agent/instrumentation/aws-java-s3-2.0/src/main/java/datadog/trace/instrumentation/aws/v2/s3/TextMapInjectAdapter.java @@ -0,0 +1,13 @@ +package datadog.trace.instrumentation.aws.v2.s3; + +import datadog.trace.bootstrap.instrumentation.api.AgentPropagation; + +public class TextMapInjectAdapter implements AgentPropagation.Setter { + + public static final TextMapInjectAdapter SETTER = new TextMapInjectAdapter(); + + @Override + public void set(final StringBuilder builder, final String key, final String value) { + builder.append('"').append(key).append("\":\"").append(value).append("\","); + } +} diff --git a/settings.gradle b/settings.gradle index 0422e36fce6..b35abe77f46 100644 --- a/settings.gradle +++ b/settings.gradle @@ -196,6 +196,7 @@ include ':dd-java-agent:instrumentation:aws-java-sns-1.0' include ':dd-java-agent:instrumentation:aws-java-sns-2.0' include ':dd-java-agent:instrumentation:aws-java-sqs-1.0' include ':dd-java-agent:instrumentation:aws-java-sqs-2.0' +include ':dd-java-agent:instrumentation:aws-java-s3-2.0' include ':dd-java-agent:instrumentation:aws-lambda-handler' include ':dd-java-agent:instrumentation:axis-2' include ':dd-java-agent:instrumentation:axway-api' From 282ca70075d5dc9139f86c141e06d4ffee22ebe3 Mon Sep 17 00:00:00 2001 From: Nicholas Hulston Date: Tue, 10 Dec 2024 10:10:43 -0500 Subject: [PATCH 02/20] Calculate pointer hash --- .../spanpointers/SpanPointersHelper.java | 28 ++++++++++++ .../aws/v2/s3/S3Interceptor.java | 43 ++++++++++++++++++- 2 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/spanpointers/SpanPointersHelper.java diff --git a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/spanpointers/SpanPointersHelper.java b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/spanpointers/SpanPointersHelper.java new file mode 100644 index 00000000000..184a3777f0b --- /dev/null +++ b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/spanpointers/SpanPointersHelper.java @@ -0,0 +1,28 @@ +package datadog.trace.bootstrap.instrumentation.spanpointers; + +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +public final class SpanPointersHelper { + /** + * Generates a unique hash from an array of strings by joining them with | before hashing. Used to + * uniquely identify AWS requests for span pointers. + * + * @param components Array of strings to hash + * @return A 32-character hash uniquely identifying the components + * @throws NoSuchAlgorithmException this should never happen; but should be handled just in case. + */ + public static String generatePointerHash(String[] components) throws NoSuchAlgorithmException { + byte[] hash = + MessageDigest.getInstance("SHA-256") + .digest(String.join("|", components).getBytes(StandardCharsets.UTF_8)); + + StringBuilder hex = new StringBuilder(32); + for (int i = 0; i < 16; i++) { + hex.append(String.format("%02x", hash[i])); + } + + return hex.toString(); + } +} diff --git a/dd-java-agent/instrumentation/aws-java-s3-2.0/src/main/java/datadog/trace/instrumentation/aws/v2/s3/S3Interceptor.java b/dd-java-agent/instrumentation/aws-java-s3-2.0/src/main/java/datadog/trace/instrumentation/aws/v2/s3/S3Interceptor.java index 2065a70f18f..9c587c4b202 100644 --- a/dd-java-agent/instrumentation/aws-java-s3-2.0/src/main/java/datadog/trace/instrumentation/aws/v2/s3/S3Interceptor.java +++ b/dd-java-agent/instrumentation/aws-java-s3-2.0/src/main/java/datadog/trace/instrumentation/aws/v2/s3/S3Interceptor.java @@ -1,13 +1,54 @@ package datadog.trace.instrumentation.aws.v2.s3; +import static datadog.trace.bootstrap.instrumentation.spanpointers.SpanPointersHelper.generatePointerHash; + +import java.security.NoSuchAlgorithmException; 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.services.s3.model.CompleteMultipartUploadRequest; +import software.amazon.awssdk.services.s3.model.CompleteMultipartUploadResponse; +import software.amazon.awssdk.services.s3.model.CopyObjectRequest; +import software.amazon.awssdk.services.s3.model.CopyObjectResponse; +import software.amazon.awssdk.services.s3.model.PutObjectRequest; +import software.amazon.awssdk.services.s3.model.PutObjectResponse; public class S3Interceptor implements ExecutionInterceptor { @Override public void afterExecution( Context.AfterExecution context, ExecutionAttributes executionAttributes) { - System.out.println("S3Interceptor afterExecution()"); + String bucket, key, eTag; + Object request = context.request(); + Object response = context.response(); + // https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3Client.html + if (request instanceof PutObjectRequest) { + PutObjectRequest putObjectRequest = (PutObjectRequest) request; + bucket = putObjectRequest.bucket(); + key = putObjectRequest.key(); + eTag = ((PutObjectResponse) response).eTag(); + } else if (request instanceof CopyObjectRequest) { + CopyObjectRequest copyObjectRequest = (CopyObjectRequest) request; + bucket = copyObjectRequest.destinationBucket(); + key = copyObjectRequest.destinationKey(); + eTag = ((CopyObjectResponse) response).copyObjectResult().eTag(); + } else if (request instanceof CompleteMultipartUploadRequest) { + CompleteMultipartUploadRequest completeMultipartUploadRequest = + (CompleteMultipartUploadRequest) request; + bucket = completeMultipartUploadRequest.bucket(); + key = completeMultipartUploadRequest.key(); + eTag = ((CompleteMultipartUploadResponse) response).eTag(); + } else { + return; + } + + if (eTag.startsWith("\"") && eTag.endsWith("\"")) { + eTag = eTag.substring(1, eTag.length() - 1); + } + System.out.printf("[DEBUG] bucket: %s, key: %s, eTag: %s%n", bucket, key, eTag); + try { + String hash = generatePointerHash(new String[] {bucket, key, eTag}); + System.out.println("[DEBUG] hash: " + hash); + } catch (NoSuchAlgorithmException ignored) { + } } } From 9106b50370c99ca8f8b0fd4cec73015952140b56 Mon Sep 17 00:00:00 2001 From: Nicholas Hulston Date: Tue, 10 Dec 2024 10:17:46 -0500 Subject: [PATCH 03/20] Add span pointer to S3 request span --- .../spanpointers/SpanPointersHelper.java | 38 ++++++++++++++++++- .../aws/v2/s3/S3Interceptor.java | 33 +++++++++++++--- 2 files changed, 64 insertions(+), 7 deletions(-) diff --git a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/spanpointers/SpanPointersHelper.java b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/spanpointers/SpanPointersHelper.java index 184a3777f0b..d7a965eaf4e 100644 --- a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/spanpointers/SpanPointersHelper.java +++ b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/spanpointers/SpanPointersHelper.java @@ -1,10 +1,21 @@ package datadog.trace.bootstrap.instrumentation.spanpointers; +import datadog.trace.bootstrap.instrumentation.api.AgentSpan; +import datadog.trace.bootstrap.instrumentation.api.AgentSpanLink; +import datadog.trace.bootstrap.instrumentation.api.AgentTracer; +import datadog.trace.bootstrap.instrumentation.api.SpanAttributes; +import datadog.trace.bootstrap.instrumentation.api.SpanLink; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; public final class SpanPointersHelper { + public static final String S3_PTR_KIND = "aws.s3.object"; + private static final String LINK_KIND = "span-pointer"; + + // The pointer direction will always be down. The agent handles cases where the direction is up. + private static final String DOWN_DIRECTION = "d"; + /** * Generates a unique hash from an array of strings by joining them with | before hashing. Used to * uniquely identify AWS requests for span pointers. @@ -13,7 +24,7 @@ public final class SpanPointersHelper { * @return A 32-character hash uniquely identifying the components * @throws NoSuchAlgorithmException this should never happen; but should be handled just in case. */ - public static String generatePointerHash(String[] components) throws NoSuchAlgorithmException { + private static String generatePointerHash(String[] components) throws NoSuchAlgorithmException { byte[] hash = MessageDigest.getInstance("SHA-256") .digest(String.join("|", components).getBytes(StandardCharsets.UTF_8)); @@ -25,4 +36,29 @@ public static String generatePointerHash(String[] components) throws NoSuchAlgor return hex.toString(); } + + /** + * Adds a span pointer to the given span, using the SHA-256 hash of the components. + * + * @param span The span to add the pointer to + * @param kind Identifies which hashing rules to follow + * @param components Array of strings to hash, following span pointer rules + * @throws NoSuchAlgorithmException if unable to calculate hash + * @see Span pointer + * rules + */ + public static void addSpanPointer(AgentSpan span, String kind, String[] components) + throws NoSuchAlgorithmException { + SpanAttributes attributes = + (SpanAttributes) + SpanAttributes.builder() + .put("ptr.kind", kind) + .put("ptr.dir", DOWN_DIRECTION) + .put("ptr.hash", generatePointerHash(components)) + .put("link.kind", LINK_KIND) + .build(); + + AgentTracer.NoopContext zeroContext = AgentTracer.NoopContext.INSTANCE; + span.addLink(SpanLink.from(zeroContext, AgentSpanLink.DEFAULT_FLAGS, "", attributes)); + } } diff --git a/dd-java-agent/instrumentation/aws-java-s3-2.0/src/main/java/datadog/trace/instrumentation/aws/v2/s3/S3Interceptor.java b/dd-java-agent/instrumentation/aws-java-s3-2.0/src/main/java/datadog/trace/instrumentation/aws/v2/s3/S3Interceptor.java index 9c587c4b202..cd8d582da8a 100644 --- a/dd-java-agent/instrumentation/aws-java-s3-2.0/src/main/java/datadog/trace/instrumentation/aws/v2/s3/S3Interceptor.java +++ b/dd-java-agent/instrumentation/aws-java-s3-2.0/src/main/java/datadog/trace/instrumentation/aws/v2/s3/S3Interceptor.java @@ -1,9 +1,14 @@ package datadog.trace.instrumentation.aws.v2.s3; -import static datadog.trace.bootstrap.instrumentation.spanpointers.SpanPointersHelper.generatePointerHash; +import static datadog.trace.bootstrap.instrumentation.spanpointers.SpanPointersHelper.S3_PTR_KIND; +import static datadog.trace.bootstrap.instrumentation.spanpointers.SpanPointersHelper.addSpanPointer; -import java.security.NoSuchAlgorithmException; +import datadog.trace.bootstrap.InstanceStore; +import datadog.trace.bootstrap.instrumentation.api.AgentSpan; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import software.amazon.awssdk.core.interceptor.Context; +import software.amazon.awssdk.core.interceptor.ExecutionAttribute; import software.amazon.awssdk.core.interceptor.ExecutionAttributes; import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; import software.amazon.awssdk.services.s3.model.CompleteMultipartUploadRequest; @@ -14,12 +19,26 @@ import software.amazon.awssdk.services.s3.model.PutObjectResponse; public class S3Interceptor implements ExecutionInterceptor { + private static final Logger log = LoggerFactory.getLogger(S3Interceptor.class); + + public static final ExecutionAttribute SPAN_ATTRIBUTE = + InstanceStore.of(ExecutionAttribute.class) + .putIfAbsent("DatadogSpan", () -> new ExecutionAttribute<>("DatadogSpan")); + @Override public void afterExecution( Context.AfterExecution context, ExecutionAttributes executionAttributes) { + AgentSpan span = executionAttributes.getAttribute(SPAN_ATTRIBUTE); + if (span == null) { + log.debug("Unable to find S3 request span. Not creating span pointer."); + return; + } + String bucket, key, eTag; Object request = context.request(); Object response = context.response(); + + // Get bucket, key, and eTag for hash calculation. // https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3Client.html if (request instanceof PutObjectRequest) { PutObjectRequest putObjectRequest = (PutObjectRequest) request; @@ -41,14 +60,16 @@ public void afterExecution( return; } + // Hash calculation rules: + // https://github.com/DataDog/dd-span-pointer-rules/blob/main/AWS/S3/Object/README.md if (eTag.startsWith("\"") && eTag.endsWith("\"")) { eTag = eTag.substring(1, eTag.length() - 1); } - System.out.printf("[DEBUG] bucket: %s, key: %s, eTag: %s%n", bucket, key, eTag); + String[] components = new String[] {bucket, key, eTag}; try { - String hash = generatePointerHash(new String[] {bucket, key, eTag}); - System.out.println("[DEBUG] hash: " + hash); - } catch (NoSuchAlgorithmException ignored) { + addSpanPointer(span, S3_PTR_KIND, components); + } catch (Exception e) { + log.debug("Failed to add span pointer: {}", e.getMessage()); } } } From d95149ecb29e8b49a697defa4712d2851bbcc1f1 Mon Sep 17 00:00:00 2001 From: Nicholas Hulston Date: Tue, 10 Dec 2024 11:59:37 -0500 Subject: [PATCH 04/20] Unit tests for SpanPointersHelper --- .../SpanPointersHelperTest.groovy | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 dd-java-agent/agent-bootstrap/src/test/groovy/datadog/trace/bootstrap/instrumentation/spanpointers/SpanPointersHelperTest.groovy diff --git a/dd-java-agent/agent-bootstrap/src/test/groovy/datadog/trace/bootstrap/instrumentation/spanpointers/SpanPointersHelperTest.groovy b/dd-java-agent/agent-bootstrap/src/test/groovy/datadog/trace/bootstrap/instrumentation/spanpointers/SpanPointersHelperTest.groovy new file mode 100644 index 00000000000..e78a920a51e --- /dev/null +++ b/dd-java-agent/agent-bootstrap/src/test/groovy/datadog/trace/bootstrap/instrumentation/spanpointers/SpanPointersHelperTest.groovy @@ -0,0 +1,75 @@ +package datadog.trace.bootstrap.instrumentation.spanpointers + +import datadog.trace.api.DDSpanId +import datadog.trace.api.DDTraceId +import datadog.trace.bootstrap.instrumentation.api.AgentSpan +import datadog.trace.bootstrap.instrumentation.api.SpanLink +import spock.lang.Specification + +class SpanPointersHelperTest extends Specification { + def "addSpanPointer adds correct link to span with basic values"() { + given: + AgentSpan span = Mock(AgentSpan) + String kind = SpanPointersHelper.S3_PTR_KIND + String[] components = ["some-bucket", "some-key.data", "ab12ef34"] + String expectedHash = "e721375466d4116ab551213fdea08413" + + when: + SpanPointersHelper.addSpanPointer(span, kind, components) + + then: + 1 * span.addLink({ SpanLink link -> + assert link.traceId() == DDTraceId.ZERO + assert link.spanId() == DDSpanId.ZERO + assert link.attributes.asMap().get("ptr.kind") == kind + assert link.attributes.asMap().get("ptr.dir") == SpanPointersHelper.DOWN_DIRECTION + assert link.attributes.asMap().get("ptr.hash") == expectedHash + assert link.attributes.asMap().get("link.kind") == SpanPointersHelper.LINK_KIND + true + }) + } + + def "addSpanPointer adds correct link to span with non-ascii key"() { + given: + AgentSpan span = Mock(AgentSpan) + String kind = SpanPointersHelper.S3_PTR_KIND + String[] components = ["some-bucket", "some-key.你好", "ab12ef34"] + String expectedHash = "d1333a04b9928ab462b5c6cadfa401f4" + + when: + SpanPointersHelper.addSpanPointer(span, kind, components) + + then: + 1 * span.addLink({ SpanLink link -> + assert link.traceId() == DDTraceId.ZERO + assert link.spanId() == DDSpanId.ZERO + assert link.attributes.asMap().get("ptr.kind") == kind + assert link.attributes.asMap().get("ptr.dir") == SpanPointersHelper.DOWN_DIRECTION + assert link.attributes.asMap().get("ptr.hash") == expectedHash + assert link.attributes.asMap().get("link.kind") == SpanPointersHelper.LINK_KIND + true + }) + } + + def "addSpanPointer adds correct link to span with multipart-upload"() { + given: + AgentSpan span = Mock(AgentSpan) + String kind = SpanPointersHelper.S3_PTR_KIND + String[] components = ["some-bucket", "some-key.data", "ab12ef34-5"] + String expectedHash = "2b90dffc37ebc7bc610152c3dc72af9f" + + when: + SpanPointersHelper.addSpanPointer(span, kind, components) + + then: + 1 * span.addLink({ SpanLink link -> + assert link.traceId() == DDTraceId.ZERO + assert link.spanId() == DDSpanId.ZERO + assert link.attributes.asMap().get("ptr.kind") == kind + assert link.attributes.asMap().get("ptr.dir") == SpanPointersHelper.DOWN_DIRECTION + assert link.attributes.asMap().get("ptr.hash") == expectedHash + assert link.attributes.asMap().get("link.kind") == SpanPointersHelper.LINK_KIND + true + }) + } +} From c2e77272000522962a8d5874c559bf5a9a786c80 Mon Sep 17 00:00:00 2001 From: Nicholas Hulston Date: Tue, 10 Dec 2024 11:42:32 -0500 Subject: [PATCH 05/20] Integration tests --- .../spanpointers/SpanPointersHelper.java | 4 +- .../src/test/groovy/S3ClientTest.groovy | 384 ++++++++++++++++++ 2 files changed, 386 insertions(+), 2 deletions(-) create mode 100644 dd-java-agent/instrumentation/aws-java-s3-2.0/src/test/groovy/S3ClientTest.groovy diff --git a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/spanpointers/SpanPointersHelper.java b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/spanpointers/SpanPointersHelper.java index d7a965eaf4e..bc03a6f76e3 100644 --- a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/spanpointers/SpanPointersHelper.java +++ b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/spanpointers/SpanPointersHelper.java @@ -11,10 +11,10 @@ public final class SpanPointersHelper { public static final String S3_PTR_KIND = "aws.s3.object"; - private static final String LINK_KIND = "span-pointer"; + public static final String LINK_KIND = "span-pointer"; // The pointer direction will always be down. The agent handles cases where the direction is up. - private static final String DOWN_DIRECTION = "d"; + public static final String DOWN_DIRECTION = "d"; /** * Generates a unique hash from an array of strings by joining them with | before hashing. Used to diff --git a/dd-java-agent/instrumentation/aws-java-s3-2.0/src/test/groovy/S3ClientTest.groovy b/dd-java-agent/instrumentation/aws-java-s3-2.0/src/test/groovy/S3ClientTest.groovy new file mode 100644 index 00000000000..f840f23f3a3 --- /dev/null +++ b/dd-java-agent/instrumentation/aws-java-s3-2.0/src/test/groovy/S3ClientTest.groovy @@ -0,0 +1,384 @@ +import datadog.trace.agent.test.AgentTestRunner +import datadog.trace.api.DDSpanTypes +import datadog.trace.bootstrap.instrumentation.spanpointers.SpanPointersHelper +import groovy.json.JsonSlurper +import org.testcontainers.containers.GenericContainer +import org.testcontainers.utility.DockerImageName +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials +import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider +import software.amazon.awssdk.core.sync.RequestBody +import software.amazon.awssdk.regions.Region +import software.amazon.awssdk.services.s3.S3Client +import software.amazon.awssdk.services.s3.model.* +import software.amazon.awssdk.services.s3.S3Configuration +import spock.lang.Shared + +import java.time.Duration + +class S3ClientTest extends AgentTestRunner { + static final LOCALSTACK = new GenericContainer(DockerImageName.parse("localstack/localstack")) + .withExposedPorts(4566) + .withEnv("SERVICES", "s3") + .withReuse(true) + .withStartupTimeout(Duration.ofSeconds(120)) + + @Shared + S3Client s3Client + + @Shared + String bucketName + + def setupSpec() { + LOCALSTACK.start() + def endPoint = "http://" + LOCALSTACK.getHost() + ":" + LOCALSTACK.getMappedPort(4566) + + s3Client = S3Client.builder() + .endpointOverride(URI.create(endPoint)) + .region(Region.of("us-east-1")) + .credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create("test", "test"))) + .serviceConfiguration(S3Configuration.builder() + .pathStyleAccessEnabled(true) + .build()) + .build() + + // Create a test bucket + bucketName = "s3-bucket-name-test" + s3Client.createBucket { it.bucket(bucketName) } + } + + def cleanupSpec() { + LOCALSTACK.stop() + } + + def "should add span pointer for putObject operation"() { + when: + TEST_WRITER.clear() + String key = "test-key" + String content = "test body" + + s3Client.putObject( + PutObjectRequest.builder().bucket(bucketName).key(key).build(), + RequestBody.fromString(content) + ) + + then: + assertTraces(1) { + trace(1) { + span { + serviceName "java-aws-sdk" + operationName "aws.http" + resourceName "S3.PutObject" + spanType DDSpanTypes.HTTP_CLIENT + tags { + defaultTags() + tag "component", "java-aws-sdk" + tag "aws.operation", "PutObject" + tag "aws.service", "S3" + tag "aws_service", "S3" + tag "aws.agent", "java-aws-sdk" + tag "aws.bucket.name", bucketName + tag "aws.object.key", key + tag "bucketname", bucketName + tag "http.method", "PUT" + tag "http.status_code", 200 + tag "http.url", { it.startsWith("http://localhost") && it.contains("/$key") } + tag "peer.hostname", "localhost" + tag "peer.port", { it instanceof Integer } + tag "span.kind", "client" + tag "aws.requestId", { it != null } + tag "_dd.span_links", { it != null } + + // Assert the span links + def spanLinks = tags["_dd.span_links"] + assert spanLinks != null + def links = new JsonSlurper().parseText(spanLinks) + assert links.size() == 1 + def link = links[0] + assert link["attributes"] != null + assert link["attributes"]["ptr.kind"] == SpanPointersHelper.S3_PTR_KIND + assert link["attributes"]["ptr.dir"] == SpanPointersHelper.DOWN_DIRECTION + assert link["attributes"]["ptr.hash"] == "6d1a2fe194c6579187408f827f942be3" + assert link["attributes"]["link.kind"] == SpanPointersHelper.LINK_KIND + } + } + } + } + } + + def "should add span pointer for copyObject operation"() { + when: + TEST_WRITER.clear() + String sourceKey = "test-key" + String destKey = "new-key" + String content = "test body" + + s3Client.putObject( + PutObjectRequest.builder().bucket(bucketName).key(sourceKey).build(), + RequestBody.fromString(content) + ) + s3Client.copyObject( + CopyObjectRequest.builder() + .sourceBucket(bucketName) + .destinationBucket(bucketName) + .sourceKey(sourceKey) + .destinationKey(destKey) + .build() + ) + + then: + assertTraces(2) { + trace(1) { + span { + serviceName "java-aws-sdk" + operationName "aws.http" + resourceName "S3.PutObject" + spanType DDSpanTypes.HTTP_CLIENT + tags { + defaultTags() + tag "component", "java-aws-sdk" + tag "aws.operation", "PutObject" + tag "aws.service", "S3" + tag "aws_service", "S3" + tag "aws.agent", "java-aws-sdk" + tag "aws.bucket.name", bucketName + tag "aws.object.key", sourceKey + tag "bucketname", bucketName + tag "http.method", "PUT" + tag "http.status_code", 200 + tag "http.url", { it.startsWith("http://localhost") && it.contains("/$sourceKey") } + tag "peer.hostname", "localhost" + tag "peer.port", { it instanceof Integer } + tag "span.kind", "client" + tag "aws.requestId", { it != null } + tag "_dd.span_links", { it != null } + } + } + } + trace(1) { + span { + serviceName "java-aws-sdk" + operationName "aws.http" + resourceName "S3.CopyObject" + spanType DDSpanTypes.HTTP_CLIENT + tags { + defaultTags() + tag "component", "java-aws-sdk" + tag "aws.operation", "CopyObject" + tag "aws.service", "S3" + tag "aws_service", "S3" + tag "aws.agent", "java-aws-sdk" + tag "aws.bucket.name", bucketName + tag "aws.object.key", destKey + tag "bucketname", bucketName + tag "http.method", "PUT" + tag "http.status_code", 200 + tag "http.url", { it.startsWith("http://localhost") && it.contains("/$destKey") } + tag "peer.hostname", "localhost" + tag "peer.port", { it instanceof Integer } + tag "span.kind", "client" + tag "aws.requestId", { it != null } + tag "_dd.span_links", { it != null } + + // Assert the span links + def spanLinks = tags["_dd.span_links"] + assert spanLinks != null + def links = new JsonSlurper().parseText(spanLinks) + assert links.size() == 1 + def link = links[0] + assert link["attributes"] != null + assert link["attributes"]["ptr.kind"] == SpanPointersHelper.S3_PTR_KIND + assert link["attributes"]["ptr.dir"] == SpanPointersHelper.DOWN_DIRECTION + assert link["attributes"]["ptr.hash"] == "1542053ce6d393c424b1374bac1fc0c5" + assert link["attributes"]["link.kind"] == SpanPointersHelper.LINK_KIND + } + } + } + } + } + + def "should add span pointer for completeMultipartUpload operation"() { + when: + TEST_WRITER.clear() + String key = "multipart-test" + + // Initiate multipart upload + def createMultipartUploadResponse = s3Client.createMultipartUpload( + CreateMultipartUploadRequest.builder().bucket(bucketName).key(key).build() + ) + String uploadId = createMultipartUploadResponse.uploadId() + + // Create parts (5MB each) + int partSize = 5 * 1024 * 1024 + byte[] part1Data = new byte[partSize] + Arrays.fill(part1Data, (byte) 'a') + byte[] part2Data = new byte[partSize] + Arrays.fill(part2Data, (byte) 'b') + + // Upload parts + List completedParts = [] + s3Client.uploadPart( + UploadPartRequest.builder() + .bucket(bucketName) + .key(key) + .uploadId(uploadId) + .partNumber(1) + .build(), + RequestBody.fromBytes(part1Data) + ).with { response -> + completedParts.add(CompletedPart.builder() + .partNumber(1) + .eTag(response.eTag()) + .build()) + } + s3Client.uploadPart( + UploadPartRequest.builder() + .bucket(bucketName) + .key(key) + .uploadId(uploadId) + .partNumber(2) + .build(), + RequestBody.fromBytes(part2Data) + ).with { response -> + completedParts.add(CompletedPart.builder() + .partNumber(2) + .eTag(response.eTag()) + .build()) + } + + // Complete multipart upload + s3Client.completeMultipartUpload( + CompleteMultipartUploadRequest.builder() + .bucket(bucketName) + .key(key) + .uploadId(uploadId) + .multipartUpload(CompletedMultipartUpload.builder().parts(completedParts).build()) + .build() + ) + + then: + // Check that spans were created and that the CompleteMultipartUpload span has the expected span pointer + assertTraces(4) { + trace(1) { + span { + serviceName "java-aws-sdk" + operationName "aws.http" + resourceName "S3.CreateMultipartUpload" + spanType DDSpanTypes.HTTP_CLIENT + tags { + defaultTags() + tag "component", "java-aws-sdk" + tag "aws.operation", "CreateMultipartUpload" + tag "aws.service", "S3" + tag "aws_service", "S3" + tag "aws.agent", "java-aws-sdk" + tag "aws.bucket.name", bucketName + tag "aws.object.key", key + tag "bucketname", bucketName + tag "http.method", "POST" + tag "http.status_code", 200 + tag "http.url", { it.startsWith("http://localhost") && it.contains("/$key") } + tag "peer.hostname", "localhost" + tag "peer.port", { it instanceof Integer } + tag "span.kind", "client" + tag "aws.requestId", { it != null } + tag "http.query.string", "uploads" + } + } + } + trace(1) { + span { + serviceName "java-aws-sdk" + operationName "aws.http" + resourceName "S3.UploadPart" + spanType DDSpanTypes.HTTP_CLIENT + tags { + defaultTags() + tag "component", "java-aws-sdk" + tag "aws.operation", "UploadPart" + tag "aws.service", "S3" + tag "aws_service", "S3" + tag "aws.agent", "java-aws-sdk" + tag "aws.bucket.name", bucketName + tag "aws.object.key", key + tag "bucketname", bucketName + tag "http.method", "PUT" + tag "http.status_code", 200 + tag "http.url", { it.startsWith("http://localhost") && it.contains("/$key") } + tag "peer.hostname", "localhost" + tag "peer.port", { it instanceof Integer } + tag "span.kind", "client" + tag "aws.requestId", { it != null } + tag "http.query.string", { it != null } + } + } + } + trace(1) { + span { + serviceName "java-aws-sdk" + operationName "aws.http" + resourceName "S3.UploadPart" + spanType DDSpanTypes.HTTP_CLIENT + tags { + defaultTags() + tag "component", "java-aws-sdk" + tag "aws.operation", "UploadPart" + tag "aws.service", "S3" + tag "aws_service", "S3" + tag "aws.agent", "java-aws-sdk" + tag "aws.bucket.name", bucketName + tag "aws.object.key", key + tag "bucketname", bucketName + tag "http.method", "PUT" + tag "http.status_code", 200 + tag "http.url", { it.startsWith("http://localhost") && it.contains("/$key") } + tag "peer.hostname", "localhost" + tag "peer.port", { it instanceof Integer } + tag "span.kind", "client" + tag "aws.requestId", { it != null } + tag "http.query.string", { it != null } + } + } + } + trace(1) { + span { + serviceName "java-aws-sdk" + operationName "aws.http" + resourceName "S3.CompleteMultipartUpload" + spanType DDSpanTypes.HTTP_CLIENT + tags { + defaultTags() + tag "component", "java-aws-sdk" + tag "aws.operation", "CompleteMultipartUpload" + tag "aws.service", "S3" + tag "aws_service", "S3" + tag "aws.agent", "java-aws-sdk" + tag "aws.bucket.name", bucketName + tag "aws.object.key", key + tag "bucketname", bucketName + tag "http.method", "POST" + tag "http.status_code", 200 + tag "http.url", { it.startsWith("http://localhost") && it.contains("/$key") } + tag "peer.hostname", "localhost" + tag "peer.port", { it instanceof Integer } + tag "span.kind", "client" + tag "aws.requestId", { it != null } + tag "http.query.string", { it != null } + tag "_dd.span_links", { it != null } + + // Assert the span links + def spanLinks = tags["_dd.span_links"] + assert spanLinks != null + def links = new JsonSlurper().parseText(spanLinks) + assert links.size() == 1 + def link = links[0] + assert link["attributes"] != null + assert link["attributes"]["ptr.kind"] == SpanPointersHelper.S3_PTR_KIND + assert link["attributes"]["ptr.dir"] == SpanPointersHelper.DOWN_DIRECTION + assert link["attributes"]["ptr.hash"] == "422412aa6b472a7194f3e24f4b12b4a6" + assert link["attributes"]["link.kind"] == SpanPointersHelper.LINK_KIND + } + } + } + } + } +} From 49a2179c835ed59280784a92d9a47b6b9aa26049 Mon Sep 17 00:00:00 2001 From: Nicholas Hulston Date: Tue, 10 Dec 2024 16:02:39 -0500 Subject: [PATCH 06/20] Specify correct versions to instrument --- dd-java-agent/instrumentation/aws-java-s3-2.0/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dd-java-agent/instrumentation/aws-java-s3-2.0/build.gradle b/dd-java-agent/instrumentation/aws-java-s3-2.0/build.gradle index 017ba58f8a0..de89acf7e20 100644 --- a/dd-java-agent/instrumentation/aws-java-s3-2.0/build.gradle +++ b/dd-java-agent/instrumentation/aws-java-s3-2.0/build.gradle @@ -2,7 +2,7 @@ muzzle { pass { group = "software.amazon.awssdk" module = "s3" - versions = "[2.0,3)" + versions = "[2.10.36,3)" assertInverse = true } } From fc9b6968b61370ec327018b438cb77003fcd0455 Mon Sep 17 00:00:00 2001 From: Nicholas Hulston Date: Thu, 12 Dec 2024 16:25:16 -0500 Subject: [PATCH 07/20] code review improvements --- dd-java-agent/instrumentation/aws-java-s3-2.0/build.gradle | 4 ++-- .../instrumentation/aws/v2/s3/S3ClientInstrumentation.java | 2 +- .../trace/instrumentation/aws/v2/s3/S3Interceptor.java | 5 ++++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/dd-java-agent/instrumentation/aws-java-s3-2.0/build.gradle b/dd-java-agent/instrumentation/aws-java-s3-2.0/build.gradle index de89acf7e20..d6cfcbb7104 100644 --- a/dd-java-agent/instrumentation/aws-java-s3-2.0/build.gradle +++ b/dd-java-agent/instrumentation/aws-java-s3-2.0/build.gradle @@ -16,8 +16,8 @@ dependencies { compileOnly group: 'software.amazon.awssdk', name: 's3', version: '2.29.26' // Include httpclient instrumentation for testing because it is a dependency for aws-sdk. - testImplementation project(':dd-java-agent:instrumentation:apache-httpclient-4') - testImplementation project(':dd-java-agent:instrumentation:aws-java-sdk-2.2') + testRuntimeOnly project(':dd-java-agent:instrumentation:apache-httpclient-4') + testRuntimeOnly project(':dd-java-agent:instrumentation:aws-java-sdk-2.2') testImplementation 'software.amazon.awssdk:s3:2.29.26' testImplementation 'org.testcontainers:localstack:1.20.1' diff --git a/dd-java-agent/instrumentation/aws-java-s3-2.0/src/main/java/datadog/trace/instrumentation/aws/v2/s3/S3ClientInstrumentation.java b/dd-java-agent/instrumentation/aws-java-s3-2.0/src/main/java/datadog/trace/instrumentation/aws/v2/s3/S3ClientInstrumentation.java index 6ff1eada9c7..f7d50571826 100644 --- a/dd-java-agent/instrumentation/aws-java-s3-2.0/src/main/java/datadog/trace/instrumentation/aws/v2/s3/S3ClientInstrumentation.java +++ b/dd-java-agent/instrumentation/aws-java-s3-2.0/src/main/java/datadog/trace/instrumentation/aws/v2/s3/S3ClientInstrumentation.java @@ -14,7 +14,7 @@ public final class S3ClientInstrumentation extends InstrumenterModule.Tracing implements Instrumenter.ForSingleType { public S3ClientInstrumentation() { - super("s3"); + super("s3", "aws-s3"); } @Override diff --git a/dd-java-agent/instrumentation/aws-java-s3-2.0/src/main/java/datadog/trace/instrumentation/aws/v2/s3/S3Interceptor.java b/dd-java-agent/instrumentation/aws-java-s3-2.0/src/main/java/datadog/trace/instrumentation/aws/v2/s3/S3Interceptor.java index cd8d582da8a..480a6aaa4e5 100644 --- a/dd-java-agent/instrumentation/aws-java-s3-2.0/src/main/java/datadog/trace/instrumentation/aws/v2/s3/S3Interceptor.java +++ b/dd-java-agent/instrumentation/aws-java-s3-2.0/src/main/java/datadog/trace/instrumentation/aws/v2/s3/S3Interceptor.java @@ -62,7 +62,10 @@ public void afterExecution( // Hash calculation rules: // https://github.com/DataDog/dd-span-pointer-rules/blob/main/AWS/S3/Object/README.md - if (eTag.startsWith("\"") && eTag.endsWith("\"")) { + if (eTag != null + && !eTag.isEmpty() + && eTag.charAt(0) == '"' + && eTag.charAt(eTag.length() - 1) == '"') { eTag = eTag.substring(1, eTag.length() - 1); } String[] components = new String[] {bucket, key, eTag}; From 1d445fd8dc3d749ee07d844574a32c1e39bbec6b Mon Sep 17 00:00:00 2001 From: Nicholas Hulston Date: Tue, 7 Jan 2025 11:59:21 -0500 Subject: [PATCH 08/20] update generatePointerHash; avoid operation. Approx 5-10% improvement in speed --- .../spanpointers/SpanPointersHelper.java | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/spanpointers/SpanPointersHelper.java b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/spanpointers/SpanPointersHelper.java index bc03a6f76e3..c63e329ed42 100644 --- a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/spanpointers/SpanPointersHelper.java +++ b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/spanpointers/SpanPointersHelper.java @@ -25,13 +25,23 @@ public final class SpanPointersHelper { * @throws NoSuchAlgorithmException this should never happen; but should be handled just in case. */ private static String generatePointerHash(String[] components) throws NoSuchAlgorithmException { - byte[] hash = - MessageDigest.getInstance("SHA-256") - .digest(String.join("|", components).getBytes(StandardCharsets.UTF_8)); + MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); + // Update the digest incrementally for each component. + boolean first = true; + for (String component : components) { + if (!first) { + messageDigest.update((byte) '|'); + } else { + first = false; + } + messageDigest.update(component.getBytes(StandardCharsets.UTF_8)); + } + + byte[] fullHash = messageDigest.digest(); StringBuilder hex = new StringBuilder(32); for (int i = 0; i < 16; i++) { - hex.append(String.format("%02x", hash[i])); + hex.append(String.format("%02x", fullHash[i])); } return hex.toString(); From 204c49e2ae4e8075c6803f04f9d413a54cb21d6f Mon Sep 17 00:00:00 2001 From: Nicholas Hulston Date: Wed, 8 Jan 2025 15:00:32 -0500 Subject: [PATCH 09/20] add 'implements Instrumenter.HasMethodAdvice' to S3ClientInstrumentation --- .../instrumentation/aws/v2/s3/S3ClientInstrumentation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dd-java-agent/instrumentation/aws-java-s3-2.0/src/main/java/datadog/trace/instrumentation/aws/v2/s3/S3ClientInstrumentation.java b/dd-java-agent/instrumentation/aws-java-s3-2.0/src/main/java/datadog/trace/instrumentation/aws/v2/s3/S3ClientInstrumentation.java index f7d50571826..6f28c9aef98 100644 --- a/dd-java-agent/instrumentation/aws-java-s3-2.0/src/main/java/datadog/trace/instrumentation/aws/v2/s3/S3ClientInstrumentation.java +++ b/dd-java-agent/instrumentation/aws-java-s3-2.0/src/main/java/datadog/trace/instrumentation/aws/v2/s3/S3ClientInstrumentation.java @@ -12,7 +12,7 @@ @AutoService(InstrumenterModule.class) public final class S3ClientInstrumentation extends InstrumenterModule.Tracing - implements Instrumenter.ForSingleType { + implements Instrumenter.ForSingleType, Instrumenter.HasMethodAdvice { public S3ClientInstrumentation() { super("s3", "aws-s3"); } From f88201d8fdc0ccac83c050854e83c4849277c4e0 Mon Sep 17 00:00:00 2001 From: Nicholas Hulston Date: Wed, 8 Jan 2025 16:13:34 -0500 Subject: [PATCH 10/20] Add env var for disabling span pointers --- .../trace/instrumentation/aws/v2/s3/S3Interceptor.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dd-java-agent/instrumentation/aws-java-s3-2.0/src/main/java/datadog/trace/instrumentation/aws/v2/s3/S3Interceptor.java b/dd-java-agent/instrumentation/aws-java-s3-2.0/src/main/java/datadog/trace/instrumentation/aws/v2/s3/S3Interceptor.java index 480a6aaa4e5..9a18414202a 100644 --- a/dd-java-agent/instrumentation/aws-java-s3-2.0/src/main/java/datadog/trace/instrumentation/aws/v2/s3/S3Interceptor.java +++ b/dd-java-agent/instrumentation/aws-java-s3-2.0/src/main/java/datadog/trace/instrumentation/aws/v2/s3/S3Interceptor.java @@ -28,6 +28,11 @@ public class S3Interceptor implements ExecutionInterceptor { @Override public void afterExecution( Context.AfterExecution context, ExecutionAttributes executionAttributes) { + String flag = System.getenv("DD_AWS_SDK_ADD_SPAN_POINTERS"); + if (flag != null && flag.equalsIgnoreCase("false")) { + return; + } + AgentSpan span = executionAttributes.getAttribute(SPAN_ATTRIBUTE); if (span == null) { log.debug("Unable to find S3 request span. Not creating span pointer."); From 0350cc974d58d4f22278dbee973cc9b643c0ee6e Mon Sep 17 00:00:00 2001 From: Nicholas Hulston Date: Mon, 13 Jan 2025 10:56:28 -0500 Subject: [PATCH 11/20] Move env var to Config class --- .../trace/instrumentation/aws/v2/s3/S3Interceptor.java | 4 ++-- .../trace/api/config/TraceInstrumentationConfig.java | 1 + internal-api/src/main/java/datadog/trace/api/Config.java | 6 ++++++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/dd-java-agent/instrumentation/aws-java-s3-2.0/src/main/java/datadog/trace/instrumentation/aws/v2/s3/S3Interceptor.java b/dd-java-agent/instrumentation/aws-java-s3-2.0/src/main/java/datadog/trace/instrumentation/aws/v2/s3/S3Interceptor.java index 9a18414202a..53f5482b81d 100644 --- a/dd-java-agent/instrumentation/aws-java-s3-2.0/src/main/java/datadog/trace/instrumentation/aws/v2/s3/S3Interceptor.java +++ b/dd-java-agent/instrumentation/aws-java-s3-2.0/src/main/java/datadog/trace/instrumentation/aws/v2/s3/S3Interceptor.java @@ -3,6 +3,7 @@ import static datadog.trace.bootstrap.instrumentation.spanpointers.SpanPointersHelper.S3_PTR_KIND; import static datadog.trace.bootstrap.instrumentation.spanpointers.SpanPointersHelper.addSpanPointer; +import datadog.trace.api.Config; import datadog.trace.bootstrap.InstanceStore; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; import org.slf4j.Logger; @@ -28,8 +29,7 @@ public class S3Interceptor implements ExecutionInterceptor { @Override public void afterExecution( Context.AfterExecution context, ExecutionAttributes executionAttributes) { - String flag = System.getenv("DD_AWS_SDK_ADD_SPAN_POINTERS"); - if (flag != null && flag.equalsIgnoreCase("false")) { + if (!Config.get().isSpanPointersEnabled()) { return; } diff --git a/dd-trace-api/src/main/java/datadog/trace/api/config/TraceInstrumentationConfig.java b/dd-trace-api/src/main/java/datadog/trace/api/config/TraceInstrumentationConfig.java index f15f2899d79..495f42c5da0 100644 --- a/dd-trace-api/src/main/java/datadog/trace/api/config/TraceInstrumentationConfig.java +++ b/dd-trace-api/src/main/java/datadog/trace/api/config/TraceInstrumentationConfig.java @@ -165,6 +165,7 @@ public final class TraceInstrumentationConfig { public static final String AXIS_PROMOTE_RESOURCE_NAME = "trace.axis.promote.resource-name"; public static final String SQS_BODY_PROPAGATION_ENABLED = "trace.sqs.body.propagation.enabled"; + public static final String ADD_SPAN_POINTERS = "aws.add.span.pointers"; private TraceInstrumentationConfig() {} } diff --git a/internal-api/src/main/java/datadog/trace/api/Config.java b/internal-api/src/main/java/datadog/trace/api/Config.java index 02b78b68031..5d722f4706b 100644 --- a/internal-api/src/main/java/datadog/trace/api/Config.java +++ b/internal-api/src/main/java/datadog/trace/api/Config.java @@ -422,6 +422,7 @@ public static String getHostName() { private final boolean awsPropagationEnabled; private final boolean sqsPropagationEnabled; private final boolean sqsBodyPropagationEnabled; + private final boolean addSpanPointers; private final boolean kafkaClientPropagationEnabled; private final Set kafkaClientPropagationDisabledTopics; @@ -1600,6 +1601,7 @@ PROFILING_DATADOG_PROFILER_ENABLED, isDatadogProfilerSafeInCurrentEnvironment()) awsPropagationEnabled = isPropagationEnabled(true, "aws", "aws-sdk"); sqsPropagationEnabled = isPropagationEnabled(true, "sqs"); sqsBodyPropagationEnabled = configProvider.getBoolean(SQS_BODY_PROPAGATION_ENABLED, false); + addSpanPointers = configProvider.getBoolean(ADD_SPAN_POINTERS, true); kafkaClientPropagationEnabled = isPropagationEnabled(true, "kafka", "kafka.client"); kafkaClientPropagationDisabledTopics = @@ -3135,6 +3137,10 @@ public boolean isSqsBodyPropagationEnabled() { return sqsBodyPropagationEnabled; } + public boolean isSpanPointersEnabled() { + return addSpanPointers; + } + public boolean isKafkaClientPropagationEnabled() { return kafkaClientPropagationEnabled; } From cc6b675678da9b4076b3f0b59e73f75291d013aa Mon Sep 17 00:00:00 2001 From: Nicholas Hulston Date: Wed, 15 Jan 2025 13:40:18 -0500 Subject: [PATCH 12/20] Move hash calculation and span pointer modification to `SpanPointersProcessor` for async execution --- .../spanpointers/SpanPointersHelper.java | 74 ------------- .../aws/v2/s3/S3Interceptor.java | 46 ++------ .../datadog/trace/core/DDSpanContext.java | 3 +- .../tagprocessor/SpanPointersProcessor.java | 101 ++++++++++++++++++ .../core/tagprocessor/TagsPostProcessor.java | 5 +- .../TagsPostProcessorFactory.java | 3 + 6 files changed, 119 insertions(+), 113 deletions(-) delete mode 100644 dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/spanpointers/SpanPointersHelper.java create mode 100644 dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/SpanPointersProcessor.java diff --git a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/spanpointers/SpanPointersHelper.java b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/spanpointers/SpanPointersHelper.java deleted file mode 100644 index c63e329ed42..00000000000 --- a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/spanpointers/SpanPointersHelper.java +++ /dev/null @@ -1,74 +0,0 @@ -package datadog.trace.bootstrap.instrumentation.spanpointers; - -import datadog.trace.bootstrap.instrumentation.api.AgentSpan; -import datadog.trace.bootstrap.instrumentation.api.AgentSpanLink; -import datadog.trace.bootstrap.instrumentation.api.AgentTracer; -import datadog.trace.bootstrap.instrumentation.api.SpanAttributes; -import datadog.trace.bootstrap.instrumentation.api.SpanLink; -import java.nio.charset.StandardCharsets; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; - -public final class SpanPointersHelper { - public static final String S3_PTR_KIND = "aws.s3.object"; - public static final String LINK_KIND = "span-pointer"; - - // The pointer direction will always be down. The agent handles cases where the direction is up. - public static final String DOWN_DIRECTION = "d"; - - /** - * Generates a unique hash from an array of strings by joining them with | before hashing. Used to - * uniquely identify AWS requests for span pointers. - * - * @param components Array of strings to hash - * @return A 32-character hash uniquely identifying the components - * @throws NoSuchAlgorithmException this should never happen; but should be handled just in case. - */ - private static String generatePointerHash(String[] components) throws NoSuchAlgorithmException { - MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); - - // Update the digest incrementally for each component. - boolean first = true; - for (String component : components) { - if (!first) { - messageDigest.update((byte) '|'); - } else { - first = false; - } - messageDigest.update(component.getBytes(StandardCharsets.UTF_8)); - } - - byte[] fullHash = messageDigest.digest(); - StringBuilder hex = new StringBuilder(32); - for (int i = 0; i < 16; i++) { - hex.append(String.format("%02x", fullHash[i])); - } - - return hex.toString(); - } - - /** - * Adds a span pointer to the given span, using the SHA-256 hash of the components. - * - * @param span The span to add the pointer to - * @param kind Identifies which hashing rules to follow - * @param components Array of strings to hash, following span pointer rules - * @throws NoSuchAlgorithmException if unable to calculate hash - * @see Span pointer - * rules - */ - public static void addSpanPointer(AgentSpan span, String kind, String[] components) - throws NoSuchAlgorithmException { - SpanAttributes attributes = - (SpanAttributes) - SpanAttributes.builder() - .put("ptr.kind", kind) - .put("ptr.dir", DOWN_DIRECTION) - .put("ptr.hash", generatePointerHash(components)) - .put("link.kind", LINK_KIND) - .build(); - - AgentTracer.NoopContext zeroContext = AgentTracer.NoopContext.INSTANCE; - span.addLink(SpanLink.from(zeroContext, AgentSpanLink.DEFAULT_FLAGS, "", attributes)); - } -} diff --git a/dd-java-agent/instrumentation/aws-java-s3-2.0/src/main/java/datadog/trace/instrumentation/aws/v2/s3/S3Interceptor.java b/dd-java-agent/instrumentation/aws-java-s3-2.0/src/main/java/datadog/trace/instrumentation/aws/v2/s3/S3Interceptor.java index 53f5482b81d..d38d1ab5030 100644 --- a/dd-java-agent/instrumentation/aws-java-s3-2.0/src/main/java/datadog/trace/instrumentation/aws/v2/s3/S3Interceptor.java +++ b/dd-java-agent/instrumentation/aws-java-s3-2.0/src/main/java/datadog/trace/instrumentation/aws/v2/s3/S3Interceptor.java @@ -1,8 +1,5 @@ package datadog.trace.instrumentation.aws.v2.s3; -import static datadog.trace.bootstrap.instrumentation.spanpointers.SpanPointersHelper.S3_PTR_KIND; -import static datadog.trace.bootstrap.instrumentation.spanpointers.SpanPointersHelper.addSpanPointer; - import datadog.trace.api.Config; import datadog.trace.bootstrap.InstanceStore; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; @@ -12,11 +9,8 @@ import software.amazon.awssdk.core.interceptor.ExecutionAttribute; import software.amazon.awssdk.core.interceptor.ExecutionAttributes; import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; -import software.amazon.awssdk.services.s3.model.CompleteMultipartUploadRequest; import software.amazon.awssdk.services.s3.model.CompleteMultipartUploadResponse; -import software.amazon.awssdk.services.s3.model.CopyObjectRequest; import software.amazon.awssdk.services.s3.model.CopyObjectResponse; -import software.amazon.awssdk.services.s3.model.PutObjectRequest; import software.amazon.awssdk.services.s3.model.PutObjectResponse; public class S3Interceptor implements ExecutionInterceptor { @@ -38,46 +32,24 @@ public void afterExecution( log.debug("Unable to find S3 request span. Not creating span pointer."); return; } - - String bucket, key, eTag; - Object request = context.request(); + String eTag; Object response = context.response(); - // Get bucket, key, and eTag for hash calculation. + // Get eTag for hash calculation. // https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3Client.html - if (request instanceof PutObjectRequest) { - PutObjectRequest putObjectRequest = (PutObjectRequest) request; - bucket = putObjectRequest.bucket(); - key = putObjectRequest.key(); + if (response instanceof PutObjectResponse) { eTag = ((PutObjectResponse) response).eTag(); - } else if (request instanceof CopyObjectRequest) { - CopyObjectRequest copyObjectRequest = (CopyObjectRequest) request; - bucket = copyObjectRequest.destinationBucket(); - key = copyObjectRequest.destinationKey(); + } else if (response instanceof CopyObjectResponse) { eTag = ((CopyObjectResponse) response).copyObjectResult().eTag(); - } else if (request instanceof CompleteMultipartUploadRequest) { - CompleteMultipartUploadRequest completeMultipartUploadRequest = - (CompleteMultipartUploadRequest) request; - bucket = completeMultipartUploadRequest.bucket(); - key = completeMultipartUploadRequest.key(); + } else if (response instanceof CompleteMultipartUploadResponse) { eTag = ((CompleteMultipartUploadResponse) response).eTag(); } else { return; } - // Hash calculation rules: - // https://github.com/DataDog/dd-span-pointer-rules/blob/main/AWS/S3/Object/README.md - if (eTag != null - && !eTag.isEmpty() - && eTag.charAt(0) == '"' - && eTag.charAt(eTag.length() - 1) == '"') { - eTag = eTag.substring(1, eTag.length() - 1); - } - String[] components = new String[] {bucket, key, eTag}; - try { - addSpanPointer(span, S3_PTR_KIND, components); - } catch (Exception e) { - log.debug("Failed to add span pointer: {}", e.getMessage()); - } + // 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); } } diff --git a/dd-trace-core/src/main/java/datadog/trace/core/DDSpanContext.java b/dd-trace-core/src/main/java/datadog/trace/core/DDSpanContext.java index d8092874bb9..fcbf046122a 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/DDSpanContext.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/DDSpanContext.java @@ -848,7 +848,8 @@ public void processTagsAndBaggage( final MetadataConsumer consumer, int longRunningVersion, List links) { synchronized (unsafeTags) { // Tags - Map tags = TagsPostProcessorFactory.instance().processTags(unsafeTags, this); + Map tags = + TagsPostProcessorFactory.instance().processTags(unsafeTags, this, links); String linksTag = DDSpanLink.toTag(links); if (linksTag != null) { tags.put(SPAN_LINKS, linksTag); diff --git a/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/SpanPointersProcessor.java b/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/SpanPointersProcessor.java new file mode 100644 index 00000000000..e8d321be8a8 --- /dev/null +++ b/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/SpanPointersProcessor.java @@ -0,0 +1,101 @@ +package datadog.trace.core.tagprocessor; + +import datadog.trace.bootstrap.instrumentation.api.AgentSpanLink; +import datadog.trace.bootstrap.instrumentation.api.AgentTracer; +import datadog.trace.bootstrap.instrumentation.api.InstrumentationTags; +import datadog.trace.bootstrap.instrumentation.api.SpanAttributes; +import datadog.trace.bootstrap.instrumentation.api.SpanLink; +import datadog.trace.core.DDSpanContext; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.List; +import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SpanPointersProcessor implements TagsPostProcessor { + private static final Logger log = LoggerFactory.getLogger(SpanPointersProcessor.class); + + // The pointer direction will always be down. The serverless agent handles cases where the + // direction is up. + public static final String DOWN_DIRECTION = "d"; + public static final String S3_PTR_KIND = "aws.s3.object"; + public static final String LINK_KIND = "span-pointer"; + private static final String eTagKey = "s3.eTag"; + + @Override + public Map processTags( + Map unsafeTags, DDSpanContext spanContext, List spanLinks) { + String eTag = asString(unsafeTags.get(eTagKey)); + if (eTag == null) { + return unsafeTags; + } + String bucket = asString(unsafeTags.get(InstrumentationTags.AWS_BUCKET_NAME)); + String key = asString(unsafeTags.get(InstrumentationTags.AWS_OBJECT_KEY)); + if (bucket == null || key == null) { + log.debug("Unable to calculate span pointer hash because could not find bucket or key tags."); + return unsafeTags; + } + + // Hash calculation rules: + // https://github.com/DataDog/dd-span-pointer-rules/blob/main/AWS/S3/Object/README.md + if (!eTag.isEmpty() && eTag.charAt(0) == '"' && eTag.charAt(eTag.length() - 1) == '"') { + eTag = eTag.substring(1, eTag.length() - 1); + } + String[] components = new String[] {bucket, key, eTag}; + try { + SpanAttributes attributes = + SpanAttributes.builder() + .put("ptr.kind", S3_PTR_KIND) + .put("ptr.dir", DOWN_DIRECTION) + .put("ptr.hash", generatePointerHash(components)) + .put("link.kind", LINK_KIND) + .build(); + + AgentTracer.NoopContext zeroContext = AgentTracer.NoopContext.INSTANCE; + AgentSpanLink link = SpanLink.from(zeroContext, AgentSpanLink.DEFAULT_FLAGS, "", attributes); + spanLinks.add(link); + } catch (Exception e) { + log.debug("Failed to add span pointer: {}", e.getMessage()); + } + unsafeTags.remove(eTagKey); + + return unsafeTags; + } + + private static String asString(Object o) { + return o instanceof String ? (String) o : null; + } + + /** + * Generates a unique hash from an array of strings by joining them with | before hashing. Used to + * uniquely identify AWS requests for span pointers. + * + * @param components Array of strings to hash + * @return A 32-character hash uniquely identifying the components + * @throws NoSuchAlgorithmException this should never happen; but should be handled just in case. + */ + private static String generatePointerHash(String[] components) throws NoSuchAlgorithmException { + MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); + + // Update the digest incrementally for each component. + boolean first = true; + for (String component : components) { + if (!first) { + messageDigest.update((byte) '|'); + } else { + first = false; + } + messageDigest.update(component.getBytes(StandardCharsets.UTF_8)); + } + + byte[] fullHash = messageDigest.digest(); + StringBuilder hex = new StringBuilder(32); + for (int i = 0; i < 16; i++) { + hex.append(String.format("%02x", fullHash[i])); + } + + return hex.toString(); + } +} diff --git a/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/TagsPostProcessor.java b/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/TagsPostProcessor.java index 423d3654074..f188e10d090 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/TagsPostProcessor.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/TagsPostProcessor.java @@ -1,8 +1,11 @@ package datadog.trace.core.tagprocessor; +import datadog.trace.bootstrap.instrumentation.api.AgentSpanLink; import datadog.trace.core.DDSpanContext; +import java.util.List; import java.util.Map; public interface TagsPostProcessor { - Map processTags(Map unsafeTags, DDSpanContext spanContext); + Map processTags( + Map unsafeTags, DDSpanContext spanContext, List spanLinks); } diff --git a/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/TagsPostProcessorFactory.java b/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/TagsPostProcessorFactory.java index 33fe68dfe4b..58ee80a0d0a 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/TagsPostProcessorFactory.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/TagsPostProcessorFactory.java @@ -25,6 +25,9 @@ private static TagsPostProcessor create() { processors.add(ptp); } } + if (Config.get().isSpanPointersEnabled()) { + processors.add(new SpanPointersProcessor()); + } return new PostProcessorChain( processors.toArray(processors.toArray(new TagsPostProcessor[0]))); } From 7f49f827d8783d845cc3a3e9085d084fd689790c Mon Sep 17 00:00:00 2001 From: Nicholas Hulston Date: Wed, 15 Jan 2025 13:41:13 -0500 Subject: [PATCH 13/20] update other implementations of TagsPostProcessor --- .../datadog/trace/core/tagprocessor/BaseServiceAdder.java | 4 +++- .../trace/core/tagprocessor/PayloadTagsProcessor.java | 4 +++- .../trace/core/tagprocessor/PeerServiceCalculator.java | 4 +++- .../datadog/trace/core/tagprocessor/PostProcessorChain.java | 6 ++++-- .../datadog/trace/core/tagprocessor/QueryObfuscator.java | 4 +++- .../trace/core/tagprocessor/RemoteHostnameAdder.java | 4 +++- 6 files changed, 19 insertions(+), 7 deletions(-) diff --git a/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/BaseServiceAdder.java b/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/BaseServiceAdder.java index 9184213f369..c855262f048 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/BaseServiceAdder.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/BaseServiceAdder.java @@ -1,8 +1,10 @@ package datadog.trace.core.tagprocessor; import datadog.trace.api.DDTags; +import datadog.trace.bootstrap.instrumentation.api.AgentSpanLink; import datadog.trace.bootstrap.instrumentation.api.UTF8BytesString; import datadog.trace.core.DDSpanContext; +import java.util.List; import java.util.Map; import javax.annotation.Nullable; @@ -15,7 +17,7 @@ public BaseServiceAdder(@Nullable final String ddService) { @Override public Map processTags( - Map unsafeTags, DDSpanContext spanContext) { + Map unsafeTags, DDSpanContext spanContext, List spanLinks) { if (ddService != null && spanContext != null && !ddService.toString().equalsIgnoreCase(spanContext.getServiceName())) { diff --git a/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/PayloadTagsProcessor.java b/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/PayloadTagsProcessor.java index 22a0752a413..d50c8510295 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/PayloadTagsProcessor.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/PayloadTagsProcessor.java @@ -3,6 +3,7 @@ import datadog.trace.api.Config; import datadog.trace.api.ConfigDefaults; import datadog.trace.api.telemetry.LogCollector; +import datadog.trace.bootstrap.instrumentation.api.AgentSpanLink; import datadog.trace.core.DDSpanContext; import datadog.trace.payloadtags.PayloadTagsData; import datadog.trace.payloadtags.json.JsonPath; @@ -68,7 +69,8 @@ public static PayloadTagsProcessor create(Config config) { } @Override - public Map processTags(Map spanTags, DDSpanContext spanContext) { + public Map processTags( + Map spanTags, DDSpanContext spanContext, List spanLinks) { int spanMaxTags = maxTags + spanTags.size(); for (Map.Entry tagPrefixRedactionRules : redactionRulesByTagPrefix.entrySet()) { diff --git a/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/PeerServiceCalculator.java b/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/PeerServiceCalculator.java index 9167bde7e10..625fae0f839 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/PeerServiceCalculator.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/PeerServiceCalculator.java @@ -4,8 +4,10 @@ import datadog.trace.api.DDTags; import datadog.trace.api.naming.NamingSchema; import datadog.trace.api.naming.SpanNaming; +import datadog.trace.bootstrap.instrumentation.api.AgentSpanLink; import datadog.trace.bootstrap.instrumentation.api.Tags; import datadog.trace.core.DDSpanContext; +import java.util.List; import java.util.Map; import javax.annotation.Nonnull; @@ -31,7 +33,7 @@ public PeerServiceCalculator() { @Override public Map processTags( - Map unsafeTags, DDSpanContext spanContext) { + Map unsafeTags, DDSpanContext spanContext, List spanLinks) { Object peerService = unsafeTags.get(Tags.PEER_SERVICE); // the user set it if (peerService != null) { diff --git a/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/PostProcessorChain.java b/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/PostProcessorChain.java index 32156b50356..fbf5b511e24 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/PostProcessorChain.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/PostProcessorChain.java @@ -1,6 +1,8 @@ package datadog.trace.core.tagprocessor; +import datadog.trace.bootstrap.instrumentation.api.AgentSpanLink; import datadog.trace.core.DDSpanContext; +import java.util.List; import java.util.Map; import java.util.Objects; import javax.annotation.Nonnull; @@ -14,10 +16,10 @@ public PostProcessorChain(@Nonnull final TagsPostProcessor... processors) { @Override public Map processTags( - Map unsafeTags, DDSpanContext spanContext) { + Map unsafeTags, DDSpanContext spanContext, List spanLinks) { Map currentTags = unsafeTags; for (final TagsPostProcessor tagsPostProcessor : chain) { - currentTags = tagsPostProcessor.processTags(currentTags, spanContext); + currentTags = tagsPostProcessor.processTags(currentTags, spanContext, spanLinks); } return currentTags; } diff --git a/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/QueryObfuscator.java b/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/QueryObfuscator.java index 1af11529398..934aa5df1f0 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/QueryObfuscator.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/QueryObfuscator.java @@ -4,9 +4,11 @@ import com.google.re2j.Pattern; import com.google.re2j.PatternSyntaxException; import datadog.trace.api.DDTags; +import datadog.trace.bootstrap.instrumentation.api.AgentSpanLink; import datadog.trace.bootstrap.instrumentation.api.Tags; import datadog.trace.core.DDSpanContext; import datadog.trace.util.Strings; +import java.util.List; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -57,7 +59,7 @@ private String obfuscate(String query) { @Override public Map processTags( - Map unsafeTags, DDSpanContext spanContext) { + Map unsafeTags, DDSpanContext spanContext, List spanLinks) { Object query = unsafeTags.get(DDTags.HTTP_QUERY); if (query instanceof CharSequence) { query = obfuscate(query.toString()); diff --git a/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/RemoteHostnameAdder.java b/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/RemoteHostnameAdder.java index 0cf80cd18c0..b872b824e38 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/RemoteHostnameAdder.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/RemoteHostnameAdder.java @@ -1,7 +1,9 @@ package datadog.trace.core.tagprocessor; import datadog.trace.api.DDTags; +import datadog.trace.bootstrap.instrumentation.api.AgentSpanLink; import datadog.trace.core.DDSpanContext; +import java.util.List; import java.util.Map; import java.util.function.Supplier; @@ -14,7 +16,7 @@ public RemoteHostnameAdder(Supplier hostnameSupplier) { @Override public Map processTags( - Map unsafeTags, DDSpanContext spanContext) { + Map unsafeTags, DDSpanContext spanContext, List spanLinks) { if (spanContext.getSpanId() == spanContext.getRootSpanId()) { unsafeTags.put(DDTags.TRACER_HOST, hostnameSupplier.get()); } From d048f4b670d4cb89256b3a840a6aac3ad394828c Mon Sep 17 00:00:00 2001 From: Nicholas Hulston Date: Wed, 15 Jan 2025 14:25:51 -0500 Subject: [PATCH 14/20] update and migrate tests --- .../SpanPointersHelperTest.groovy | 75 ------------- .../src/test/groovy/S3ClientTest.groovy | 20 ++-- .../tagprocessor/SpanPointersProcessor.java | 12 +-- .../PostProcessorChainTest.groovy | 9 +- .../SpanPointersProcessorTest.groovy | 100 ++++++++++++++++++ 5 files changed, 121 insertions(+), 95 deletions(-) delete mode 100644 dd-java-agent/agent-bootstrap/src/test/groovy/datadog/trace/bootstrap/instrumentation/spanpointers/SpanPointersHelperTest.groovy create mode 100644 dd-trace-core/src/test/groovy/datadog/trace/core/tagprocessor/SpanPointersProcessorTest.groovy diff --git a/dd-java-agent/agent-bootstrap/src/test/groovy/datadog/trace/bootstrap/instrumentation/spanpointers/SpanPointersHelperTest.groovy b/dd-java-agent/agent-bootstrap/src/test/groovy/datadog/trace/bootstrap/instrumentation/spanpointers/SpanPointersHelperTest.groovy deleted file mode 100644 index e78a920a51e..00000000000 --- a/dd-java-agent/agent-bootstrap/src/test/groovy/datadog/trace/bootstrap/instrumentation/spanpointers/SpanPointersHelperTest.groovy +++ /dev/null @@ -1,75 +0,0 @@ -package datadog.trace.bootstrap.instrumentation.spanpointers - -import datadog.trace.api.DDSpanId -import datadog.trace.api.DDTraceId -import datadog.trace.bootstrap.instrumentation.api.AgentSpan -import datadog.trace.bootstrap.instrumentation.api.SpanLink -import spock.lang.Specification - -class SpanPointersHelperTest extends Specification { - def "addSpanPointer adds correct link to span with basic values"() { - given: - AgentSpan span = Mock(AgentSpan) - String kind = SpanPointersHelper.S3_PTR_KIND - String[] components = ["some-bucket", "some-key.data", "ab12ef34"] - String expectedHash = "e721375466d4116ab551213fdea08413" - - when: - SpanPointersHelper.addSpanPointer(span, kind, components) - - then: - 1 * span.addLink({ SpanLink link -> - assert link.traceId() == DDTraceId.ZERO - assert link.spanId() == DDSpanId.ZERO - assert link.attributes.asMap().get("ptr.kind") == kind - assert link.attributes.asMap().get("ptr.dir") == SpanPointersHelper.DOWN_DIRECTION - assert link.attributes.asMap().get("ptr.hash") == expectedHash - assert link.attributes.asMap().get("link.kind") == SpanPointersHelper.LINK_KIND - true - }) - } - - def "addSpanPointer adds correct link to span with non-ascii key"() { - given: - AgentSpan span = Mock(AgentSpan) - String kind = SpanPointersHelper.S3_PTR_KIND - String[] components = ["some-bucket", "some-key.你好", "ab12ef34"] - String expectedHash = "d1333a04b9928ab462b5c6cadfa401f4" - - when: - SpanPointersHelper.addSpanPointer(span, kind, components) - - then: - 1 * span.addLink({ SpanLink link -> - assert link.traceId() == DDTraceId.ZERO - assert link.spanId() == DDSpanId.ZERO - assert link.attributes.asMap().get("ptr.kind") == kind - assert link.attributes.asMap().get("ptr.dir") == SpanPointersHelper.DOWN_DIRECTION - assert link.attributes.asMap().get("ptr.hash") == expectedHash - assert link.attributes.asMap().get("link.kind") == SpanPointersHelper.LINK_KIND - true - }) - } - - def "addSpanPointer adds correct link to span with multipart-upload"() { - given: - AgentSpan span = Mock(AgentSpan) - String kind = SpanPointersHelper.S3_PTR_KIND - String[] components = ["some-bucket", "some-key.data", "ab12ef34-5"] - String expectedHash = "2b90dffc37ebc7bc610152c3dc72af9f" - - when: - SpanPointersHelper.addSpanPointer(span, kind, components) - - then: - 1 * span.addLink({ SpanLink link -> - assert link.traceId() == DDTraceId.ZERO - assert link.spanId() == DDSpanId.ZERO - assert link.attributes.asMap().get("ptr.kind") == kind - assert link.attributes.asMap().get("ptr.dir") == SpanPointersHelper.DOWN_DIRECTION - assert link.attributes.asMap().get("ptr.hash") == expectedHash - assert link.attributes.asMap().get("link.kind") == SpanPointersHelper.LINK_KIND - true - }) - } -} diff --git a/dd-java-agent/instrumentation/aws-java-s3-2.0/src/test/groovy/S3ClientTest.groovy b/dd-java-agent/instrumentation/aws-java-s3-2.0/src/test/groovy/S3ClientTest.groovy index f840f23f3a3..2899e6deeba 100644 --- a/dd-java-agent/instrumentation/aws-java-s3-2.0/src/test/groovy/S3ClientTest.groovy +++ b/dd-java-agent/instrumentation/aws-java-s3-2.0/src/test/groovy/S3ClientTest.groovy @@ -1,6 +1,6 @@ import datadog.trace.agent.test.AgentTestRunner import datadog.trace.api.DDSpanTypes -import datadog.trace.bootstrap.instrumentation.spanpointers.SpanPointersHelper +import datadog.trace.core.tagprocessor.SpanPointersProcessor import groovy.json.JsonSlurper import org.testcontainers.containers.GenericContainer import org.testcontainers.utility.DockerImageName @@ -95,10 +95,10 @@ class S3ClientTest extends AgentTestRunner { assert links.size() == 1 def link = links[0] assert link["attributes"] != null - assert link["attributes"]["ptr.kind"] == SpanPointersHelper.S3_PTR_KIND - assert link["attributes"]["ptr.dir"] == SpanPointersHelper.DOWN_DIRECTION + assert link["attributes"]["ptr.kind"] == SpanPointersProcessor.S3_PTR_KIND + assert link["attributes"]["ptr.dir"] == SpanPointersProcessor.DOWN_DIRECTION assert link["attributes"]["ptr.hash"] == "6d1a2fe194c6579187408f827f942be3" - assert link["attributes"]["link.kind"] == SpanPointersHelper.LINK_KIND + assert link["attributes"]["link.kind"] == SpanPointersProcessor.LINK_KIND } } } @@ -186,10 +186,10 @@ class S3ClientTest extends AgentTestRunner { assert links.size() == 1 def link = links[0] assert link["attributes"] != null - assert link["attributes"]["ptr.kind"] == SpanPointersHelper.S3_PTR_KIND - assert link["attributes"]["ptr.dir"] == SpanPointersHelper.DOWN_DIRECTION + assert link["attributes"]["ptr.kind"] == SpanPointersProcessor.S3_PTR_KIND + assert link["attributes"]["ptr.dir"] == SpanPointersProcessor.DOWN_DIRECTION assert link["attributes"]["ptr.hash"] == "1542053ce6d393c424b1374bac1fc0c5" - assert link["attributes"]["link.kind"] == SpanPointersHelper.LINK_KIND + assert link["attributes"]["link.kind"] == SpanPointersProcessor.LINK_KIND } } } @@ -372,10 +372,10 @@ class S3ClientTest extends AgentTestRunner { assert links.size() == 1 def link = links[0] assert link["attributes"] != null - assert link["attributes"]["ptr.kind"] == SpanPointersHelper.S3_PTR_KIND - assert link["attributes"]["ptr.dir"] == SpanPointersHelper.DOWN_DIRECTION + assert link["attributes"]["ptr.kind"] == SpanPointersProcessor.S3_PTR_KIND + assert link["attributes"]["ptr.dir"] == SpanPointersProcessor.DOWN_DIRECTION assert link["attributes"]["ptr.hash"] == "422412aa6b472a7194f3e24f4b12b4a6" - assert link["attributes"]["link.kind"] == SpanPointersHelper.LINK_KIND + assert link["attributes"]["link.kind"] == SpanPointersProcessor.LINK_KIND } } } diff --git a/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/SpanPointersProcessor.java b/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/SpanPointersProcessor.java index e8d321be8a8..f85765dde82 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/SpanPointersProcessor.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/SpanPointersProcessor.java @@ -19,15 +19,15 @@ public class SpanPointersProcessor implements TagsPostProcessor { // The pointer direction will always be down. The serverless agent handles cases where the // direction is up. - public static final String DOWN_DIRECTION = "d"; - public static final String S3_PTR_KIND = "aws.s3.object"; - public static final String LINK_KIND = "span-pointer"; - private static final String eTagKey = "s3.eTag"; + private static final String DOWN_DIRECTION = "d"; + private static final String S3_PTR_KIND = "aws.s3.object"; + private static final String LINK_KIND = "span-pointer"; + private static final String ETAG_KEY = "s3.eTag"; @Override public Map processTags( Map unsafeTags, DDSpanContext spanContext, List spanLinks) { - String eTag = asString(unsafeTags.get(eTagKey)); + String eTag = asString(unsafeTags.get(ETAG_KEY)); if (eTag == null) { return unsafeTags; } @@ -59,7 +59,7 @@ public Map processTags( } catch (Exception e) { log.debug("Failed to add span pointer: {}", e.getMessage()); } - unsafeTags.remove(eTagKey); + unsafeTags.remove(ETAG_KEY); return unsafeTags; } diff --git a/dd-trace-core/src/test/groovy/datadog/trace/core/tagprocessor/PostProcessorChainTest.groovy b/dd-trace-core/src/test/groovy/datadog/trace/core/tagprocessor/PostProcessorChainTest.groovy index 772268ec201..dd16e8a565c 100644 --- a/dd-trace-core/src/test/groovy/datadog/trace/core/tagprocessor/PostProcessorChainTest.groovy +++ b/dd-trace-core/src/test/groovy/datadog/trace/core/tagprocessor/PostProcessorChainTest.groovy @@ -1,5 +1,6 @@ package datadog.trace.core.tagprocessor +import datadog.trace.bootstrap.instrumentation.api.AgentSpanLink import datadog.trace.core.DDSpanContext import datadog.trace.test.util.DDSpecification @@ -8,7 +9,7 @@ class PostProcessorChainTest extends DDSpecification { setup: def processor1 = new TagsPostProcessor() { @Override - Map processTags(Map unsafeTags, DDSpanContext spanContext) { + Map processTags(Map unsafeTags, DDSpanContext spanContext, List spanLinks) { unsafeTags.put("key1", "processor1") unsafeTags.put("key2", "processor1") return unsafeTags @@ -16,7 +17,7 @@ class PostProcessorChainTest extends DDSpecification { } def processor2 = new TagsPostProcessor() { @Override - Map processTags(Map unsafeTags, DDSpanContext spanContext) { + Map processTags(Map unsafeTags, DDSpanContext spanContext, List spanLinks) { unsafeTags.put("key1", "processor2") return unsafeTags } @@ -36,13 +37,13 @@ class PostProcessorChainTest extends DDSpecification { setup: def processor1 = new TagsPostProcessor() { @Override - Map processTags(Map unsafeTags, DDSpanContext spanContext) { + Map processTags(Map unsafeTags, DDSpanContext spanContext, List spanLinks) { return ["my": "tag"] } } def processor2 = new TagsPostProcessor() { @Override - Map processTags(Map unsafeTags, DDSpanContext spanContext) { + Map processTags(Map unsafeTags, DDSpanContext spanContext, List spanLinks) { if (unsafeTags.containsKey("test")) { unsafeTags.put("found", "true") } diff --git a/dd-trace-core/src/test/groovy/datadog/trace/core/tagprocessor/SpanPointersProcessorTest.groovy b/dd-trace-core/src/test/groovy/datadog/trace/core/tagprocessor/SpanPointersProcessorTest.groovy new file mode 100644 index 00000000000..09bae762eff --- /dev/null +++ b/dd-trace-core/src/test/groovy/datadog/trace/core/tagprocessor/SpanPointersProcessorTest.groovy @@ -0,0 +1,100 @@ +package datadog.trace.core.tagprocessor + +import datadog.trace.api.DDSpanId +import datadog.trace.api.DDTraceId +import datadog.trace.bootstrap.instrumentation.api.InstrumentationTags +import datadog.trace.bootstrap.instrumentation.api.SpanLink +import datadog.trace.core.DDSpanContext +import datadog.trace.test.util.DDSpecification + +class SpanPointersProcessorTest extends DDSpecification{ + def "SpanPointersProcessor adds correct link with basic values"() { + given: + def processor = new SpanPointersProcessor() + def unsafeTags = [ + (InstrumentationTags.AWS_BUCKET_NAME): "some-bucket", + (InstrumentationTags.AWS_OBJECT_KEY) : "some-key.data", + "s3.eTag" : "ab12ef34" + ] + def spanContext = Mock(DDSpanContext) + def spanLinks = [] + def expectedHash = "e721375466d4116ab551213fdea08413" + + when: + // Process the tags; the processor should remove 's3.eTag' and add one link + def returnedTags = processor.processTags(unsafeTags, spanContext, spanLinks) + + then: + // 1. s3.eTag was removed + !returnedTags.containsKey("s3.eTag") + // 2. Exactly one link was added + spanLinks.size() == 1 + // 3. Check link + def link = spanLinks[0] + link instanceof SpanLink + link.traceId() == DDTraceId.ZERO + link.spanId() == DDSpanId.ZERO + link.attributes.asMap().get("ptr.kind") == SpanPointersProcessor.S3_PTR_KIND + link.attributes.asMap().get("ptr.dir") == SpanPointersProcessor.DOWN_DIRECTION + link.attributes.asMap().get("ptr.hash") == expectedHash + link.attributes.asMap().get("link.kind") == SpanPointersProcessor.LINK_KIND + } + + def "SpanPointersProcessor adds correct link with non-ascii key"() { + given: + def processor = new SpanPointersProcessor() + def unsafeTags = [ + (InstrumentationTags.AWS_BUCKET_NAME): "some-bucket", + (InstrumentationTags.AWS_OBJECT_KEY) : "some-key.你好", + "s3.eTag" : "ab12ef34" + ] + def spanContext = Mock(DDSpanContext) + def spanLinks = [] + + // From the original test, expected hash for these components + def expectedHash = "d1333a04b9928ab462b5c6cadfa401f4" + + when: + def returnedTags = processor.processTags(unsafeTags, spanContext, spanLinks) + + then: + !returnedTags.containsKey("s3.eTag") + spanLinks.size() == 1 + def link = spanLinks[0] + link.traceId() == DDTraceId.ZERO + link.spanId() == DDSpanId.ZERO + link.attributes.asMap().get("ptr.kind") == SpanPointersProcessor.S3_PTR_KIND + link.attributes.asMap().get("ptr.dir") == SpanPointersProcessor.DOWN_DIRECTION + link.attributes.asMap().get("ptr.hash") == expectedHash + link.attributes.asMap().get("link.kind") == SpanPointersProcessor.LINK_KIND + } + + def "SpanPointersProcessor adds correct link with multipart-upload ETag"() { + given: + def processor = new SpanPointersProcessor() + def unsafeTags = [ + (InstrumentationTags.AWS_BUCKET_NAME): "some-bucket", + (InstrumentationTags.AWS_OBJECT_KEY) : "some-key.data", + "s3.eTag" : "ab12ef34-5" + ] + def spanContext = Mock(DDSpanContext) + def spanLinks = [] + + // From the original test, expected hash for these components + def expectedHash = "2b90dffc37ebc7bc610152c3dc72af9f" + + when: + def returnedTags = processor.processTags(unsafeTags, spanContext, spanLinks) + + then: + !returnedTags.containsKey("s3.eTag") + spanLinks.size() == 1 + def link = spanLinks[0] + link.traceId() == DDTraceId.ZERO + link.spanId() == DDSpanId.ZERO + link.attributes.asMap().get("ptr.kind") == SpanPointersProcessor.S3_PTR_KIND + link.attributes.asMap().get("ptr.dir") == SpanPointersProcessor.DOWN_DIRECTION + link.attributes.asMap().get("ptr.hash") == expectedHash + link.attributes.asMap().get("link.kind") == SpanPointersProcessor.LINK_KIND + } +} From 1645afeb80d1e57aa732a911630be4249a3db0a6 Mon Sep 17 00:00:00 2001 From: Nicholas Hulston Date: Thu, 16 Jan 2025 09:49:05 -0500 Subject: [PATCH 15/20] update muzzle --- dd-java-agent/instrumentation/aws-java-s3-2.0/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dd-java-agent/instrumentation/aws-java-s3-2.0/build.gradle b/dd-java-agent/instrumentation/aws-java-s3-2.0/build.gradle index d6cfcbb7104..21fa4b5818c 100644 --- a/dd-java-agent/instrumentation/aws-java-s3-2.0/build.gradle +++ b/dd-java-agent/instrumentation/aws-java-s3-2.0/build.gradle @@ -2,7 +2,7 @@ muzzle { pass { group = "software.amazon.awssdk" module = "s3" - versions = "[2.10.36,3)" + versions = "[2,3)" assertInverse = true } } From 6ce63c79018ba38ad7215a0ff21d1f76fdc5bce6 Mon Sep 17 00:00:00 2001 From: Nicholas Hulston Date: Tue, 21 Jan 2025 12:03:11 -0500 Subject: [PATCH 16/20] update `ADD_SPAN_POINTERS` env var name --- .../datadog/trace/api/config/TraceInstrumentationConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dd-trace-api/src/main/java/datadog/trace/api/config/TraceInstrumentationConfig.java b/dd-trace-api/src/main/java/datadog/trace/api/config/TraceInstrumentationConfig.java index 495f42c5da0..e310469132a 100644 --- a/dd-trace-api/src/main/java/datadog/trace/api/config/TraceInstrumentationConfig.java +++ b/dd-trace-api/src/main/java/datadog/trace/api/config/TraceInstrumentationConfig.java @@ -165,7 +165,7 @@ public final class TraceInstrumentationConfig { public static final String AXIS_PROMOTE_RESOURCE_NAME = "trace.axis.promote.resource-name"; public static final String SQS_BODY_PROPAGATION_ENABLED = "trace.sqs.body.propagation.enabled"; - public static final String ADD_SPAN_POINTERS = "aws.add.span.pointers"; + public static final String ADD_SPAN_POINTERS = "trace.aws.add.span.pointers"; private TraceInstrumentationConfig() {} } From 9b35b3095bba2fe466082a5b628c3cbe581a14be Mon Sep 17 00:00:00 2001 From: Nicholas Hulston Date: Tue, 21 Jan 2025 12:04:08 -0500 Subject: [PATCH 17/20] improve `asString` helper method; improve hash performance --- .../core/tagprocessor/SpanPointersProcessor.java | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/SpanPointersProcessor.java b/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/SpanPointersProcessor.java index f85765dde82..016d49fe373 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/SpanPointersProcessor.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/SpanPointersProcessor.java @@ -6,9 +6,11 @@ import datadog.trace.bootstrap.instrumentation.api.SpanAttributes; import datadog.trace.bootstrap.instrumentation.api.SpanLink; import datadog.trace.core.DDSpanContext; +import datadog.trace.util.Strings; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.util.Arrays; import java.util.List; import java.util.Map; import org.slf4j.Logger; @@ -27,7 +29,7 @@ public class SpanPointersProcessor implements TagsPostProcessor { @Override public Map processTags( Map unsafeTags, DDSpanContext spanContext, List spanLinks) { - String eTag = asString(unsafeTags.get(ETAG_KEY)); + String eTag = asString(unsafeTags.remove(ETAG_KEY)); if (eTag == null) { return unsafeTags; } @@ -59,13 +61,12 @@ public Map processTags( } catch (Exception e) { log.debug("Failed to add span pointer: {}", e.getMessage()); } - unsafeTags.remove(ETAG_KEY); return unsafeTags; } private static String asString(Object o) { - return o instanceof String ? (String) o : null; + return o == null ? null : o.toString(); } /** @@ -91,11 +92,8 @@ private static String generatePointerHash(String[] components) throws NoSuchAlgo } byte[] fullHash = messageDigest.digest(); - StringBuilder hex = new StringBuilder(32); - for (int i = 0; i < 16; i++) { - hex.append(String.format("%02x", fullHash[i])); - } - - return hex.toString(); + // Only take first 16 bytes of the hash and convert to hex + byte[] truncatedHash = Arrays.copyOf(fullHash, 16); + return Strings.toHexString(truncatedHash); } } From a516c46ee273b5c52d506efd78602cb25a1aa3f0 Mon Sep 17 00:00:00 2001 From: Nicholas Hulston Date: Tue, 21 Jan 2025 13:16:20 -0500 Subject: [PATCH 18/20] fix checksum flaky tests --- .../aws-java-s3-2.0/src/test/groovy/S3ClientTest.groovy | 1 + 1 file changed, 1 insertion(+) diff --git a/dd-java-agent/instrumentation/aws-java-s3-2.0/src/test/groovy/S3ClientTest.groovy b/dd-java-agent/instrumentation/aws-java-s3-2.0/src/test/groovy/S3ClientTest.groovy index 2899e6deeba..fe9decb6f11 100644 --- a/dd-java-agent/instrumentation/aws-java-s3-2.0/src/test/groovy/S3ClientTest.groovy +++ b/dd-java-agent/instrumentation/aws-java-s3-2.0/src/test/groovy/S3ClientTest.groovy @@ -38,6 +38,7 @@ class S3ClientTest extends AgentTestRunner { .credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create("test", "test"))) .serviceConfiguration(S3Configuration.builder() .pathStyleAccessEnabled(true) + .checksumValidationEnabled(false) .build()) .build() From 42c62fefd89358323048ea062acfd3156c2d0641 Mon Sep 17 00:00:00 2001 From: Andrea Marziali Date: Wed, 22 Jan 2025 09:52:18 +0100 Subject: [PATCH 19/20] use generic config. add defaults --- .../instrumentation/aws/v2/s3/S3Interceptor.java | 4 +++- .../java/datadog/trace/api/ConfigDefaults.java | 1 + .../api/config/TraceInstrumentationConfig.java | 2 +- .../tagprocessor/TagsPostProcessorFactory.java | 6 +++++- .../src/main/java/datadog/trace/api/Config.java | 14 ++++++++------ 5 files changed, 18 insertions(+), 9 deletions(-) diff --git a/dd-java-agent/instrumentation/aws-java-s3-2.0/src/main/java/datadog/trace/instrumentation/aws/v2/s3/S3Interceptor.java b/dd-java-agent/instrumentation/aws-java-s3-2.0/src/main/java/datadog/trace/instrumentation/aws/v2/s3/S3Interceptor.java index d38d1ab5030..25c3b1f2309 100644 --- a/dd-java-agent/instrumentation/aws-java-s3-2.0/src/main/java/datadog/trace/instrumentation/aws/v2/s3/S3Interceptor.java +++ b/dd-java-agent/instrumentation/aws-java-s3-2.0/src/main/java/datadog/trace/instrumentation/aws/v2/s3/S3Interceptor.java @@ -20,10 +20,12 @@ public class S3Interceptor implements ExecutionInterceptor { InstanceStore.of(ExecutionAttribute.class) .putIfAbsent("DatadogSpan", () -> new ExecutionAttribute<>("DatadogSpan")); + private static final boolean CAN_ADD_SPAN_POINTERS = Config.get().isAddSpanPointers("aws"); + @Override public void afterExecution( Context.AfterExecution context, ExecutionAttributes executionAttributes) { - if (!Config.get().isSpanPointersEnabled()) { + if (!CAN_ADD_SPAN_POINTERS) { return; } diff --git a/dd-trace-api/src/main/java/datadog/trace/api/ConfigDefaults.java b/dd-trace-api/src/main/java/datadog/trace/api/ConfigDefaults.java index 8bbdc38f272..4eb414f3394 100644 --- a/dd-trace-api/src/main/java/datadog/trace/api/ConfigDefaults.java +++ b/dd-trace-api/src/main/java/datadog/trace/api/ConfigDefaults.java @@ -248,6 +248,7 @@ public final class ConfigDefaults { static final boolean DEFAULT_ELASTICSEARCH_BODY_ENABLED = false; static final boolean DEFAULT_ELASTICSEARCH_PARAMS_ENABLED = true; static final boolean DEFAULT_ELASTICSEARCH_BODY_AND_PARAMS_ENABLED = false; + static final boolean DEFAULT_ADD_SPAN_POINTERS = true; static final boolean DEFAULT_SPARK_TASK_HISTOGRAM_ENABLED = true; static final boolean DEFAULT_SPARK_APP_NAME_AS_SERVICE = false; diff --git a/dd-trace-api/src/main/java/datadog/trace/api/config/TraceInstrumentationConfig.java b/dd-trace-api/src/main/java/datadog/trace/api/config/TraceInstrumentationConfig.java index a97a29366e3..b124e03131f 100644 --- a/dd-trace-api/src/main/java/datadog/trace/api/config/TraceInstrumentationConfig.java +++ b/dd-trace-api/src/main/java/datadog/trace/api/config/TraceInstrumentationConfig.java @@ -167,7 +167,7 @@ public final class TraceInstrumentationConfig { public static final String AXIS_PROMOTE_RESOURCE_NAME = "trace.axis.promote.resource-name"; public static final String SQS_BODY_PROPAGATION_ENABLED = "trace.sqs.body.propagation.enabled"; - public static final String ADD_SPAN_POINTERS = "trace.aws.add.span.pointers"; + public static final String ADD_SPAN_POINTERS = "add.span.pointers"; private TraceInstrumentationConfig() {} } diff --git a/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/TagsPostProcessorFactory.java b/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/TagsPostProcessorFactory.java index 58ee80a0d0a..154d7f54153 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/TagsPostProcessorFactory.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/TagsPostProcessorFactory.java @@ -25,7 +25,11 @@ private static TagsPostProcessor create() { processors.add(ptp); } } - if (Config.get().isSpanPointersEnabled()) { + // today we only have aws as config key however we could have span pointers for different + // integrations. + // At that moment we should run the postprocessor for all the spans (and filter by component + // to skip non-interesting ones) + if (Config.get().isAddSpanPointers("aws")) { processors.add(new SpanPointersProcessor()); } return new PostProcessorChain( diff --git a/internal-api/src/main/java/datadog/trace/api/Config.java b/internal-api/src/main/java/datadog/trace/api/Config.java index 3d7d2754df5..a5f376ce812 100644 --- a/internal-api/src/main/java/datadog/trace/api/Config.java +++ b/internal-api/src/main/java/datadog/trace/api/Config.java @@ -423,7 +423,6 @@ public static String getHostName() { private final boolean awsPropagationEnabled; private final boolean sqsPropagationEnabled; private final boolean sqsBodyPropagationEnabled; - private final boolean addSpanPointers; private final boolean kafkaClientPropagationEnabled; private final Set kafkaClientPropagationDisabledTopics; @@ -1627,7 +1626,6 @@ PROFILING_DATADOG_PROFILER_ENABLED, isDatadogProfilerSafeInCurrentEnvironment()) awsPropagationEnabled = isPropagationEnabled(true, "aws", "aws-sdk"); sqsPropagationEnabled = isPropagationEnabled(true, "sqs"); sqsBodyPropagationEnabled = configProvider.getBoolean(SQS_BODY_PROPAGATION_ENABLED, false); - addSpanPointers = configProvider.getBoolean(ADD_SPAN_POINTERS, true); kafkaClientPropagationEnabled = isPropagationEnabled(true, "kafka", "kafka.client"); kafkaClientPropagationDisabledTopics = @@ -3158,10 +3156,6 @@ public boolean isSqsBodyPropagationEnabled() { return sqsBodyPropagationEnabled; } - public boolean isSpanPointersEnabled() { - return addSpanPointers; - } - public boolean isKafkaClientPropagationEnabled() { return kafkaClientPropagationEnabled; } @@ -3792,6 +3786,14 @@ public boolean isTimeInQueueEnabled( Arrays.asList(integrationNames), "", ".time-in-queue.enabled", defaultEnabled); } + public boolean isAddSpanPointers(final String integrationName) { + return configProvider.isEnabled( + Collections.singletonList(ADD_SPAN_POINTERS), + integrationName, + "", + DEFAULT_ADD_SPAN_POINTERS); + } + public boolean isEnabled( final boolean defaultEnabled, final String settingName, String settingSuffix) { return configProvider.isEnabled( From 218c106e88afcd64865e4902f0ba50585d214f76 Mon Sep 17 00:00:00 2001 From: Andrea Marziali Date: Wed, 22 Jan 2025 10:04:41 +0100 Subject: [PATCH 20/20] refactor constants and fix tests --- .../aws/v2/s3/S3Interceptor.java | 4 ++- .../tagprocessor/SpanPointersProcessor.java | 24 +++++++++-------- .../tagprocessor/BaseServiceAdderTest.groovy | 2 +- .../PayloadTagsProcessorTest.groovy | 26 +++++++++---------- .../PeerServiceCalculatorTest.groovy | 12 ++++----- .../PostProcessorChainTest.groovy | 4 +-- .../tagprocessor/QueryObfuscatorTest.groovy | 4 +-- .../api/InstrumentationTags.java | 1 + 8 files changed, 41 insertions(+), 36 deletions(-) diff --git a/dd-java-agent/instrumentation/aws-java-s3-2.0/src/main/java/datadog/trace/instrumentation/aws/v2/s3/S3Interceptor.java b/dd-java-agent/instrumentation/aws-java-s3-2.0/src/main/java/datadog/trace/instrumentation/aws/v2/s3/S3Interceptor.java index 25c3b1f2309..bdaf1ba6559 100644 --- a/dd-java-agent/instrumentation/aws-java-s3-2.0/src/main/java/datadog/trace/instrumentation/aws/v2/s3/S3Interceptor.java +++ b/dd-java-agent/instrumentation/aws-java-s3-2.0/src/main/java/datadog/trace/instrumentation/aws/v2/s3/S3Interceptor.java @@ -1,5 +1,7 @@ package datadog.trace.instrumentation.aws.v2.s3; +import static datadog.trace.bootstrap.instrumentation.api.InstrumentationTags.S3_ETAG; + import datadog.trace.api.Config; import datadog.trace.bootstrap.InstanceStore; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; @@ -52,6 +54,6 @@ public void afterExecution( // 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); + span.setTag(S3_ETAG, eTag); } } diff --git a/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/SpanPointersProcessor.java b/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/SpanPointersProcessor.java index 016d49fe373..685ad55fa89 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/SpanPointersProcessor.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/SpanPointersProcessor.java @@ -1,8 +1,11 @@ package datadog.trace.core.tagprocessor; +import static datadog.trace.bootstrap.instrumentation.api.InstrumentationTags.AWS_BUCKET_NAME; +import static datadog.trace.bootstrap.instrumentation.api.InstrumentationTags.AWS_OBJECT_KEY; +import static datadog.trace.bootstrap.instrumentation.api.InstrumentationTags.S3_ETAG; + import datadog.trace.bootstrap.instrumentation.api.AgentSpanLink; import datadog.trace.bootstrap.instrumentation.api.AgentTracer; -import datadog.trace.bootstrap.instrumentation.api.InstrumentationTags; import datadog.trace.bootstrap.instrumentation.api.SpanAttributes; import datadog.trace.bootstrap.instrumentation.api.SpanLink; import datadog.trace.core.DDSpanContext; @@ -17,26 +20,25 @@ import org.slf4j.LoggerFactory; public class SpanPointersProcessor implements TagsPostProcessor { - private static final Logger log = LoggerFactory.getLogger(SpanPointersProcessor.class); + private static final Logger LOG = LoggerFactory.getLogger(SpanPointersProcessor.class); // The pointer direction will always be down. The serverless agent handles cases where the // direction is up. - private static final String DOWN_DIRECTION = "d"; - private static final String S3_PTR_KIND = "aws.s3.object"; - private static final String LINK_KIND = "span-pointer"; - private static final String ETAG_KEY = "s3.eTag"; + static final String DOWN_DIRECTION = "d"; + static final String S3_PTR_KIND = "aws.s3.object"; + static final String LINK_KIND = "span-pointer"; @Override public Map processTags( Map unsafeTags, DDSpanContext spanContext, List spanLinks) { - String eTag = asString(unsafeTags.remove(ETAG_KEY)); + String eTag = asString(unsafeTags.remove(S3_ETAG)); if (eTag == null) { return unsafeTags; } - String bucket = asString(unsafeTags.get(InstrumentationTags.AWS_BUCKET_NAME)); - String key = asString(unsafeTags.get(InstrumentationTags.AWS_OBJECT_KEY)); + String bucket = asString(unsafeTags.get(AWS_BUCKET_NAME)); + String key = asString(unsafeTags.get(AWS_OBJECT_KEY)); if (bucket == null || key == null) { - log.debug("Unable to calculate span pointer hash because could not find bucket or key tags."); + LOG.debug("Unable to calculate span pointer hash because could not find bucket or key tags."); return unsafeTags; } @@ -59,7 +61,7 @@ public Map processTags( AgentSpanLink link = SpanLink.from(zeroContext, AgentSpanLink.DEFAULT_FLAGS, "", attributes); spanLinks.add(link); } catch (Exception e) { - log.debug("Failed to add span pointer: {}", e.getMessage()); + LOG.debug("Failed to add span pointer: {}", e.getMessage()); } return unsafeTags; diff --git a/dd-trace-core/src/test/groovy/datadog/trace/core/tagprocessor/BaseServiceAdderTest.groovy b/dd-trace-core/src/test/groovy/datadog/trace/core/tagprocessor/BaseServiceAdderTest.groovy index 22646e0fe98..fbab796b7c8 100644 --- a/dd-trace-core/src/test/groovy/datadog/trace/core/tagprocessor/BaseServiceAdderTest.groovy +++ b/dd-trace-core/src/test/groovy/datadog/trace/core/tagprocessor/BaseServiceAdderTest.groovy @@ -12,7 +12,7 @@ class BaseServiceAdderTest extends DDSpecification { def spanContext = Mock(DDSpanContext) when: - def enrichedTags = calculator.processTags([:], spanContext) + def enrichedTags = calculator.processTags([:], spanContext, []) then: 1 * spanContext.getServiceName() >> serviceName diff --git a/dd-trace-core/src/test/groovy/datadog/trace/core/tagprocessor/PayloadTagsProcessorTest.groovy b/dd-trace-core/src/test/groovy/datadog/trace/core/tagprocessor/PayloadTagsProcessorTest.groovy index 3e144b38f5d..40ef22a3f4e 100644 --- a/dd-trace-core/src/test/groovy/datadog/trace/core/tagprocessor/PayloadTagsProcessorTest.groovy +++ b/dd-trace-core/src/test/groovy/datadog/trace/core/tagprocessor/PayloadTagsProcessorTest.groovy @@ -82,7 +82,7 @@ class PayloadTagsProcessorTest extends DDSpecification { ] expect: - ptp.processTags(spanTags, null) == ["foo": "bar", "tag1": 1] + ptp.processTags(spanTags, null, []) == ["foo": "bar", "tag1": 1] } static PathAndValue pv(PathCursor path, Object value) { @@ -107,7 +107,7 @@ class PayloadTagsProcessorTest extends DDSpecification { ] expect: - ptp.processTags(spanTags, null) == ["foo": "bar", "tag1": 1, "payload.tag1": 0] + ptp.processTags(spanTags, null, []) == ["foo": "bar", "tag1": 1, "payload.tag1": 0] } def "expand preserving tag types"() { @@ -124,7 +124,7 @@ class PayloadTagsProcessorTest extends DDSpecification { ]) expect: - ptp.processTags(st, null) == [ + ptp.processTags(st, null, []) == [ "payload.tag1": 11, "payload.tag2.Value": 2342l, "payload.tag3.0": 3.14d, @@ -142,7 +142,7 @@ class PayloadTagsProcessorTest extends DDSpecification { def st = spanTags("payload", [pv(pc().push("tag7"), unknownValue),]) expect: - ptp.processTags(st, null) == [ + ptp.processTags(st, null, []) == [ "payload.tag7": unknownValue.toString(), ] } @@ -159,7 +159,7 @@ class PayloadTagsProcessorTest extends DDSpecification { ]) expect: - ptp.processTags(st, null) == [ + ptp.processTags(st, null, []) == [ "p.j3.0": "1", "p.j3.1": 2, "p.j3.2": 3.14d, @@ -204,7 +204,7 @@ class PayloadTagsProcessorTest extends DDSpecification { def st = spanTags("dd", [pv(pc(), json),]) expect: - ptp.processTags(st, null) == [ + ptp.processTags(st, null, []) == [ 'dd.a' : 33, 'dd.Message.id' : 45, 'dd.Message.user.a' : 1.15d, @@ -220,7 +220,7 @@ class PayloadTagsProcessorTest extends DDSpecification { def st = spanTags("p", [pv(pc().push("key"), invalidJson),]) expect: - ptp.processTags(st, null) == [ + ptp.processTags(st, null, []) == [ "p.key": invalidJson, ] @@ -246,7 +246,7 @@ class PayloadTagsProcessorTest extends DDSpecification { ]) expect: - ptp.processTags(st, null) == [ + ptp.processTags(st, null, []) == [ "p.j1.foo": "bar", "p.j2.0": 1, "p.j2.1": true, @@ -261,7 +261,7 @@ class PayloadTagsProcessorTest extends DDSpecification { def st = spanTags("p", [pv(pc().push("v"), new ByteArrayInputStream("""{ "inner": $innerJson}""".bytes))]) then: - ptp.processTags(st, null) == [ + ptp.processTags(st, null, []) == [ "p.v.inner.a": 1.15d, "p.v.inner.password": "my-secret-password", ] @@ -283,7 +283,7 @@ class PayloadTagsProcessorTest extends DDSpecification { def st = spanTags("p", [pv(pc().push("key"), new ByteArrayInputStream(invalidJson.bytes)),]) expect: - ptp.processTags(st, null) == [ + ptp.processTags(st, null, []) == [ "p.key": "", ] @@ -303,7 +303,7 @@ class PayloadTagsProcessorTest extends DDSpecification { ]) expect: - ptp.processTags(st, null) == [ + ptp.processTags(st, null, []) == [ "p.j3.0": "redacted", "p.j3.1": 2, "p.j3.2": 3.14d, @@ -326,7 +326,7 @@ class PayloadTagsProcessorTest extends DDSpecification { ]) expect: - ptp.processTags(st, null) == [ + ptp.processTags(st, null, []) == [ "p.j3.0": "redacted", "p.j3.1": 2, "p.j3.2": 3.14d, @@ -345,7 +345,7 @@ class PayloadTagsProcessorTest extends DDSpecification { ]) expect: - ptp.processTags(st, null) == [ + ptp.processTags(st, null, []) == [ "p.j3.0": "redacted", "p.j3.1": 2, "p.j3.2": 3.14d, diff --git a/dd-trace-core/src/test/groovy/datadog/trace/core/tagprocessor/PeerServiceCalculatorTest.groovy b/dd-trace-core/src/test/groovy/datadog/trace/core/tagprocessor/PeerServiceCalculatorTest.groovy index 4c104a93070..bc8281835bb 100644 --- a/dd-trace-core/src/test/groovy/datadog/trace/core/tagprocessor/PeerServiceCalculatorTest.groovy +++ b/dd-trace-core/src/test/groovy/datadog/trace/core/tagprocessor/PeerServiceCalculatorTest.groovy @@ -13,7 +13,7 @@ class PeerServiceCalculatorTest extends DDSpecification { setup: def calculator = new PeerServiceCalculator(new NamingSchemaV0().peerService(), Collections.emptyMap()) when: - def enrichedTags = calculator.processTags(tags, null) + def enrichedTags = calculator.processTags(tags, null, []) then: // tags are not modified assert enrichedTags == tags @@ -33,7 +33,7 @@ class PeerServiceCalculatorTest extends DDSpecification { def calculator = new PeerServiceCalculator(new NamingSchemaV1().peerService(), Collections.emptyMap()) when: tags.put(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) - def calculated = calculator.processTags(tags, null) + def calculated = calculator.processTags(tags, null, []) then: calculated.get(DDTags.PEER_SERVICE_SOURCE) == provenance @@ -56,7 +56,7 @@ class PeerServiceCalculatorTest extends DDSpecification { injectSysConfig(TracerConfig.TRACE_PEER_SERVICE_DEFAULTS_ENABLED, "true") def calculator = new PeerServiceCalculator(new NamingSchemaV0().peerService(), Collections.emptyMap()) when: - def calculated = calculator.processTags(["span.kind": "client", "peer.hostname": "test"], null) + def calculated = calculator.processTags(["span.kind": "client", "peer.hostname": "test"], null, []) then: assert calculated.get(Tags.PEER_SERVICE) == "test" } @@ -70,7 +70,7 @@ class PeerServiceCalculatorTest extends DDSpecification { def tags = ["span.kind": kind, "peer.hostname": "test"] then: - assert calculator.processTags(tags, null).containsKey(Tags.PEER_SERVICE) == calculate + assert calculator.processTags(tags, null, []).containsKey(Tags.PEER_SERVICE) == calculate where: kind | calculate @@ -87,7 +87,7 @@ class PeerServiceCalculatorTest extends DDSpecification { def calculator = new PeerServiceCalculator(new NamingSchemaV0().peerService(), Config.get().getPeerServiceMapping()) when: - def calculated = calculator.processTags(tags, null) + def calculated = calculator.processTags(tags, null, []) then: assert calculated.get(Tags.PEER_SERVICE) == expected @@ -108,7 +108,7 @@ class PeerServiceCalculatorTest extends DDSpecification { def calculator = new PeerServiceCalculator(new NamingSchemaV0().peerService(), Config.get().getPeerServiceComponentOverrides()) when: - def calculated = calculator.processTags(tags, null) + def calculated = calculator.processTags(tags, null, []) then: assert calculated.get(Tags.PEER_SERVICE) == expected diff --git a/dd-trace-core/src/test/groovy/datadog/trace/core/tagprocessor/PostProcessorChainTest.groovy b/dd-trace-core/src/test/groovy/datadog/trace/core/tagprocessor/PostProcessorChainTest.groovy index dd16e8a565c..2a1dc2583f3 100644 --- a/dd-trace-core/src/test/groovy/datadog/trace/core/tagprocessor/PostProcessorChainTest.groovy +++ b/dd-trace-core/src/test/groovy/datadog/trace/core/tagprocessor/PostProcessorChainTest.groovy @@ -27,7 +27,7 @@ class PostProcessorChainTest extends DDSpecification { def tags = ["key1": "root", "key3": "root"] when: - def out = chain.processTags(tags, null) + def out = chain.processTags(tags, null, []) then: assert out == ["key1": "processor2", "key2": "processor1", "key3": "root"] @@ -55,7 +55,7 @@ class PostProcessorChainTest extends DDSpecification { def tags = ["test": "test"] when: - def out = chain.processTags(tags, null) + def out = chain.processTags(tags, null, []) then: assert out == ["my": "tag"] diff --git a/dd-trace-core/src/test/groovy/datadog/trace/core/tagprocessor/QueryObfuscatorTest.groovy b/dd-trace-core/src/test/groovy/datadog/trace/core/tagprocessor/QueryObfuscatorTest.groovy index 11dbcbb7c76..655a1a720fb 100644 --- a/dd-trace-core/src/test/groovy/datadog/trace/core/tagprocessor/QueryObfuscatorTest.groovy +++ b/dd-trace-core/src/test/groovy/datadog/trace/core/tagprocessor/QueryObfuscatorTest.groovy @@ -14,7 +14,7 @@ class QueryObfuscatorTest extends DDSpecification { ] when: - def result = obfuscator.processTags(tags, null) + def result = obfuscator.processTags(tags, null, []) then: assert result.get(DDTags.HTTP_QUERY) == expectedQuery @@ -36,7 +36,7 @@ class QueryObfuscatorTest extends DDSpecification { ] when: - def result = obfuscator.processTags(tags, null) + def result = obfuscator.processTags(tags, null, []) then: assert result.get(DDTags.HTTP_QUERY) == expectedQuery diff --git a/internal-api/src/main/java/datadog/trace/bootstrap/instrumentation/api/InstrumentationTags.java b/internal-api/src/main/java/datadog/trace/bootstrap/instrumentation/api/InstrumentationTags.java index ff94ab022d7..5c8e0405e7c 100644 --- a/internal-api/src/main/java/datadog/trace/bootstrap/instrumentation/api/InstrumentationTags.java +++ b/internal-api/src/main/java/datadog/trace/bootstrap/instrumentation/api/InstrumentationTags.java @@ -36,6 +36,7 @@ public class InstrumentationTags { public static final String TABLE_NAME = "tablename"; public static final String AWS_REQUEST_ID = "aws.requestId"; public static final String AWS_STORAGE_CLASS = "aws.storage.class"; + public static final String S3_ETAG = "s3.eTag"; public static final String BUCKET = "bucket"; public static final String CASSANDRA_CONTACT_POINTS = "db.cassandra.contact.points";