diff --git a/dd-java-agent/instrumentation/aws-java-sdk-1.11.0/src/main/java/datadog/trace/instrumentation/aws/v0/AwsSdkClientDecorator.java b/dd-java-agent/instrumentation/aws-java-sdk-1.11.0/src/main/java/datadog/trace/instrumentation/aws/v0/AwsSdkClientDecorator.java
index b164cf54349..b80f55a8420 100644
--- a/dd-java-agent/instrumentation/aws-java-sdk-1.11.0/src/main/java/datadog/trace/instrumentation/aws/v0/AwsSdkClientDecorator.java
+++ b/dd-java-agent/instrumentation/aws-java-sdk-1.11.0/src/main/java/datadog/trace/instrumentation/aws/v0/AwsSdkClientDecorator.java
@@ -83,12 +83,13 @@ public AgentSpan onRequest(final AgentSpan span, final Request request) {
final AmazonWebServiceRequest originalRequest = request.getOriginalRequest();
final Class> awsOperation = originalRequest.getClass();
final GetterAccess access = GetterAccess.of(originalRequest);
+ final String endpoint = request.getEndpoint().toString();
span.setTag(InstrumentationTags.AWS_AGENT, COMPONENT_NAME);
span.setTag(InstrumentationTags.AWS_SERVICE, awsServiceName);
span.setTag(InstrumentationTags.TOP_LEVEL_AWS_SERVICE, awsSimplifiedServiceName);
span.setTag(InstrumentationTags.AWS_OPERATION, awsOperation.getSimpleName());
- span.setTag(InstrumentationTags.AWS_ENDPOINT, request.getEndpoint().toString());
+ span.setTag(InstrumentationTags.AWS_ENDPOINT, endpoint);
CharSequence awsRequestName = AwsNameCache.getQualifiedName(request);
span.setResourceName(awsRequestName, RPC_COMMAND_NAME);
@@ -182,11 +183,20 @@ public AgentSpan onRequest(final AgentSpan span, final Request request) {
bestPeerService = tableName;
}
- // for aws we can calculate this eagerly without needing to have to looking up tags in the peer
- // service interceptor
- if (bestPrecursor != null && SpanNaming.instance().namingSchema().peerService().supports()) {
- span.setTag(Tags.PEER_SERVICE, bestPeerService);
- span.setTag(DDTags.PEER_SERVICE_SOURCE, bestPrecursor);
+ // Set peer.service based on Config for serverless functions
+ if (Config.get().isAwsServerless()) {
+ URI uri = request.getEndpoint();
+ String hostname = uri.getHost();
+ if (uri.getPort() != -1) {
+ hostname = hostname + ":" + uri.getPort();
+ }
+ span.setTag(Tags.PEER_SERVICE, hostname);
+ span.setTag(DDTags.PEER_SERVICE_SOURCE, "peer.service");
+ } else {
+ if (bestPrecursor != null && SpanNaming.instance().namingSchema().peerService().supports()) {
+ span.setTag(Tags.PEER_SERVICE, bestPeerService);
+ span.setTag(DDTags.PEER_SERVICE_SOURCE, bestPrecursor);
+ }
}
// DSM
diff --git a/dd-java-agent/instrumentation/aws-java-sdk-1.11.0/src/test/groovy/AWS1ClientTest.groovy b/dd-java-agent/instrumentation/aws-java-sdk-1.11.0/src/test/groovy/AWS1ClientTest.groovy
index 994caa353f5..dabe083af69 100644
--- a/dd-java-agent/instrumentation/aws-java-sdk-1.11.0/src/test/groovy/AWS1ClientTest.groovy
+++ b/dd-java-agent/instrumentation/aws-java-sdk-1.11.0/src/test/groovy/AWS1ClientTest.groovy
@@ -401,6 +401,88 @@ abstract class AWS1ClientTest extends VersionedNamingTestBase {
cleanup:
server.close()
}
+
+ def "#service #operation sets peer.service in serverless environment"() {
+ setup:
+
+ if (version() == 0) {
+ return
+ }
+
+ // Set the AWS Lambda function name environment variable
+ injectEnvConfig("AWS_LAMBDA_FUNCTION_NAME", "my-test-lambda-function", false)
+
+ // Set response body
+ responseBody.set(body)
+ if (jsonPointerStr != null) {
+ jsonPointer.set(jsonPointerStr)
+ }
+
+ when:
+ // Make the request
+ def response = call.call(client)
+
+ // Wait for traces to be written
+ TEST_WRITER.waitForTraces(1)
+
+ then:
+ response != null
+
+ // Verify the trace
+ assertTraces(1) {
+ trace(1) {
+ span {
+ serviceName expectedService(service, operation)
+ operationName expectedOperation(service, operation)
+ resourceName "$service.$operation"
+ spanType DDSpanTypes.HTTP_CLIENT
+ errored false
+ measured true
+ parent()
+ tags {
+ "$Tags.COMPONENT" "java-aws-sdk"
+ "$Tags.SPAN_KIND" Tags.SPAN_KIND_CLIENT
+ "$Tags.HTTP_URL" "$server.address/"
+ "$Tags.HTTP_METHOD" method
+ "$Tags.HTTP_STATUS" 200
+ "$Tags.PEER_PORT" server.address.port
+ "$Tags.PEER_HOSTNAME" "localhost"
+ "aws.service" { it.contains(service) }
+ "aws_service" { it.contains(service.toLowerCase()) }
+ "aws.endpoint" "$server.address"
+ "aws.operation" "${operation}Request"
+ "aws.agent" "java-aws-sdk"
+
+ // Service-specific tags
+ for (def addedTag : additionalTags) {
+ "$addedTag.key" "$addedTag.value"
+ }
+
+ // Test specific peer service assertions in serverless
+ "peer.service" "${server.address.host}:${server.address.port}"
+ "_dd.peer.service.source" "peer.service"
+
+ defaultTags(false, true)
+ }
+ }
+ }
+ }
+
+ cleanup:
+
+ if (jsonPointerStr != null) {
+ jsonPointer.set(null)
+ }
+
+ where:
+ service | operation | method | path | client | call | additionalTags | body | jsonPointerStr
+ "S3" | "CreateBucket" | "PUT" | "/test-bucket/" | AmazonS3ClientBuilder.standard().withPathStyleAccessEnabled(true).withEndpointConfiguration(endpoint).withCredentials(credentialsProvider).build() | { c -> c.createBucket("test-bucket") } | ["aws.bucket.name": "test-bucket", "bucketname": "test-bucket"] | "" | null
+ "SQS" | "CreateQueue" | "POST" | "/" | AmazonSQSClientBuilder.standard().withEndpointConfiguration(endpoint).withCredentials(credentialsProvider).build() | { c -> c.createQueue(new CreateQueueRequest("test-queue")) } | ["aws.queue.name": "test-queue", "queuename": "test-queue"] | """https://queue.amazonaws.com/123456789012/test-queuetest-request-id""" | "/CreateQueueResponse/CreateQueueResult"
+ "SQS" | "SendMessage" | "POST" | "/test-queue-url" | AmazonSQSClientBuilder.standard().withEndpointConfiguration(endpoint).withCredentials(credentialsProvider).build() | { c -> c.sendMessage(new SendMessageRequest("test-queue-url", "test")) } | ["aws.queue.url": "test-queue-url"] | """098f6bcd4621d373cade4e832627b4f6test-msg-idtest-request-id""" | "/SendMessageResponse/SendMessageResult"
+ "SNS" | "Publish" | "POST" | "/" | AmazonSNSClientBuilder.standard().withEndpointConfiguration(endpoint).withCredentials(credentialsProvider).build() | { c -> c.publish(new PublishRequest("arn:aws:sns::123:test-topic", "test")) } | ["aws.topic.name": "test-topic", "topicname": "test-topic"] | """test-msg-idtest-request-id""" | "/PublishResponse/PublishResult"
+ "DynamoDBv2" | "CreateTable" | "POST" | "/" | AmazonDynamoDBClientBuilder.standard().withEndpointConfiguration(endpoint).withCredentials(credentialsProvider).build() | { c -> c.createTable(new CreateTableRequest("test-table", null)) } | ["aws.table.name": "test-table", "tablename": "test-table"] | "" | null
+ "Kinesis" | "DeleteStream" | "POST" | "/" | AmazonKinesisClientBuilder.standard().withEndpointConfiguration(endpoint).withCredentials(credentialsProvider).build() | { c -> c.deleteStream(new DeleteStreamRequest().withStreamName("test-stream")) } | ["aws.stream.name": "test-stream", "streamname": "test-stream"] | "" | null
+ }
}
class AWS1ClientV0Test extends AWS1ClientTest {
diff --git a/dd-java-agent/instrumentation/aws-java-sdk-2.2/src/main/java/datadog/trace/instrumentation/aws/v2/AwsSdkClientDecorator.java b/dd-java-agent/instrumentation/aws-java-sdk-2.2/src/main/java/datadog/trace/instrumentation/aws/v2/AwsSdkClientDecorator.java
index c5aa03bf64f..4a270b63772 100644
--- a/dd-java-agent/instrumentation/aws-java-sdk-2.2/src/main/java/datadog/trace/instrumentation/aws/v2/AwsSdkClientDecorator.java
+++ b/dd-java-agent/instrumentation/aws-java-sdk-2.2/src/main/java/datadog/trace/instrumentation/aws/v2/AwsSdkClientDecorator.java
@@ -208,6 +208,18 @@ public AgentSpan onSdkRequest(
}
}
+ // Set peer.service based on Config for serverless functions
+ if (Config.get().isAwsServerless()) {
+ URI uri = httpRequest.getUri();
+ String hostname = uri.getHost();
+ if (uri.getPort() != -1) {
+ hostname = hostname + ":" + uri.getPort();
+ }
+
+ span.setTag(Tags.PEER_SERVICE, hostname);
+ span.setTag(DDTags.PEER_SERVICE_SOURCE, "peer.service");
+ }
+
return span;
}
diff --git a/dd-java-agent/instrumentation/aws-java-sdk-2.2/src/test/groovy/Aws2ClientTest.groovy b/dd-java-agent/instrumentation/aws-java-sdk-2.2/src/test/groovy/Aws2ClientTest.groovy
index e075fb61892..f8d57ed1542 100644
--- a/dd-java-agent/instrumentation/aws-java-sdk-2.2/src/test/groovy/Aws2ClientTest.groovy
+++ b/dd-java-agent/instrumentation/aws-java-sdk-2.2/src/test/groovy/Aws2ClientTest.groovy
@@ -421,6 +421,112 @@ abstract class Aws2ClientTest extends VersionedNamingTestBase {
cleanup:
server.close()
}
+
+ def "#service #operation sets peer.service in serverless environment"() {
+ setup:
+
+ if (version() == 0) {
+ return
+ }
+
+ // Set the AWS Lambda function name environment variable
+ injectEnvConfig("AWS_LAMBDA_FUNCTION_NAME", "my-test-lambda-function", false)
+
+ // Create client with mocked endpoint
+ def client = builder
+ .endpointOverride(server.address)
+ .region(Region.US_EAST_1)
+ .credentialsProvider(CREDENTIALS_PROVIDER)
+ .build()
+
+ // Set response body
+ responseBody.set(body)
+
+ when:
+ // Make the request
+ def response = call.call(client)
+
+ if (response instanceof Future) {
+ response = response.get()
+ }
+
+ // Wait for traces to be written
+ TEST_WRITER.waitForTraces(1)
+
+ then:
+ response != null
+
+ // Verify the trace
+ assertTraces(1) {
+ trace(1) {
+ span {
+ serviceName expectedService(service, operation)
+ operationName expectedOperation(service, operation)
+ resourceName "$service.$operation"
+ spanType DDSpanTypes.HTTP_CLIENT
+ errored false
+ measured true
+ parent()
+ tags {
+ defaultTags(false, true)
+
+ // AWS specific tags
+ "aws.service" service
+ "aws_service" service
+ "aws.operation" operation
+ "aws.agent" "java-aws-sdk"
+ "aws.requestId" requestId
+
+ // HTTP tags
+ "$Tags.HTTP_METHOD" method
+ "$Tags.HTTP_STATUS" 200
+ "$Tags.HTTP_URL" String
+
+ // Peer tags
+ "$Tags.PEER_HOSTNAME" "localhost"
+ "$Tags.PEER_PORT" server.address.port
+ "$Tags.SPAN_KIND" Tags.SPAN_KIND_CLIENT
+ "$Tags.COMPONENT" "java-aws-sdk"
+
+ // Service-specific tags
+ if (service == "S3") {
+ "aws.bucket.name" "test-bucket"
+ "bucketname" "test-bucket"
+ } else if (service == "Sqs" && operation == "CreateQueue") {
+ "aws.queue.name" "test-queue"
+ "queuename" "test-queue"
+ } else if (service == "Sqs" && operation == "SendMessage") {
+ "aws.queue.url" "test-queue-url"
+ } else if (service == "Sns" && operation == "Publish") {
+ "aws.topic.name" "test-topic"
+ "topicname" "test-topic"
+ } else if (service == "DynamoDb") {
+ "aws.table.name" "test-table"
+ "tablename" "test-table"
+ } else if (service == "Kinesis") {
+ "aws.stream.name" "test-stream"
+ "streamname" "test-stream"
+ }
+
+ urlTags("${server.address}${path}", ExpectedQueryParams.getExpectedQueryParams(operation))
+
+ // Test specific peer service assertions in serverless
+ "peer.service" "${server.address.host}:${server.address.port}"
+ "_dd.peer.service.source" "peer.service"
+ }
+ }
+ }
+ }
+
+ where:
+ service | operation | method | path | builder | call | body | requestId
+ "S3" | "CreateBucket" | "PUT" | "/test-bucket" | S3Client.builder() | { c -> c.createBucket(CreateBucketRequest.builder().bucket("test-bucket").build()) } | "" | "UNKNOWN"
+ "Sqs" | "CreateQueue" | "POST" | "/" | SqsClient.builder() | { c -> c.createQueue(CreateQueueRequest.builder().queueName("test-queue").build()) } | """https://queue.amazonaws.com/123456789012/test-queuetest-request-id""" | "test-request-id"
+ "Sqs" | "SendMessage" | "POST" | "/" | SqsClient.builder() | { c -> c.sendMessage(SendMessageRequest.builder().queueUrl("test-queue-url").messageBody("test").build()) } | """098f6bcd4621d373cade4e832627b4f6test-msg-idtest-request-id""" | "test-request-id"
+ "Sns" | "Publish" | "POST" | "/" | SnsClient.builder() | { c -> c.publish(PublishRequest.builder().topicArn("arn:aws:sns::123:test-topic").message("test").build()) } | """test-msg-idtest-request-id""" | "test-request-id"
+ "DynamoDb" | "CreateTable" | "POST" | "/" | DynamoDbClient.builder() | { c -> c.createTable(CreateTableRequest.builder().tableName("test-table").build()) } | "" | "UNKNOWN"
+ "Kinesis" | "DeleteStream" | "POST" | "/" | KinesisClient.builder() | { c -> c.deleteStream(DeleteStreamRequest.builder().streamName("test-stream").build()) } | "" | "UNKNOWN"
+ }
}
class Aws2ClientV0ForkedTest extends Aws2ClientTest {
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 0a1727a1b37..5ff002d4144 100644
--- a/internal-api/src/main/java/datadog/trace/api/Config.java
+++ b/internal-api/src/main/java/datadog/trace/api/Config.java
@@ -1168,6 +1168,7 @@ public static String getHostName() {
private final boolean azureAppServices;
private final boolean azureFunctions;
+ private final boolean awsServerless;
private final String traceAgentPath;
private final List traceAgentArgs;
private final String dogStatsDPath;
@@ -1487,6 +1488,9 @@ private Config(final ConfigProvider configProvider, final InstrumenterConfig ins
azureFunctions =
getEnv("FUNCTIONS_WORKER_RUNTIME") != null && getEnv("FUNCTIONS_EXTENSION_VERSION") != null;
+ awsServerless =
+ getEnv("AWS_LAMBDA_FUNCTION_NAME") != null && !getEnv("AWS_LAMBDA_FUNCTION_NAME").isEmpty();
+
spanAttributeSchemaVersion = schemaVersionFromConfig();
peerHostNameEnabled = configProvider.getBoolean(TRACE_PEER_HOSTNAME_ENABLED, true);
@@ -4270,6 +4274,10 @@ public boolean isAzureAppServices() {
return azureAppServices;
}
+ public boolean isAwsServerless() {
+ return awsServerless;
+ }
+
public boolean isDataStreamsEnabled() {
return dataStreamsEnabled;
}