From 84699838fc0bcb58a72baa8152027f3e1e6cd364 Mon Sep 17 00:00:00 2001 From: Michael He <53622546+yiyuan-he@users.noreply.github.com> Date: Thu, 21 Nov 2024 15:13:59 -0800 Subject: [PATCH] Add Contract Tests for LLM Attributes and Models (#952) *Description of changes:* Add new contract tests for Gen AI attributes and models. *Test Plan:* contract-tests-pr By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. --- .../opentelemetry-java-instrumentation.patch | 2947 +++++------------ .../test/awssdk/base/AwsSdkBaseTest.java | 374 ++- .../test/awssdk/v1/AwsSdkV1Test.java | 29 +- .../test/awssdk/v2/AwsSdkV2Test.java | 37 +- .../utils/SemanticConventionsConstants.java | 6 + .../main/java/com/amazon/sampleapp/Utils.java | 66 +- .../main/java/com/amazon/sampleapp/App.java | 173 + .../main/java/com/amazon/sampleapp/App.java | 166 + 8 files changed, 1750 insertions(+), 2048 deletions(-) diff --git a/.github/patches/opentelemetry-java-instrumentation.patch b/.github/patches/opentelemetry-java-instrumentation.patch index 98f517c572..45e804a057 100644 --- a/.github/patches/opentelemetry-java-instrumentation.patch +++ b/.github/patches/opentelemetry-java-instrumentation.patch @@ -1,5 +1,5 @@ diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/build.gradle.kts b/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/build.gradle.kts -index f357a19f88..8a78577580 100644 +index f357a19f88..fa90530579 100644 --- a/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/build.gradle.kts +++ b/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/build.gradle.kts @@ -47,6 +47,14 @@ dependencies { @@ -8,7 +8,7 @@ index f357a19f88..8a78577580 100644 testLibrary("com.amazonaws:aws-java-sdk-sns:1.11.106") + testLibrary("com.amazonaws:aws-java-sdk-sqs:1.11.106") + testLibrary("com.amazonaws:aws-java-sdk-secretsmanager:1.11.309") -+ // testLibrary("com.amazonaws:aws-java-sdk-stepfunctions:1.11.230") ++ testLibrary("com.amazonaws:aws-java-sdk-stepfunctions:1.11.230") + testLibrary("com.amazonaws:aws-java-sdk-lambda:1.11.678") + testLibrary("com.amazonaws:aws-java-sdk-bedrock:1.12.744") + testLibrary("com.amazonaws:aws-java-sdk-bedrockagent:1.12.744") @@ -18,1765 +18,63 @@ index f357a19f88..8a78577580 100644 testImplementation(project(":instrumentation:aws-sdk:aws-sdk-1.11:testing")) diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/test/groovy/S3TracingTest.groovy b/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/test/groovy/S3TracingTest.groovy -index 987a50ed95..a39b216252 100644 +index 987a50ed95..889c856a7c 100644 --- a/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/test/groovy/S3TracingTest.groovy +++ b/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/test/groovy/S3TracingTest.groovy -@@ -19,679 +19,679 @@ class S3TracingTest extends AgentInstrumentationSpecification { - awsConnector.disconnect() - } - -- def "S3 upload triggers SQS message"() { -- setup: -- String queueName = "s3ToSqsTestQueue" -- String bucketName = "otel-s3-to-sqs-test-bucket" -+ //def "S3 upload triggers SQS message"() { -+ // setup: -+ // String queueName = "s3ToSqsTestQueue" -+ // String bucketName = "otel-s3-to-sqs-test-bucket" -+ // -+ // String queueUrl = awsConnector.createQueue(queueName) -+ // awsConnector.createBucket(bucketName) -+ // -+ // String queueArn = awsConnector.getQueueArn(queueUrl) -+ // awsConnector.setQueuePublishingPolicy(queueUrl, queueArn) -+ // awsConnector.enableS3ToSqsNotifications(bucketName, queueArn) -+ // -+ // when: -+ // // test message, auto created by AWS -+ // awsConnector.receiveMessage(queueUrl) -+ // awsConnector.putSampleData(bucketName) -+ // // traced message -+ // def receiveMessageResult = awsConnector.receiveMessage(queueUrl) -+ // receiveMessageResult.messages.each {message -> -+ // runWithSpan("process child") {} -+ // } -+ // -+ // // cleanup -+ // awsConnector.deleteBucket(bucketName) -+ // awsConnector.purgeQueue(queueUrl) -+ // -+ // then: -+ // assertTraces(10) { -+ // trace(0, 1) { -+ // -+ // span(0) { -+ // name "SQS.CreateQueue" -+ // kind CLIENT -+ // hasNoParent() -+ // attributes { -+ // "aws.agent" "java-aws-sdk" -+ // "aws.endpoint" String -+ // "rpc.method" "CreateQueue" -+ // "aws.queue.name" queueName -+ // "rpc.system" "aws-api" -+ // "rpc.service" "AmazonSQS" -+ // "http.method" "POST" -+ // "http.status_code" 200 -+ // "http.url" String -+ // "net.peer.name" String -+ // "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -+ // "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -+ // "net.peer.port" { it == null || Number } -+ // "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } -+ // "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } -+ // } -+ // } -+ // } -+ // trace(1, 1) { -+ // -+ // span(0) { -+ // name "S3.CreateBucket" -+ // kind CLIENT -+ // hasNoParent() -+ // attributes { -+ // "aws.agent" "java-aws-sdk" -+ // "aws.endpoint" String -+ // "rpc.method" "CreateBucket" -+ // "rpc.system" "aws-api" -+ // "rpc.service" "Amazon S3" -+ // "aws.bucket.name" bucketName -+ // "http.method" "PUT" -+ // "http.status_code" 200 -+ // "http.url" String -+ // "net.peer.name" String -+ // "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -+ // "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -+ // "net.peer.port" { it == null || Number } -+ // "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } -+ // "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } -+ // } -+ // } -+ // } -+ // trace(2, 1) { -+ // -+ // span(0) { -+ // name "SQS.GetQueueAttributes" -+ // kind CLIENT -+ // hasNoParent() -+ // attributes { -+ // "aws.agent" "java-aws-sdk" -+ // "aws.endpoint" String -+ // "rpc.method" "GetQueueAttributes" -+ // "aws.queue.url" queueUrl -+ // "rpc.system" "aws-api" -+ // "rpc.service" "AmazonSQS" -+ // "http.method" "POST" -+ // "http.status_code" 200 -+ // "http.url" String -+ // "net.peer.name" String -+ // "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -+ // "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -+ // "net.peer.port" { it == null || Number } -+ // "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } -+ // "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } -+ // } -+ // } -+ // } -+ // trace(3, 1) { -+ // -+ // span(0) { -+ // name "SQS.SetQueueAttributes" -+ // kind CLIENT -+ // hasNoParent() -+ // attributes { -+ // "aws.agent" "java-aws-sdk" -+ // "aws.endpoint" String -+ // "rpc.method" "SetQueueAttributes" -+ // "aws.queue.url" queueUrl -+ // "rpc.system" "aws-api" -+ // "rpc.service" "AmazonSQS" -+ // "http.method" "POST" -+ // "http.status_code" 200 -+ // "http.url" String -+ // "net.peer.name" String -+ // "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -+ // "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -+ // "net.peer.port" { it == null || Number } -+ // "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } -+ // "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } -+ // } -+ // } -+ // } -+ // trace(4, 1) { -+ // -+ // span(0) { -+ // name "S3.SetBucketNotificationConfiguration" -+ // kind CLIENT -+ // hasNoParent() -+ // attributes { -+ // "aws.agent" "java-aws-sdk" -+ // "aws.endpoint" String -+ // "rpc.method" "SetBucketNotificationConfiguration" -+ // "rpc.system" "aws-api" -+ // "rpc.service" "Amazon S3" -+ // "aws.bucket.name" bucketName -+ // "http.method" "PUT" -+ // "http.status_code" 200 -+ // "http.url" String -+ // "net.peer.name" String -+ // "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -+ // "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -+ // "net.peer.port" { it == null || Number } -+ // "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } -+ // "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } -+ // } -+ // } -+ // } -+ // trace(5, 3) { -+ // span(0) { -+ // name "S3.PutObject" -+ // kind CLIENT -+ // hasNoParent() -+ // attributes { -+ // "aws.agent" "java-aws-sdk" -+ // "aws.endpoint" String -+ // "rpc.method" "PutObject" -+ // "rpc.system" "aws-api" -+ // "rpc.service" "Amazon S3" -+ // "aws.bucket.name" bucketName -+ // "http.method" "PUT" -+ // "http.status_code" 200 -+ // "http.url" String -+ // "net.peer.name" String -+ // "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -+ // "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -+ // "net.peer.port" { it == null || Number } -+ // "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } -+ // "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } -+ // } -+ // } -+ // span(1) { -+ // name "s3ToSqsTestQueue process" -+ // kind CONSUMER -+ // childOf span(0) -+ // attributes { -+ // "aws.agent" "java-aws-sdk" -+ // "aws.endpoint" String -+ // "rpc.method" "ReceiveMessage" -+ // "aws.queue.url" queueUrl -+ // "rpc.system" "aws-api" -+ // "rpc.service" "AmazonSQS" -+ // "http.method" "POST" -+ // "http.url" String -+ // "net.peer.name" String -+ // "net.peer.port" { it == null || Number } -+ // "$SemanticAttributes.MESSAGING_SYSTEM" "AmazonSQS" -+ // "$SemanticAttributes.MESSAGING_DESTINATION_NAME" "s3ToSqsTestQueue" -+ // "$SemanticAttributes.MESSAGING_OPERATION" "process" -+ // "$SemanticAttributes.MESSAGING_MESSAGE_ID" String -+ // } -+ // } -+ // span(2) { -+ // name "process child" -+ // childOf span(1) -+ // attributes { -+ // } -+ // } -+ // } -+ // trace(6, 1) { -+ // span(0) { -+ // name "S3.ListObjects" -+ // kind CLIENT -+ // hasNoParent() -+ // attributes { -+ // "aws.agent" "java-aws-sdk" -+ // "aws.endpoint" String -+ // "rpc.method" "ListObjects" -+ // "rpc.system" "aws-api" -+ // "rpc.service" "Amazon S3" -+ // "aws.bucket.name" bucketName -+ // "http.method" "GET" -+ // "http.status_code" 200 -+ // "http.url" String -+ // "net.peer.name" String -+ // "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -+ // "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -+ // "net.peer.port" { it == null || Number } -+ // "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } -+ // "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } -+ // } -+ // } -+ // } -+ // trace(7, 1) { -+ // span(0) { -+ // name "S3.DeleteObject" -+ // kind CLIENT -+ // hasNoParent() -+ // attributes { -+ // "aws.agent" "java-aws-sdk" -+ // "aws.endpoint" String -+ // "rpc.method" "DeleteObject" -+ // "rpc.system" "aws-api" -+ // "rpc.service" "Amazon S3" -+ // "aws.bucket.name" bucketName -+ // "http.method" "DELETE" -+ // "http.status_code" 204 -+ // "http.url" String -+ // "net.peer.name" String -+ // "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -+ // "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -+ // "net.peer.port" { it == null || Number } -+ // "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } -+ // "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } -+ // } -+ // } -+ // } -+ // trace(8, 1) { -+ // span(0) { -+ // name "S3.DeleteBucket" -+ // kind CLIENT -+ // hasNoParent() -+ // attributes { -+ // "aws.agent" "java-aws-sdk" -+ // "aws.endpoint" String -+ // "rpc.method" "DeleteBucket" -+ // "rpc.system" "aws-api" -+ // "rpc.service" "Amazon S3" -+ // "aws.bucket.name" bucketName -+ // "http.method" "DELETE" -+ // "http.status_code" 204 -+ // "http.url" String -+ // "net.peer.name" String -+ // "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -+ // "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -+ // "net.peer.port" { it == null || Number } -+ // "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } -+ // "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } -+ // } -+ // } -+ // } -+ // trace(9, 1) { -+ // span(0) { -+ // name "SQS.PurgeQueue" -+ // kind CLIENT -+ // hasNoParent() -+ // attributes { -+ // "aws.agent" "java-aws-sdk" -+ // "aws.endpoint" String -+ // "rpc.method" "PurgeQueue" -+ // "aws.queue.url" queueUrl -+ // "rpc.system" "aws-api" -+ // "rpc.service" "AmazonSQS" -+ // "http.method" "POST" -+ // "http.status_code" 200 -+ // "http.url" String -+ // "net.peer.name" String -+ // "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -+ // "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -+ // "net.peer.port" { it == null || Number } -+ // "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } -+ // "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } -+ // } -+ // } -+ // } -+ // } -+ //} - -- String queueUrl = awsConnector.createQueue(queueName) -- awsConnector.createBucket(bucketName) -- -- String queueArn = awsConnector.getQueueArn(queueUrl) -- awsConnector.setQueuePublishingPolicy(queueUrl, queueArn) -- awsConnector.enableS3ToSqsNotifications(bucketName, queueArn) -- -- when: -- // test message, auto created by AWS -- awsConnector.receiveMessage(queueUrl) -- awsConnector.putSampleData(bucketName) -- // traced message -- def receiveMessageResult = awsConnector.receiveMessage(queueUrl) -- receiveMessageResult.messages.each {message -> -- runWithSpan("process child") {} -- } -- -- // cleanup -- awsConnector.deleteBucket(bucketName) -- awsConnector.purgeQueue(queueUrl) -- -- then: -- assertTraces(10) { -- trace(0, 1) { -- -- span(0) { -- name "SQS.CreateQueue" -- kind CLIENT -- hasNoParent() -- attributes { -- "aws.agent" "java-aws-sdk" -- "aws.endpoint" String -- "rpc.method" "CreateQueue" -- "aws.queue.name" queueName -- "rpc.system" "aws-api" -- "rpc.service" "AmazonSQS" -- "http.method" "POST" -- "http.status_code" 200 -- "http.url" String -- "net.peer.name" String -- "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -- "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -- "net.peer.port" { it == null || Number } -- "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } -- "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } -- } -- } -- } -- trace(1, 1) { -- -- span(0) { -- name "S3.CreateBucket" -- kind CLIENT -- hasNoParent() -- attributes { -- "aws.agent" "java-aws-sdk" -- "aws.endpoint" String -- "rpc.method" "CreateBucket" -- "rpc.system" "aws-api" -- "rpc.service" "Amazon S3" -- "aws.bucket.name" bucketName -- "http.method" "PUT" -- "http.status_code" 200 -- "http.url" String -- "net.peer.name" String -- "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -- "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -- "net.peer.port" { it == null || Number } -- "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } -- "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } -- } -- } -- } -- trace(2, 1) { -- -- span(0) { -- name "SQS.GetQueueAttributes" -- kind CLIENT -- hasNoParent() -- attributes { -- "aws.agent" "java-aws-sdk" -- "aws.endpoint" String -- "rpc.method" "GetQueueAttributes" -- "aws.queue.url" queueUrl -- "rpc.system" "aws-api" -- "rpc.service" "AmazonSQS" -- "http.method" "POST" -- "http.status_code" 200 -- "http.url" String -- "net.peer.name" String -- "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -- "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -- "net.peer.port" { it == null || Number } -- "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } -- "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } -- } -- } -- } -- trace(3, 1) { -- -- span(0) { -- name "SQS.SetQueueAttributes" -- kind CLIENT -- hasNoParent() -- attributes { -- "aws.agent" "java-aws-sdk" -- "aws.endpoint" String -- "rpc.method" "SetQueueAttributes" -- "aws.queue.url" queueUrl -- "rpc.system" "aws-api" -- "rpc.service" "AmazonSQS" -- "http.method" "POST" -- "http.status_code" 200 -- "http.url" String -- "net.peer.name" String -- "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -- "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -- "net.peer.port" { it == null || Number } -- "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } -- "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } -- } -- } -- } -- trace(4, 1) { -- -- span(0) { -- name "S3.SetBucketNotificationConfiguration" -- kind CLIENT -- hasNoParent() -- attributes { -- "aws.agent" "java-aws-sdk" -- "aws.endpoint" String -- "rpc.method" "SetBucketNotificationConfiguration" -- "rpc.system" "aws-api" -- "rpc.service" "Amazon S3" -- "aws.bucket.name" bucketName -- "http.method" "PUT" -- "http.status_code" 200 -- "http.url" String -- "net.peer.name" String -- "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -- "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -- "net.peer.port" { it == null || Number } -- "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } -- "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } -- } -- } -- } -- trace(5, 3) { -- span(0) { -- name "S3.PutObject" -- kind CLIENT -- hasNoParent() -- attributes { -- "aws.agent" "java-aws-sdk" -- "aws.endpoint" String -- "rpc.method" "PutObject" -- "rpc.system" "aws-api" -- "rpc.service" "Amazon S3" -- "aws.bucket.name" bucketName -- "http.method" "PUT" -- "http.status_code" 200 -- "http.url" String -- "net.peer.name" String -- "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -- "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -- "net.peer.port" { it == null || Number } -- "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } -- "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } -- } -- } -- span(1) { -- name "s3ToSqsTestQueue process" -- kind CONSUMER -- childOf span(0) -- attributes { -- "aws.agent" "java-aws-sdk" -- "aws.endpoint" String -- "rpc.method" "ReceiveMessage" -- "aws.queue.url" queueUrl -- "rpc.system" "aws-api" -- "rpc.service" "AmazonSQS" -- "http.method" "POST" -- "http.url" String -- "net.peer.name" String -- "net.peer.port" { it == null || Number } -- "$SemanticAttributes.MESSAGING_SYSTEM" "AmazonSQS" -- "$SemanticAttributes.MESSAGING_DESTINATION_NAME" "s3ToSqsTestQueue" -- "$SemanticAttributes.MESSAGING_OPERATION" "process" -- "$SemanticAttributes.MESSAGING_MESSAGE_ID" String -- } -- } -- span(2) { -- name "process child" -- childOf span(1) -- attributes { -- } -- } -- } -- trace(6, 1) { -- span(0) { -- name "S3.ListObjects" -- kind CLIENT -- hasNoParent() -- attributes { -- "aws.agent" "java-aws-sdk" -- "aws.endpoint" String -- "rpc.method" "ListObjects" -- "rpc.system" "aws-api" -- "rpc.service" "Amazon S3" -- "aws.bucket.name" bucketName -- "http.method" "GET" -- "http.status_code" 200 -- "http.url" String -- "net.peer.name" String -- "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -- "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -- "net.peer.port" { it == null || Number } -- "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } -- "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } -- } -- } -- } -- trace(7, 1) { -- span(0) { -- name "S3.DeleteObject" -- kind CLIENT -- hasNoParent() -- attributes { -- "aws.agent" "java-aws-sdk" -- "aws.endpoint" String -- "rpc.method" "DeleteObject" -- "rpc.system" "aws-api" -- "rpc.service" "Amazon S3" -- "aws.bucket.name" bucketName -- "http.method" "DELETE" -- "http.status_code" 204 -- "http.url" String -- "net.peer.name" String -- "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -- "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -- "net.peer.port" { it == null || Number } -- "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } -- "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } -- } -- } -- } -- trace(8, 1) { -- span(0) { -- name "S3.DeleteBucket" -- kind CLIENT -- hasNoParent() -- attributes { -- "aws.agent" "java-aws-sdk" -- "aws.endpoint" String -- "rpc.method" "DeleteBucket" -- "rpc.system" "aws-api" -- "rpc.service" "Amazon S3" -- "aws.bucket.name" bucketName -- "http.method" "DELETE" -- "http.status_code" 204 -- "http.url" String -- "net.peer.name" String -- "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -- "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -- "net.peer.port" { it == null || Number } -- "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } -- "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } -- } -- } -- } -- trace(9, 1) { -- span(0) { -- name "SQS.PurgeQueue" -- kind CLIENT -- hasNoParent() -- attributes { -- "aws.agent" "java-aws-sdk" -- "aws.endpoint" String -- "rpc.method" "PurgeQueue" -- "aws.queue.url" queueUrl -- "rpc.system" "aws-api" -- "rpc.service" "AmazonSQS" -- "http.method" "POST" -- "http.status_code" 200 -- "http.url" String -- "net.peer.name" String -- "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -- "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -- "net.peer.port" { it == null || Number } -- "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } -- "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } -- } -- } -- } -- } -- } -- -- def "S3 upload triggers SNS topic notification, then creates SQS message"() { -- setup: -- String queueName = "s3ToSnsToSqsTestQueue" -- String bucketName = "otel-s3-sns-sqs-test-bucket" -- String topicName = "s3ToSnsToSqsTestTopic" -- -- String queueUrl = awsConnector.createQueue(queueName) -- String queueArn = awsConnector.getQueueArn(queueUrl) -- awsConnector.createBucket(bucketName) -- String topicArn = awsConnector.createTopicAndSubscribeQueue(topicName, queueArn) -- -- awsConnector.setQueuePublishingPolicy(queueUrl, queueArn) -- awsConnector.setTopicPublishingPolicy(topicArn) -- awsConnector.enableS3ToSnsNotifications(bucketName, topicArn) -- -- when: -- // test message, auto created by AWS -- awsConnector.receiveMessage(queueUrl) -- awsConnector.putSampleData(bucketName) -- // traced message -- def receiveMessageResult = awsConnector.receiveMessage(queueUrl) -- receiveMessageResult.messages.each {message -> -- runWithSpan("process child") {} -- } -- // cleanup -- awsConnector.deleteBucket(bucketName) -- awsConnector.purgeQueue(queueUrl) -- -- then: -- assertTraces(14) { -- trace(0, 1) { -- span(0) { -- name "SQS.CreateQueue" -- kind CLIENT -- hasNoParent() -- attributes { -- "aws.agent" "java-aws-sdk" -- "aws.endpoint" String -- "rpc.method" "CreateQueue" -- "aws.queue.name" queueName -- "rpc.system" "aws-api" -- "rpc.service" "AmazonSQS" -- "http.method" "POST" -- "http.status_code" 200 -- "http.url" String -- "net.peer.name" String -- "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -- "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -- "net.peer.port" { it == null || Number } -- "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } -- "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } -- } -- } -- } -- trace(1, 1) { -- span(0) { -- name "SQS.GetQueueAttributes" -- kind CLIENT -- hasNoParent() -- attributes { -- "aws.agent" "java-aws-sdk" -- "aws.endpoint" String -- "rpc.method" "GetQueueAttributes" -- "aws.queue.url" queueUrl -- "rpc.system" "aws-api" -- "rpc.service" "AmazonSQS" -- "http.method" "POST" -- "http.status_code" 200 -- "http.url" String -- "net.peer.name" String -- "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -- "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -- "net.peer.port" { it == null || Number } -- "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } -- "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } -- } -- } -- } -- trace(2, 1) { -- span(0) { -- name "S3.CreateBucket" -- kind CLIENT -- hasNoParent() -- attributes { -- "aws.agent" "java-aws-sdk" -- "aws.endpoint" String -- "rpc.method" "CreateBucket" -- "rpc.system" "aws-api" -- "rpc.service" "Amazon S3" -- "aws.bucket.name" bucketName -- "http.method" "PUT" -- "http.status_code" 200 -- "http.url" String -- "net.peer.name" String -- "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -- "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -- "net.peer.port" { it == null || Number } -- "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } -- "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } -- } -- } -- } -- trace(3, 1) { -- span(0) { -- name "SNS.CreateTopic" -- kind CLIENT -- hasNoParent() -- attributes { -- "aws.agent" "java-aws-sdk" -- "aws.endpoint" String -- "rpc.method" "CreateTopic" -- "rpc.system" "aws-api" -- "rpc.service" "AmazonSNS" -- "http.method" "POST" -- "http.status_code" 200 -- "http.url" String -- "net.peer.name" String -- "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -- "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -- "net.peer.port" { it == null || Number } -- "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } -- "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } -- } -- } -- } -- trace(4, 1) { -- span(0) { -- name "SNS.Subscribe" -- kind CLIENT -- hasNoParent() -- attributes { -- "aws.agent" "java-aws-sdk" -- "aws.endpoint" String -- "rpc.method" "Subscribe" -- "rpc.system" "aws-api" -- "rpc.service" "AmazonSNS" -- "http.method" "POST" -- "http.status_code" 200 -- "http.url" String -- "net.peer.name" String -- "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -- "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -- "net.peer.port" { it == null || Number } -- "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } -- "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } -- } -- } -- } -- trace(5, 1) { -- span(0) { -- name "SQS.SetQueueAttributes" -- kind CLIENT -- hasNoParent() -- attributes { -- "aws.agent" "java-aws-sdk" -- "aws.endpoint" String -- "rpc.method" "SetQueueAttributes" -- "aws.queue.url" queueUrl -- "rpc.system" "aws-api" -- "rpc.service" "AmazonSQS" -- "http.method" "POST" -- "http.status_code" 200 -- "http.url" String -- "net.peer.name" String -- "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -- "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -- "net.peer.port" { it == null || Number } -- "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } -- "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } -- } -- } -- } -- trace(6, 1) { -- span(0) { -- name "SNS.SetTopicAttributes" -- kind CLIENT -- hasNoParent() -- attributes { -- "aws.agent" "java-aws-sdk" -- "aws.endpoint" String -- "rpc.method" "SetTopicAttributes" -- "rpc.system" "aws-api" -- "rpc.service" "AmazonSNS" -- "http.method" "POST" -- "http.status_code" 200 -- "http.url" String -- "net.peer.name" String -- "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -- "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -- "net.peer.port" { it == null || Number } -- "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } -- "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } -- } -- } -- } -- trace(7, 1) { -- span(0) { -- name "S3.SetBucketNotificationConfiguration" -- kind CLIENT -- hasNoParent() -- attributes { -- "aws.agent" "java-aws-sdk" -- "aws.endpoint" String -- "rpc.method" "SetBucketNotificationConfiguration" -- "rpc.system" "aws-api" -- "rpc.service" "Amazon S3" -- "aws.bucket.name" bucketName -- "http.method" "PUT" -- "http.status_code" 200 -- "http.url" String -- "net.peer.name" String -- "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -- "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -- "net.peer.port" { it == null || Number } -- "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } -- "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } -- } -- } -- } -- trace(8, 1) { -- span(0) { -- name "S3.PutObject" -- kind CLIENT -- hasNoParent() -- attributes { -- "aws.agent" "java-aws-sdk" -- "aws.endpoint" String -- "rpc.method" "PutObject" -- "rpc.system" "aws-api" -- "rpc.service" "Amazon S3" -- "aws.bucket.name" bucketName -- "http.method" "PUT" -- "http.status_code" 200 -- "http.url" String -- "net.peer.name" String -- "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -- "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -- "net.peer.port" { it == null || Number } -- "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } -- "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } -- } -- } -- } -- trace(9, 2) { -- span(0) { -- name "s3ToSnsToSqsTestQueue process" -- kind CONSUMER -- hasNoParent() -- attributes { -- "aws.agent" "java-aws-sdk" -- "aws.endpoint" String -- "rpc.method" "ReceiveMessage" -- "aws.queue.url" queueUrl -- "rpc.system" "aws-api" -- "rpc.service" "AmazonSQS" -- "http.method" "POST" -- "http.url" String -- "net.peer.name" String -- "net.peer.port" { it == null || Number } -- "$SemanticAttributes.MESSAGING_SYSTEM" "AmazonSQS" -- "$SemanticAttributes.MESSAGING_DESTINATION_NAME" "s3ToSnsToSqsTestQueue" -- "$SemanticAttributes.MESSAGING_OPERATION" "process" -- "$SemanticAttributes.MESSAGING_MESSAGE_ID" String -- "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } -- "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } -- } -- } -- span(1) { -- name "process child" -- childOf span(0) -- attributes { -- } -- } -- } -- trace(10, 1) { -- span(0) { -- name "S3.ListObjects" -- kind CLIENT -- hasNoParent() -- attributes { -- "aws.agent" "java-aws-sdk" -- "aws.endpoint" String -- "rpc.method" "ListObjects" -- "rpc.system" "aws-api" -- "rpc.service" "Amazon S3" -- "aws.bucket.name" bucketName -- "http.method" "GET" -- "http.status_code" 200 -- "http.url" String -- "net.peer.name" String -- "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -- "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -- "net.peer.port" { it == null || Number } -- "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } -- "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } -- } -- } -- } -- trace(11, 1) { -- span(0) { -- name "S3.DeleteObject" -- kind CLIENT -- hasNoParent() -- attributes { -- "aws.agent" "java-aws-sdk" -- "aws.endpoint" String -- "rpc.method" "DeleteObject" -- "rpc.system" "aws-api" -- "rpc.service" "Amazon S3" -- "aws.bucket.name" bucketName -- "http.method" "DELETE" -- "http.status_code" 204 -- "http.url" String -- "net.peer.name" String -- "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -- "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -- "net.peer.port" { it == null || Number } -- "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } -- "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } -- } -- } -- } -- trace(12, 1) { -- span(0) { -- name "S3.DeleteBucket" -- kind CLIENT -- hasNoParent() -- attributes { -- "aws.agent" "java-aws-sdk" -- "aws.endpoint" String -- "rpc.method" "DeleteBucket" -- "rpc.system" "aws-api" -- "rpc.service" "Amazon S3" -- "aws.bucket.name" bucketName -- "http.method" "DELETE" -- "http.status_code" 204 -- "http.url" String -- "net.peer.name" String -- "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -- "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -- "net.peer.port" { it == null || Number } -- "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } -- "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } -- } -- } -- } -- trace(13, 1) { -- span(0) { -- name "SQS.PurgeQueue" -- kind CLIENT -- hasNoParent() -- attributes { -- "aws.agent" "java-aws-sdk" -- "aws.endpoint" String -- "rpc.method" "PurgeQueue" -- "aws.queue.url" queueUrl -- "rpc.system" "aws-api" -- "rpc.service" "AmazonSQS" -- "http.method" "POST" -- "http.status_code" 200 -- "http.url" String -- "net.peer.name" String -- "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -- "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -- "net.peer.port" { it == null || Number } -- "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } -- "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } -- } -- } -- } -- } -- } -+ //def "S3 upload triggers SNS topic notification, then creates SQS message"() { -+ // setup: -+ // String queueName = "s3ToSnsToSqsTestQueue" -+ // String bucketName = "otel-s3-sns-sqs-test-bucket" -+ // String topicName = "s3ToSnsToSqsTestTopic" -+ // -+ // String queueUrl = awsConnector.createQueue(queueName) -+ // String queueArn = awsConnector.getQueueArn(queueUrl) -+ // awsConnector.createBucket(bucketName) -+ // String topicArn = awsConnector.createTopicAndSubscribeQueue(topicName, queueArn) -+ // -+ // awsConnector.setQueuePublishingPolicy(queueUrl, queueArn) -+ // awsConnector.setTopicPublishingPolicy(topicArn) -+ // awsConnector.enableS3ToSnsNotifications(bucketName, topicArn) -+ // -+ // when: -+ // // test message, auto created by AWS -+ // awsConnector.receiveMessage(queueUrl) -+ // awsConnector.putSampleData(bucketName) -+ // // traced message -+ // def receiveMessageResult = awsConnector.receiveMessage(queueUrl) -+ // receiveMessageResult.messages.each {message -> -+ // runWithSpan("process child") {} -+ // } -+ // // cleanup -+ // awsConnector.deleteBucket(bucketName) -+ // awsConnector.purgeQueue(queueUrl) -+ // -+ // then: -+ // assertTraces(14) { -+ // trace(0, 1) { -+ // span(0) { -+ // name "SQS.CreateQueue" -+ // kind CLIENT -+ // hasNoParent() -+ // attributes { -+ // "aws.agent" "java-aws-sdk" -+ // "aws.endpoint" String -+ // "rpc.method" "CreateQueue" -+ // "aws.queue.name" queueName -+ // "rpc.system" "aws-api" -+ // "rpc.service" "AmazonSQS" -+ // "http.method" "POST" -+ // "http.status_code" 200 -+ // "http.url" String -+ // "net.peer.name" String -+ // "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -+ // "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -+ // "net.peer.port" { it == null || Number } -+ // "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } -+ // "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } -+ // } -+ // } -+ // } -+ // trace(1, 1) { -+ // span(0) { -+ // name "SQS.GetQueueAttributes" -+ // kind CLIENT -+ // hasNoParent() -+ // attributes { -+ // "aws.agent" "java-aws-sdk" -+ // "aws.endpoint" String -+ // "rpc.method" "GetQueueAttributes" -+ // "aws.queue.url" queueUrl -+ // "rpc.system" "aws-api" -+ // "rpc.service" "AmazonSQS" -+ // "http.method" "POST" -+ // "http.status_code" 200 -+ // "http.url" String -+ // "net.peer.name" String -+ // "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -+ // "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -+ // "net.peer.port" { it == null || Number } -+ // "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } -+ // "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } -+ // } -+ // } -+ // } -+ // trace(2, 1) { -+ // span(0) { -+ // name "S3.CreateBucket" -+ // kind CLIENT -+ // hasNoParent() -+ // attributes { -+ // "aws.agent" "java-aws-sdk" -+ // "aws.endpoint" String -+ // "rpc.method" "CreateBucket" -+ // "rpc.system" "aws-api" -+ // "rpc.service" "Amazon S3" -+ // "aws.bucket.name" bucketName -+ // "http.method" "PUT" -+ // "http.status_code" 200 -+ // "http.url" String -+ // "net.peer.name" String -+ // "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -+ // "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -+ // "net.peer.port" { it == null || Number } -+ // "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } -+ // "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } -+ // } -+ // } -+ // } -+ // trace(3, 1) { -+ // span(0) { -+ // name "SNS.CreateTopic" -+ // kind CLIENT -+ // hasNoParent() -+ // attributes { -+ // "aws.agent" "java-aws-sdk" -+ // "aws.endpoint" String -+ // "rpc.method" "CreateTopic" -+ // "rpc.system" "aws-api" -+ // "rpc.service" "AmazonSNS" -+ // "http.method" "POST" -+ // "http.status_code" 200 -+ // "http.url" String -+ // "net.peer.name" String -+ // "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -+ // "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -+ // "net.peer.port" { it == null || Number } -+ // "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } -+ // "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } -+ // } -+ // } -+ // } -+ // trace(4, 1) { -+ // span(0) { -+ // name "SNS.Subscribe" -+ // kind CLIENT -+ // hasNoParent() -+ // attributes { -+ // "aws.agent" "java-aws-sdk" -+ // "aws.endpoint" String -+ // "rpc.method" "Subscribe" -+ // "rpc.system" "aws-api" -+ // "rpc.service" "AmazonSNS" -+ // "http.method" "POST" -+ // "http.status_code" 200 -+ // "http.url" String -+ // "net.peer.name" String -+ // "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -+ // "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -+ // "net.peer.port" { it == null || Number } -+ // "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } -+ // "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } -+ // } -+ // } -+ // } -+ // trace(5, 1) { -+ // span(0) { -+ // name "SQS.SetQueueAttributes" -+ // kind CLIENT -+ // hasNoParent() -+ // attributes { -+ // "aws.agent" "java-aws-sdk" -+ // "aws.endpoint" String -+ // "rpc.method" "SetQueueAttributes" -+ // "aws.queue.url" queueUrl -+ // "rpc.system" "aws-api" -+ // "rpc.service" "AmazonSQS" -+ // "http.method" "POST" -+ // "http.status_code" 200 -+ // "http.url" String -+ // "net.peer.name" String -+ // "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -+ // "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -+ // "net.peer.port" { it == null || Number } -+ // "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } -+ // "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } -+ // } -+ // } -+ // } -+ // trace(6, 1) { -+ // span(0) { -+ // name "SNS.SetTopicAttributes" -+ // kind CLIENT -+ // hasNoParent() -+ // attributes { -+ // "aws.agent" "java-aws-sdk" -+ // "aws.endpoint" String -+ // "rpc.method" "SetTopicAttributes" -+ // "rpc.system" "aws-api" -+ // "rpc.service" "AmazonSNS" -+ // "http.method" "POST" -+ // "http.status_code" 200 -+ // "http.url" String -+ // "net.peer.name" String -+ // "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -+ // "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -+ // "net.peer.port" { it == null || Number } -+ // "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } -+ // "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } -+ // } -+ // } -+ // } -+ // trace(7, 1) { -+ // span(0) { -+ // name "S3.SetBucketNotificationConfiguration" -+ // kind CLIENT -+ // hasNoParent() -+ // attributes { -+ // "aws.agent" "java-aws-sdk" -+ // "aws.endpoint" String -+ // "rpc.method" "SetBucketNotificationConfiguration" -+ // "rpc.system" "aws-api" -+ // "rpc.service" "Amazon S3" -+ // "aws.bucket.name" bucketName -+ // "http.method" "PUT" -+ // "http.status_code" 200 -+ // "http.url" String -+ // "net.peer.name" String -+ // "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -+ // "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -+ // "net.peer.port" { it == null || Number } -+ // "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } -+ // "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } -+ // } -+ // } -+ // } -+ // trace(8, 1) { -+ // span(0) { -+ // name "S3.PutObject" -+ // kind CLIENT -+ // hasNoParent() -+ // attributes { -+ // "aws.agent" "java-aws-sdk" -+ // "aws.endpoint" String -+ // "rpc.method" "PutObject" -+ // "rpc.system" "aws-api" -+ // "rpc.service" "Amazon S3" -+ // "aws.bucket.name" bucketName -+ // "http.method" "PUT" -+ // "http.status_code" 200 -+ // "http.url" String -+ // "net.peer.name" String -+ // "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -+ // "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -+ // "net.peer.port" { it == null || Number } -+ // "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } -+ // "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } -+ // } -+ // } -+ // } -+ // trace(9, 2) { -+ // span(0) { -+ // name "s3ToSnsToSqsTestQueue process" -+ // kind CONSUMER -+ // hasNoParent() -+ // attributes { -+ // "aws.agent" "java-aws-sdk" -+ // "aws.endpoint" String -+ // "rpc.method" "ReceiveMessage" -+ // "aws.queue.url" queueUrl -+ // "rpc.system" "aws-api" -+ // "rpc.service" "AmazonSQS" -+ // "http.method" "POST" -+ // "http.url" String -+ // "net.peer.name" String -+ // "net.peer.port" { it == null || Number } -+ // "$SemanticAttributes.MESSAGING_SYSTEM" "AmazonSQS" -+ // "$SemanticAttributes.MESSAGING_DESTINATION_NAME" "s3ToSnsToSqsTestQueue" -+ // "$SemanticAttributes.MESSAGING_OPERATION" "process" -+ // "$SemanticAttributes.MESSAGING_MESSAGE_ID" String -+ // "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } -+ // "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } -+ // } -+ // } -+ // span(1) { -+ // name "process child" -+ // childOf span(0) -+ // attributes { -+ // } -+ // } -+ // } -+ // trace(10, 1) { -+ // span(0) { -+ // name "S3.ListObjects" -+ // kind CLIENT -+ // hasNoParent() -+ // attributes { -+ // "aws.agent" "java-aws-sdk" -+ // "aws.endpoint" String -+ // "rpc.method" "ListObjects" -+ // "rpc.system" "aws-api" -+ // "rpc.service" "Amazon S3" -+ // "aws.bucket.name" bucketName -+ // "http.method" "GET" -+ // "http.status_code" 200 -+ // "http.url" String -+ // "net.peer.name" String -+ // "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -+ // "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -+ // "net.peer.port" { it == null || Number } -+ // "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } -+ // "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } -+ // } -+ // } -+ // } -+ // trace(11, 1) { -+ // span(0) { -+ // name "S3.DeleteObject" -+ // kind CLIENT -+ // hasNoParent() -+ // attributes { -+ // "aws.agent" "java-aws-sdk" -+ // "aws.endpoint" String -+ // "rpc.method" "DeleteObject" -+ // "rpc.system" "aws-api" -+ // "rpc.service" "Amazon S3" -+ // "aws.bucket.name" bucketName -+ // "http.method" "DELETE" -+ // "http.status_code" 204 -+ // "http.url" String -+ // "net.peer.name" String -+ // "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -+ // "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -+ // "net.peer.port" { it == null || Number } -+ // "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } -+ // "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } -+ // } -+ // } -+ // } -+ // trace(12, 1) { -+ // span(0) { -+ // name "S3.DeleteBucket" -+ // kind CLIENT -+ // hasNoParent() -+ // attributes { -+ // "aws.agent" "java-aws-sdk" -+ // "aws.endpoint" String -+ // "rpc.method" "DeleteBucket" -+ // "rpc.system" "aws-api" -+ // "rpc.service" "Amazon S3" -+ // "aws.bucket.name" bucketName -+ // "http.method" "DELETE" -+ // "http.status_code" 204 -+ // "http.url" String -+ // "net.peer.name" String -+ // "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -+ // "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -+ // "net.peer.port" { it == null || Number } -+ // "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } -+ // "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } -+ // } -+ // } -+ // } -+ // trace(13, 1) { -+ // span(0) { -+ // name "SQS.PurgeQueue" -+ // kind CLIENT -+ // hasNoParent() -+ // attributes { -+ // "aws.agent" "java-aws-sdk" -+ // "aws.endpoint" String -+ // "rpc.method" "PurgeQueue" -+ // "aws.queue.url" queueUrl -+ // "rpc.system" "aws-api" -+ // "rpc.service" "AmazonSQS" -+ // "http.method" "POST" -+ // "http.status_code" 200 -+ // "http.url" String -+ // "net.peer.name" String -+ // "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -+ // "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -+ // "net.peer.port" { it == null || Number } -+ // "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } -+ // "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } -+ // } -+ // } -+ // } -+ // } -+ //} - } +@@ -444,6 +444,7 @@ class S3TracingTest extends AgentInstrumentationSpecification { + "net.peer.port" { it == null || Number } + "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } ++ "aws.sns.topic.arn" "$topicArn" + } + } + } +@@ -467,6 +468,7 @@ class S3TracingTest extends AgentInstrumentationSpecification { + "net.peer.port" { it == null || Number } + "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } ++ "aws.sns.topic.arn" "$topicArn" + } + } + } +@@ -514,6 +516,7 @@ class S3TracingTest extends AgentInstrumentationSpecification { + "net.peer.port" { it == null || Number } + "$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" { it == null || it instanceof Long } ++ "aws.sns.topic.arn" "$topicArn" + } + } + } diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/test/groovy/SnsTracingTest.groovy b/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/test/groovy/SnsTracingTest.groovy -index 97749cf085..f7402c1e4b 100644 +index 97749cf085..a0b83ca870 100644 --- a/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/test/groovy/SnsTracingTest.groovy +++ b/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/test/groovy/SnsTracingTest.groovy -@@ -20,192 +20,192 @@ class SnsTracingTest extends AgentInstrumentationSpecification { - awsConnector.disconnect() - } - -- def "SNS notification triggers SQS message consumed with AWS SDK"() { -- setup: -- String queueName = "snsToSqsTestQueue" -- String topicName = "snsToSqsTestTopic" -- -- String queueUrl = awsConnector.createQueue(queueName) -- String queueArn = awsConnector.getQueueArn(queueUrl) -- awsConnector.setQueuePublishingPolicy(queueUrl, queueArn) -- String topicArn = awsConnector.createTopicAndSubscribeQueue(topicName, queueArn) -- -- when: -- awsConnector.publishSampleNotification(topicArn) -- def receiveMessageResult = awsConnector.receiveMessage(queueUrl) -- receiveMessageResult.messages.each {message -> -- runWithSpan("process child") {} -- } -- -- then: -- assertTraces(6) { -- trace(0, 1) { -- -- span(0) { -- name "SQS.CreateQueue" -- kind CLIENT -- hasNoParent() -- attributes { -- "aws.agent" "java-aws-sdk" -- "aws.endpoint" String -- "rpc.method" "CreateQueue" -- "aws.queue.name" queueName -- "rpc.system" "aws-api" -- "rpc.service" "AmazonSQS" -- "http.method" "POST" -- "http.status_code" 200 -- "http.url" String -- "net.peer.name" String -- "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -- "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -- "net.peer.port" { it == null || Number } -- "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long -- } -- } -- } -- trace(1, 1) { -- -- span(0) { -- name "SQS.GetQueueAttributes" -- kind CLIENT -- hasNoParent() -- attributes { -- "aws.agent" "java-aws-sdk" -- "aws.endpoint" String -- "rpc.method" "GetQueueAttributes" -- "aws.queue.url" queueUrl -- "rpc.system" "aws-api" -- "rpc.service" "AmazonSQS" -- "http.method" "POST" -- "http.status_code" 200 -- "http.url" String -- "net.peer.name" String -- "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -- "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -- "net.peer.port" { it == null || Number } -- "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long -- } -- } -- } -- trace(2, 1) { -- -- span(0) { -- name "SQS.SetQueueAttributes" -- kind CLIENT -- hasNoParent() -- attributes { -- "aws.agent" "java-aws-sdk" -- "aws.endpoint" String -- "rpc.method" "SetQueueAttributes" -- "aws.queue.url" queueUrl -- "rpc.system" "aws-api" -- "rpc.service" "AmazonSQS" -- "http.method" "POST" -- "http.status_code" 200 -- "http.url" String -- "net.peer.name" String -- "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -- "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -- "net.peer.port" { it == null || Number } -- "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long -- } -- } -- } -- trace(3, 1) { -- -- span(0) { -- name "SNS.CreateTopic" -- kind CLIENT -- hasNoParent() -- attributes { -- "aws.agent" "java-aws-sdk" -- "aws.endpoint" String -- "rpc.method" "CreateTopic" -- "rpc.system" "aws-api" -- "rpc.service" "AmazonSNS" -- "http.method" "POST" -- "http.status_code" 200 -- "http.url" String -- "net.peer.name" String -- "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -- "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -- "net.peer.port" { it == null || Number } -- "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long -- } -- } -- } -- trace(4, 1) { -- -- span(0) { -- name "SNS.Subscribe" -- kind CLIENT -- hasNoParent() -- attributes { -- "aws.agent" "java-aws-sdk" -- "aws.endpoint" String -- "rpc.method" "Subscribe" -- "rpc.system" "aws-api" -- "rpc.service" "AmazonSNS" -- "http.method" "POST" -- "http.status_code" 200 -- "http.url" String -- "net.peer.name" String -- "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -- "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -- "net.peer.port" { it == null || Number } -- "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long -- } -- } -- } -- trace(5, 3) { -- span(0) { -- name "SNS.Publish" -- kind CLIENT -- hasNoParent() -- attributes { -- "aws.agent" "java-aws-sdk" -- "aws.endpoint" String -- "rpc.method" "Publish" -- "rpc.system" "aws-api" -- "rpc.service" "AmazonSNS" -- "http.method" "POST" -- "http.status_code" 200 -- "http.url" String -- "net.peer.name" String -- "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -- "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -- "net.peer.port" { it == null || Number } -- "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long -- } -- } -- span(1) { -- name "snsToSqsTestQueue process" -- kind CONSUMER -- childOf span(0) -- attributes { -- "aws.agent" "java-aws-sdk" -- "aws.endpoint" String -- "aws.queue.url" queueUrl -- "rpc.system" "aws-api" -- "rpc.service" "AmazonSQS" -- "rpc.method" "ReceiveMessage" -- "http.method" "POST" -- "http.url" String -- "net.peer.name" String -- "net.peer.port" { it == null || Number } -- "$SemanticAttributes.MESSAGING_SYSTEM" "AmazonSQS" -- "$SemanticAttributes.MESSAGING_DESTINATION_NAME" "snsToSqsTestQueue" -- "$SemanticAttributes.MESSAGING_OPERATION" "process" -- "$SemanticAttributes.MESSAGING_MESSAGE_ID" String -- } -- } -- span(2) { -- name "process child" -- childOf span(1) -- attributes { -- } -- } -- } -- } -- } -+ //def "SNS notification triggers SQS message consumed with AWS SDK"() { -+ // setup: -+ // String queueName = "snsToSqsTestQueue" -+ // String topicName = "snsToSqsTestTopic" -+ // -+ // String queueUrl = awsConnector.createQueue(queueName) -+ // String queueArn = awsConnector.getQueueArn(queueUrl) -+ // awsConnector.setQueuePublishingPolicy(queueUrl, queueArn) -+ // String topicArn = awsConnector.createTopicAndSubscribeQueue(topicName, queueArn) -+ // -+ // when: -+ // awsConnector.publishSampleNotification(topicArn) -+ // def receiveMessageResult = awsConnector.receiveMessage(queueUrl) -+ // receiveMessageResult.messages.each {message -> -+ // runWithSpan("process child") {} -+ // } -+ // -+ // then: -+ // assertTraces(6) { -+ // trace(0, 1) { -+ // -+ // span(0) { -+ // name "SQS.CreateQueue" -+ // kind CLIENT -+ // hasNoParent() -+ // attributes { -+ // "aws.agent" "java-aws-sdk" -+ // "aws.endpoint" String -+ // "rpc.method" "CreateQueue" -+ // "aws.queue.name" queueName -+ // "rpc.system" "aws-api" -+ // "rpc.service" "AmazonSQS" -+ // "http.method" "POST" -+ // "http.status_code" 200 -+ // "http.url" String -+ // "net.peer.name" String -+ // "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -+ // "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -+ // "net.peer.port" { it == null || Number } -+ // "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long -+ // } -+ // } -+ // } -+ // trace(1, 1) { -+ // -+ // span(0) { -+ // name "SQS.GetQueueAttributes" -+ // kind CLIENT -+ // hasNoParent() -+ // attributes { -+ // "aws.agent" "java-aws-sdk" -+ // "aws.endpoint" String -+ // "rpc.method" "GetQueueAttributes" -+ // "aws.queue.url" queueUrl -+ // "rpc.system" "aws-api" -+ // "rpc.service" "AmazonSQS" -+ // "http.method" "POST" -+ // "http.status_code" 200 -+ // "http.url" String -+ // "net.peer.name" String -+ // "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -+ // "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -+ // "net.peer.port" { it == null || Number } -+ // "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long -+ // } -+ // } -+ // } -+ // trace(2, 1) { -+ // -+ // span(0) { -+ // name "SQS.SetQueueAttributes" -+ // kind CLIENT -+ // hasNoParent() -+ // attributes { -+ // "aws.agent" "java-aws-sdk" -+ // "aws.endpoint" String -+ // "rpc.method" "SetQueueAttributes" -+ // "aws.queue.url" queueUrl -+ // "rpc.system" "aws-api" -+ // "rpc.service" "AmazonSQS" -+ // "http.method" "POST" -+ // "http.status_code" 200 -+ // "http.url" String -+ // "net.peer.name" String -+ // "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -+ // "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -+ // "net.peer.port" { it == null || Number } -+ // "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long -+ // } -+ // } -+ // } -+ // trace(3, 1) { -+ // -+ // span(0) { -+ // name "SNS.CreateTopic" -+ // kind CLIENT -+ // hasNoParent() -+ // attributes { -+ // "aws.agent" "java-aws-sdk" -+ // "aws.endpoint" String -+ // "rpc.method" "CreateTopic" -+ // "rpc.system" "aws-api" -+ // "rpc.service" "AmazonSNS" -+ // "http.method" "POST" -+ // "http.status_code" 200 -+ // "http.url" String -+ // "net.peer.name" String -+ // "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -+ // "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -+ // "net.peer.port" { it == null || Number } -+ // "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long -+ // } -+ // } -+ // } -+ // trace(4, 1) { -+ // -+ // span(0) { -+ // name "SNS.Subscribe" -+ // kind CLIENT -+ // hasNoParent() -+ // attributes { -+ // "aws.agent" "java-aws-sdk" -+ // "aws.endpoint" String -+ // "rpc.method" "Subscribe" -+ // "rpc.system" "aws-api" -+ // "rpc.service" "AmazonSNS" -+ // "http.method" "POST" -+ // "http.status_code" 200 -+ // "http.url" String -+ // "net.peer.name" String -+ // "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -+ // "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -+ // "net.peer.port" { it == null || Number } -+ // "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long -+ // } -+ // } -+ // } -+ // trace(5, 3) { -+ // span(0) { -+ // name "SNS.Publish" -+ // kind CLIENT -+ // hasNoParent() -+ // attributes { -+ // "aws.agent" "java-aws-sdk" -+ // "aws.endpoint" String -+ // "rpc.method" "Publish" -+ // "rpc.system" "aws-api" -+ // "rpc.service" "AmazonSNS" -+ // "http.method" "POST" -+ // "http.status_code" 200 -+ // "http.url" String -+ // "net.peer.name" String -+ // "$SemanticAttributes.NET_PROTOCOL_NAME" "http" -+ // "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" -+ // "net.peer.port" { it == null || Number } -+ // "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long -+ // } -+ // } -+ // span(1) { -+ // name "snsToSqsTestQueue process" -+ // kind CONSUMER -+ // childOf span(0) -+ // attributes { -+ // "aws.agent" "java-aws-sdk" -+ // "aws.endpoint" String -+ // "aws.queue.url" queueUrl -+ // "rpc.system" "aws-api" -+ // "rpc.service" "AmazonSQS" -+ // "rpc.method" "ReceiveMessage" -+ // "http.method" "POST" -+ // "http.url" String -+ // "net.peer.name" String -+ // "net.peer.port" { it == null || Number } -+ // "$SemanticAttributes.MESSAGING_SYSTEM" "AmazonSQS" -+ // "$SemanticAttributes.MESSAGING_DESTINATION_NAME" "snsToSqsTestQueue" -+ // "$SemanticAttributes.MESSAGING_OPERATION" "process" -+ // "$SemanticAttributes.MESSAGING_MESSAGE_ID" String -+ // } -+ // } -+ // span(2) { -+ // name "process child" -+ // childOf span(1) -+ // attributes { -+ // } -+ // } -+ // } -+ // } -+ //} - } -diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/test_before_1_11_106/groovy/Aws0ClientTest.groovy b/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/test_before_1_11_106/groovy/Aws0ClientTest.groovy -index 543b6e8e8e..e4703eac17 100644 ---- a/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/test_before_1_11_106/groovy/Aws0ClientTest.groovy -+++ b/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/test_before_1_11_106/groovy/Aws0ClientTest.groovy -@@ -133,8 +133,8 @@ class Aws0ClientTest extends AgentInstrumentationSpecification { - - where: - service | operation | method | path | handlerCount | client | additionalAttributes | call | body -- "S3" | "CreateBucket" | "PUT" | "/testbucket/" | 1 | new AmazonS3Client().withEndpoint("${server.httpUri()}") | ["aws.bucket.name": "testbucket"] | { c -> c.setS3ClientOptions(S3ClientOptions.builder().setPathStyleAccess(true).build()); c.createBucket("testbucket") } | "" -- "S3" | "GetObject" | "GET" | "/someBucket/someKey" | 1 | new AmazonS3Client().withEndpoint("${server.httpUri()}") | ["aws.bucket.name": "someBucket"] | { c -> c.getObject("someBucket", "someKey") } | "" -+ //"S3" | "CreateBucket" | "PUT" | "/testbucket/" | 1 | new AmazonS3Client().withEndpoint("${server.httpUri()}") | ["aws.bucket.name": "testbucket"] | { c -> c.setS3ClientOptions(S3ClientOptions.builder().setPathStyleAccess(true).build()); c.createBucket("testbucket") } | "" -+ //"S3" | "GetObject" | "GET" | "/someBucket/someKey" | 1 | new AmazonS3Client().withEndpoint("${server.httpUri()}") | ["aws.bucket.name": "someBucket"] | { c -> c.getObject("someBucket", "someKey") } | "" - "EC2" | "AllocateAddress" | "POST" | "/" | 4 | new AmazonEC2Client().withEndpoint("${server.httpUri()}") | [:] | { c -> c.allocateAddress() } | """ - - 59dbff89-35bd-4eac-99ed-be587EXAMPLE +@@ -131,6 +131,7 @@ class SnsTracingTest extends AgentInstrumentationSpecification { + "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" + "net.peer.port" { it == null || Number } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long ++ "aws.sns.topic.arn" "$topicArn" + } + } + } +@@ -154,6 +155,7 @@ class SnsTracingTest extends AgentInstrumentationSpecification { + "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" + "net.peer.port" { it == null || Number } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long ++ "aws.sns.topic.arn" "$topicArn" + } + } + } +@@ -176,6 +178,7 @@ class SnsTracingTest extends AgentInstrumentationSpecification { + "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1" + "net.peer.port" { it == null || Number } + "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long ++ "aws.sns.topic.arn" "$topicArn" + } + } + span(1) { diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/library-autoconfigure/build.gradle.kts b/instrumentation/aws-sdk/aws-sdk-1.11/library-autoconfigure/build.gradle.kts -index 6cf49a21c4..d2f9267072 100644 +index 6cf49a21c4..3705634153 100644 --- a/instrumentation/aws-sdk/aws-sdk-1.11/library-autoconfigure/build.gradle.kts +++ b/instrumentation/aws-sdk/aws-sdk-1.11/library-autoconfigure/build.gradle.kts @@ -18,6 +18,13 @@ dependencies { @@ -1784,7 +82,7 @@ index 6cf49a21c4..d2f9267072 100644 testLibrary("com.amazonaws:aws-java-sdk-sns:1.11.106") testLibrary("com.amazonaws:aws-java-sdk-sqs:1.11.106") + testLibrary("com.amazonaws:aws-java-sdk-secretsmanager:1.11.309") -+ // testLibrary("com.amazonaws:aws-java-sdk-stepfunctions:1.11.230") ++ testLibrary("com.amazonaws:aws-java-sdk-stepfunctions:1.11.230") + testLibrary("com.amazonaws:aws-java-sdk-lambda:1.11.678") + testLibrary("com.amazonaws:aws-java-sdk-bedrock:1.12.744") + testLibrary("com.amazonaws:aws-java-sdk-bedrockagent:1.12.744") @@ -1794,7 +92,7 @@ index 6cf49a21c4..d2f9267072 100644 // last version that does not use json protocol latestDepTestLibrary("com.amazonaws:aws-java-sdk-sqs:1.12.583") diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/library/build.gradle.kts b/instrumentation/aws-sdk/aws-sdk-1.11/library/build.gradle.kts -index bfe844e413..a2cedc9fa2 100644 +index bfe844e413..dec4935b55 100644 --- a/instrumentation/aws-sdk/aws-sdk-1.11/library/build.gradle.kts +++ b/instrumentation/aws-sdk/aws-sdk-1.11/library/build.gradle.kts @@ -17,6 +17,14 @@ dependencies { @@ -1803,7 +101,7 @@ index bfe844e413..a2cedc9fa2 100644 testLibrary("com.amazonaws:aws-java-sdk-sns:1.11.106") + testLibrary("com.amazonaws:aws-java-sdk-sqs:1.11.106") + testLibrary("com.amazonaws:aws-java-sdk-secretsmanager:1.11.309") -+ // testLibrary("com.amazonaws:aws-java-sdk-stepfunctions:1.11.230") ++ testLibrary("com.amazonaws:aws-java-sdk-stepfunctions:1.11.230") + testLibrary("com.amazonaws:aws-java-sdk-lambda:1.11.678") + testLibrary("com.amazonaws:aws-java-sdk-bedrock:1.12.744") + testLibrary("com.amazonaws:aws-java-sdk-bedrockagent:1.12.744") @@ -1952,10 +250,10 @@ index 0000000000..e890cb3c0f + } +} diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AwsExperimentalAttributes.java b/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AwsExperimentalAttributes.java -index 3e8fddec5c..70e8eeae7f 100644 +index 3e8fddec5c..8f86a67b39 100644 --- a/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AwsExperimentalAttributes.java +++ b/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AwsExperimentalAttributes.java -@@ -18,6 +18,32 @@ final class AwsExperimentalAttributes { +@@ -18,6 +18,49 @@ final class AwsExperimentalAttributes { static final AttributeKey AWS_STREAM_NAME = stringKey("aws.stream.name"); static final AttributeKey AWS_TABLE_NAME = stringKey("aws.table.name"); static final AttributeKey AWS_REQUEST_ID = stringKey("aws.requestId"); @@ -1971,6 +269,23 @@ index 3e8fddec5c..70e8eeae7f 100644 + stringKey("gen_ai.request.model"); + static final AttributeKey AWS_BEDROCK_SYSTEM = stringKey("gen_ai.system"); + ++ static final AttributeKey GEN_AI_REQUEST_MAX_TOKENS = ++ stringKey("gen_ai.request.max_tokens"); ++ ++ static final AttributeKey GEN_AI_REQUEST_TEMPERATURE = ++ stringKey("gen_ai.request.temperature"); ++ ++ static final AttributeKey GEN_AI_REQUEST_TOP_P = stringKey("gen_ai.request.top_p"); ++ ++ static final AttributeKey GEN_AI_RESPONSE_FINISH_REASONS = ++ stringKey("gen_ai.response.finish_reasons"); ++ ++ static final AttributeKey GEN_AI_USAGE_INPUT_TOKENS = ++ stringKey("gen_ai.usage.input_tokens"); ++ ++ static final AttributeKey GEN_AI_USAGE_OUTPUT_TOKENS = ++ stringKey("gen_ai.usage.output_tokens"); ++ + static final AttributeKey AWS_STATE_MACHINE_ARN = + stringKey("aws.stepfunctions.state_machine.arn"); + @@ -1989,10 +304,10 @@ index 3e8fddec5c..70e8eeae7f 100644 private AwsExperimentalAttributes() {} } diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AwsSdkExperimentalAttributesExtractor.java b/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AwsSdkExperimentalAttributesExtractor.java -index 245f09a5d8..157fd891c3 100644 +index 245f09a5d8..aef7936980 100644 --- a/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AwsSdkExperimentalAttributesExtractor.java +++ b/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AwsSdkExperimentalAttributesExtractor.java -@@ -6,11 +6,23 @@ +@@ -6,13 +6,31 @@ package io.opentelemetry.instrumentation.awssdk.v1_11; import static io.opentelemetry.instrumentation.awssdk.v1_11.AwsExperimentalAttributes.AWS_AGENT; @@ -2015,8 +330,16 @@ index 245f09a5d8..157fd891c3 100644 +import static io.opentelemetry.instrumentation.awssdk.v1_11.AwsExperimentalAttributes.AWS_STEP_FUNCTIONS_ACTIVITY_ARN; import static io.opentelemetry.instrumentation.awssdk.v1_11.AwsExperimentalAttributes.AWS_STREAM_NAME; import static io.opentelemetry.instrumentation.awssdk.v1_11.AwsExperimentalAttributes.AWS_TABLE_NAME; ++import static io.opentelemetry.instrumentation.awssdk.v1_11.AwsExperimentalAttributes.GEN_AI_REQUEST_MAX_TOKENS; ++import static io.opentelemetry.instrumentation.awssdk.v1_11.AwsExperimentalAttributes.GEN_AI_REQUEST_TEMPERATURE; ++import static io.opentelemetry.instrumentation.awssdk.v1_11.AwsExperimentalAttributes.GEN_AI_REQUEST_TOP_P; ++import static io.opentelemetry.instrumentation.awssdk.v1_11.AwsExperimentalAttributes.GEN_AI_RESPONSE_FINISH_REASONS; ++import static io.opentelemetry.instrumentation.awssdk.v1_11.AwsExperimentalAttributes.GEN_AI_USAGE_INPUT_TOKENS; ++import static io.opentelemetry.instrumentation.awssdk.v1_11.AwsExperimentalAttributes.GEN_AI_USAGE_OUTPUT_TOKENS; -@@ -21,12 +33,17 @@ import io.opentelemetry.api.common.AttributeKey; + import com.amazonaws.AmazonWebServiceResponse; + import com.amazonaws.Request; +@@ -21,12 +39,17 @@ import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.AttributesBuilder; import io.opentelemetry.context.Context; import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; @@ -2034,7 +357,7 @@ index 245f09a5d8..157fd891c3 100644 @Override public void onStart(AttributesBuilder attributes, Context parentContext, Request request) { -@@ -34,21 +51,30 @@ class AwsSdkExperimentalAttributesExtractor +@@ -34,21 +57,30 @@ class AwsSdkExperimentalAttributesExtractor attributes.put(AWS_ENDPOINT, request.getEndpoint().toString()); Object originalRequest = request.getOriginalRequest(); @@ -2079,7 +402,7 @@ index 245f09a5d8..157fd891c3 100644 } } -@@ -59,12 +85,117 @@ class AwsSdkExperimentalAttributesExtractor +@@ -59,12 +91,136 @@ class AwsSdkExperimentalAttributesExtractor Request request, @Nullable Response response, @Nullable Throwable error) { @@ -2104,15 +427,15 @@ index 245f09a5d8..157fd891c3 100644 + if (requestId != null) { + attributes.put(AWS_REQUEST_ID, requestId); + } -+ } + } + // Get serviceName defined in the AWS Java SDK V1 Request class. + String serviceName = request.getServiceName(); + // Extract response attributes for Bedrock services + if (awsResp != null && isBedrockService(serviceName)) { + bedrockOnEnd(attributes, awsResp, serviceName); - } - } - } ++ } ++ } ++ } + + private static void bedrockOnStart( + AttributesBuilder attributes, @@ -2147,6 +470,14 @@ index 245f09a5d8..157fd891c3 100644 + Function getter = RequestAccess::getModelId; + String modelId = getter.apply(originalRequest); + attributes.put(AWS_BEDROCK_RUNTIME_MODEL_ID, modelId); ++ ++ setAttribute( ++ attributes, GEN_AI_REQUEST_MAX_TOKENS, originalRequest, RequestAccess::getMaxTokens); ++ setAttribute( ++ attributes, GEN_AI_REQUEST_TEMPERATURE, originalRequest, RequestAccess::getTemperature); ++ setAttribute(attributes, GEN_AI_REQUEST_TOP_P, originalRequest, RequestAccess::getTopP); ++ setAttribute( ++ attributes, GEN_AI_USAGE_INPUT_TOKENS, originalRequest, RequestAccess::getInputTokens); + break; + default: + break; @@ -2176,6 +507,17 @@ index 245f09a5d8..157fd891c3 100644 + setAttribute(attributes, AWS_AGENT_ID, awsResp, RequestAccess::getAgentId); + setAttribute(attributes, AWS_KNOWLEDGE_BASE_ID, awsResp, RequestAccess::getKnowledgeBaseId); + break; ++ case BEDROCK_RUNTIME_SERVICE: ++ if (!Objects.equals(awsResp.getClass().getSimpleName(), "InvokeModelResult")) { ++ break; ++ } ++ ++ setAttribute(attributes, GEN_AI_USAGE_INPUT_TOKENS, awsResp, RequestAccess::getInputTokens); ++ setAttribute( ++ attributes, GEN_AI_USAGE_OUTPUT_TOKENS, awsResp, RequestAccess::getOutputTokens); ++ setAttribute( ++ attributes, GEN_AI_RESPONSE_FINISH_REASONS, awsResp, RequestAccess::getFinishReasons); ++ break; + default: + break; + } @@ -2199,25 +541,242 @@ index 245f09a5d8..157fd891c3 100644 + String value = getter.apply(request); + if (value != null) { + attributes.put(key, value); -+ } -+ } + } + } } diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/RequestAccess.java b/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/RequestAccess.java -index bb2ae9266c..36e216047f 100644 +index bb2ae9266c..512d5345cc 100644 --- a/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/RequestAccess.java +++ b/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/RequestAccess.java -@@ -8,6 +8,7 @@ package io.opentelemetry.instrumentation.awssdk.v1_11; +@@ -5,9 +5,17 @@ + + package io.opentelemetry.instrumentation.awssdk.v1_11; + ++import com.fasterxml.jackson.databind.JsonNode; ++import com.fasterxml.jackson.databind.ObjectMapper; ++import java.io.IOException; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; +import java.lang.reflect.Method; ++import java.nio.ByteBuffer; ++import java.util.Arrays; ++import java.util.Objects; ++import java.util.stream.Stream; import javax.annotation.Nullable; final class RequestAccess { -@@ -20,36 +21,158 @@ final class RequestAccess { +@@ -20,36 +28,365 @@ final class RequestAccess { } }; ++ private static final ObjectMapper objectMapper = new ObjectMapper(); ++ ++ @Nullable ++ private static JsonNode parseTargetBody(ByteBuffer buffer) { ++ try { ++ byte[] bytes; ++ // Create duplicate to avoid mutating the original buffer position ++ ByteBuffer duplicate = buffer.duplicate(); ++ if (buffer.hasArray()) { ++ bytes = ++ Arrays.copyOfRange( ++ duplicate.array(), ++ duplicate.arrayOffset(), ++ duplicate.arrayOffset() + duplicate.remaining()); ++ } else { ++ bytes = new byte[buffer.remaining()]; ++ buffer.get(bytes); ++ } ++ return objectMapper.readTree(bytes); ++ } catch (IOException e) { ++ return null; ++ } ++ } ++ ++ @Nullable ++ private static JsonNode getJsonBody(Object target) { ++ if (target == null) { ++ return null; ++ } ++ ++ RequestAccess access = REQUEST_ACCESSORS.get(target.getClass()); ++ ByteBuffer bodyBuffer = invokeOrNullGeneric(access.getBody, target, ByteBuffer.class); ++ if (bodyBuffer == null) { ++ return null; ++ } ++ ++ return parseTargetBody(bodyBuffer); ++ } ++ ++ @Nullable ++ private static String findFirstMatchingPath(JsonNode jsonBody, String... paths) { ++ if (jsonBody == null) { ++ return null; ++ } ++ ++ return Stream.of(paths) ++ .map( ++ path -> { ++ JsonNode node = jsonBody.at(path); ++ if (node != null && !node.isMissingNode()) { ++ return node.asText(); ++ } ++ return null; ++ }) ++ .filter(Objects::nonNull) ++ .findFirst() ++ .orElse(null); ++ } ++ ++ @Nullable ++ private static String approximateTokenCount(JsonNode jsonBody, String... textPaths) { ++ if (jsonBody == null) { ++ return null; ++ } ++ ++ return Stream.of(textPaths) ++ .map( ++ path -> { ++ JsonNode node = jsonBody.at(path); ++ if (node != null && !node.isMissingNode()) { ++ int tokenEstimate = (int) Math.ceil(node.asText().length() / 6.0); ++ return Integer.toString(tokenEstimate); ++ } ++ return null; ++ }) ++ .filter(Objects::nonNull) ++ .findFirst() ++ .orElse(null); ++ } ++ ++ // Model -> Path Mapping: ++ // Amazon Titan -> "/textGenerationConfig/maxTokenCount" ++ // Anthropic Claude -> "/max_tokens" ++ // Cohere Command -> "/max_tokens" ++ // Cohere Command R -> "/max_tokens" ++ // AI21 Jamba -> "/max_tokens" ++ // Meta Llama -> "/max_gen_len" ++ // Mistral AI -> "/max_tokens" ++ @Nullable ++ static String getMaxTokens(Object target) { ++ return findFirstMatchingPath( ++ getJsonBody(target), "/textGenerationConfig/maxTokenCount", "/max_tokens", "/max_gen_len"); ++ } ++ ++ // Model -> Path Mapping: ++ // Amazon Titan -> "/textGenerationConfig/temperature" ++ // Anthropic Claude -> "/temperature" ++ // Cohere Command -> "/temperature" ++ // Cohere Command R -> "/temperature" ++ // AI21 Jamba -> "/temperature" ++ // Meta Llama -> "/temperature" ++ // Mistral AI -> "/temperature" ++ @Nullable ++ static String getTemperature(Object target) { ++ return findFirstMatchingPath( ++ getJsonBody(target), "/textGenerationConfig/temperature", "/temperature"); ++ } ++ ++ // Model -> Path Mapping: ++ // Amazon Titan -> "/textGenerationConfig/topP" ++ // Anthropic Claude -> "/top_p" ++ // Cohere Command -> "/p" ++ // Cohere Command R -> "/p" ++ // AI21 Jamba -> "/top_p" ++ // Meta Llama -> "/top_p" ++ // Mistral AI -> "/top_p" ++ @Nullable ++ static String getTopP(Object target) { ++ return findFirstMatchingPath(getJsonBody(target), "/textGenerationConfig/topP", "/top_p", "/p"); ++ } ++ ++ // Model -> Path Mapping: ++ // Amazon Titan -> "/inputTextTokenCount" ++ // Anthropic Claude -> "/usage/input_tokens" ++ // Cohere Command -> "/prompt" ++ // Cohere Command R -> "/message" ++ // AI21 Jamba -> "/usage/prompt_tokens" ++ // Meta Llama -> "/prompt_token_count" ++ // Mistral AI -> "/prompt" ++ @Nullable ++ static String getInputTokens(Object target) { ++ JsonNode jsonBody = getJsonBody(target); ++ if (jsonBody == null) { ++ return null; ++ } ++ ++ // Try direct tokens counts first ++ String directCount = ++ findFirstMatchingPath( ++ jsonBody, ++ "/inputTextTokenCount", ++ "/usage/input_tokens", ++ "/usage/prompt_tokens", ++ "/prompt_token_count"); ++ ++ if (directCount != null) { ++ return directCount; ++ } ++ ++ // Fall back to token approximation ++ return approximateTokenCount(jsonBody, "/prompt", "/message"); ++ } ++ ++ // Model -> Path Mapping: ++ // Amazon Titan -> "/results/0/tokenCount" ++ // Anthropic Claude -> "/usage/output_tokens" ++ // Cohere Command -> "/generations/0/text" ++ // Cohere Command R -> "/text" ++ // AI21 Jamba -> "/usage/completion_tokens" ++ // Meta Llama -> "/generation_token_count" ++ // Mistral AI -> "/outputs/0/text" ++ @Nullable ++ static String getOutputTokens(Object target) { ++ JsonNode jsonBody = getJsonBody(target); ++ if (jsonBody == null) { ++ return null; ++ } ++ ++ // Try direct token counts first ++ String directCount = ++ findFirstMatchingPath( ++ jsonBody, ++ "/results/0/tokenCount", ++ "/usage/output_tokens", ++ "/usage/completion_tokens", ++ "/generation_token_count"); ++ ++ if (directCount != null) { ++ return directCount; ++ } ++ ++ return approximateTokenCount(jsonBody, "/outputs/0/text", "/text"); ++ } ++ ++ // Model -> Path Mapping: ++ // Amazon Titan -> "/results/0/completionReason" ++ // Anthropic Claude -> "/stop_reason" ++ // Cohere Command -> "/generations/0/finish_reason" ++ // Cohere Command R -> "/finish_reason" ++ // AI21 Jamba -> "/choices/0/finish_reason" ++ // Meta Llama -> "/stop_reason" ++ // Mistral AI -> "/outputs/0/stop_reason" ++ @Nullable ++ static String getFinishReasons(Object target) { ++ String finishReason = ++ findFirstMatchingPath( ++ getJsonBody(target), ++ "/results/0/completionReason", ++ "/stop_reason", ++ "/generations/0/finish_reason", ++ "/choices/0/finish_reason", ++ "/outputs/0/stop_reason", ++ "/finish_reason"); ++ ++ return finishReason != null ? "[" + finishReason + "]" : null; ++ } ++ + @Nullable + static String getLambdaName(Object request) { + if (request == null) { @@ -2373,7 +932,25 @@ index bb2ae9266c..36e216047f 100644 @Nullable private static String invokeOrNull(@Nullable MethodHandle method, Object obj) { if (method == null) { -@@ -67,6 +190,17 @@ final class RequestAccess { +@@ -62,27 +399,82 @@ final class RequestAccess { + } + } + ++ @Nullable ++ private static T invokeOrNullGeneric( ++ @Nullable MethodHandle method, Object obj, Class returnType) { ++ if (method == null) { ++ return null; ++ } ++ try { ++ return returnType.cast(method.invoke(obj)); ++ } catch (Throwable e) { ++ return null; ++ } ++ } ++ + @Nullable private final MethodHandle getBucketName; + @Nullable private final MethodHandle getQueueUrl; @Nullable private final MethodHandle getQueueName; @Nullable private final MethodHandle getStreamName; @Nullable private final MethodHandle getTableName; @@ -2382,6 +959,7 @@ index bb2ae9266c..36e216047f 100644 + @Nullable private final MethodHandle getDataSourceId; + @Nullable private final MethodHandle getGuardrailId; + @Nullable private final MethodHandle getModelId; ++ @Nullable private final MethodHandle getBody; + @Nullable private final MethodHandle getStateMachineArn; + @Nullable private final MethodHandle getStepFunctionsActivityArn; + @Nullable private final MethodHandle getSnsTopicArn; @@ -2390,26 +968,39 @@ index bb2ae9266c..36e216047f 100644 + @Nullable private final MethodHandle getLambdaResourceId; private RequestAccess(Class clz) { - getBucketName = findAccessorOrNull(clz, "getBucketName"); -@@ -74,6 +208,17 @@ final class RequestAccess { - getQueueName = findAccessorOrNull(clz, "getQueueName"); - getStreamName = findAccessorOrNull(clz, "getStreamName"); - getTableName = findAccessorOrNull(clz, "getTableName"); -+ getAgentId = findAccessorOrNull(clz, "getAgentId"); -+ getKnowledgeBaseId = findAccessorOrNull(clz, "getKnowledgeBaseId"); -+ getDataSourceId = findAccessorOrNull(clz, "getDataSourceId"); -+ getGuardrailId = findAccessorOrNull(clz, "getGuardrailId"); -+ getModelId = findAccessorOrNull(clz, "getModelId"); -+ getStateMachineArn = findAccessorOrNull(clz, "getStateMachineArn"); -+ getStepFunctionsActivityArn = findAccessorOrNull(clz, "getActivityArn"); -+ getSnsTopicArn = findAccessorOrNull(clz, "getTopicArn"); -+ getSecretArn = findAccessorOrNull(clz, "getARN"); -+ getLambdaName = findAccessorOrNull(clz, "getFunctionName"); -+ getLambdaResourceId = findAccessorOrNull(clz, "getUUID"); +- getBucketName = findAccessorOrNull(clz, "getBucketName"); +- getQueueUrl = findAccessorOrNull(clz, "getQueueUrl"); +- getQueueName = findAccessorOrNull(clz, "getQueueName"); +- getStreamName = findAccessorOrNull(clz, "getStreamName"); +- getTableName = findAccessorOrNull(clz, "getTableName"); ++ getBucketName = findAccessorOrNull(clz, "getBucketName", String.class); ++ getQueueUrl = findAccessorOrNull(clz, "getQueueUrl", String.class); ++ getQueueName = findAccessorOrNull(clz, "getQueueName", String.class); ++ getStreamName = findAccessorOrNull(clz, "getStreamName", String.class); ++ getTableName = findAccessorOrNull(clz, "getTableName", String.class); ++ getAgentId = findAccessorOrNull(clz, "getAgentId", String.class); ++ getKnowledgeBaseId = findAccessorOrNull(clz, "getKnowledgeBaseId", String.class); ++ getDataSourceId = findAccessorOrNull(clz, "getDataSourceId", String.class); ++ getGuardrailId = findAccessorOrNull(clz, "getGuardrailId", String.class); ++ getModelId = findAccessorOrNull(clz, "getModelId", String.class); ++ getBody = findAccessorOrNull(clz, "getBody", ByteBuffer.class); ++ getStateMachineArn = findAccessorOrNull(clz, "getStateMachineArn", String.class); ++ getStepFunctionsActivityArn = findAccessorOrNull(clz, "getActivityArn", String.class); ++ getSnsTopicArn = findAccessorOrNull(clz, "getTopicArn", String.class); ++ getSecretArn = findAccessorOrNull(clz, "getARN", String.class); ++ getLambdaName = findAccessorOrNull(clz, "getFunctionName", String.class); ++ getLambdaResourceId = findAccessorOrNull(clz, "getUUID", String.class); } @Nullable -@@ -85,4 +230,21 @@ final class RequestAccess { +- private static MethodHandle findAccessorOrNull(Class clz, String methodName) { ++ private static MethodHandle findAccessorOrNull( ++ Class clz, String methodName, Class returnType) { + try { + return MethodHandles.publicLookup() +- .findVirtual(clz, methodName, MethodType.methodType(String.class)); ++ .findVirtual(clz, methodName, MethodType.methodType(returnType)); + } catch (Throwable t) { return null; } } @@ -2432,7 +1023,7 @@ index bb2ae9266c..36e216047f 100644 + } } diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/testing/build.gradle.kts b/instrumentation/aws-sdk/aws-sdk-1.11/testing/build.gradle.kts -index 548631e9f1..b31b01b87b 100644 +index 548631e9f1..51483839a7 100644 --- a/instrumentation/aws-sdk/aws-sdk-1.11/testing/build.gradle.kts +++ b/instrumentation/aws-sdk/aws-sdk-1.11/testing/build.gradle.kts @@ -14,6 +14,14 @@ dependencies { @@ -2440,7 +1031,7 @@ index 548631e9f1..b31b01b87b 100644 compileOnly("com.amazonaws:aws-java-sdk-sns:1.11.106") compileOnly("com.amazonaws:aws-java-sdk-sqs:1.11.106") + compileOnly("com.amazonaws:aws-java-sdk-secretsmanager:1.11.309") -+ // compileOnly("com.amazonaws:aws-java-sdk-stepfunctions:1.11.230") ++ compileOnly("com.amazonaws:aws-java-sdk-stepfunctions:1.11.230") + compileOnly("com.amazonaws:aws-java-sdk-lambda:1.11.678") + + compileOnly("com.amazonaws:aws-java-sdk-bedrock:1.12.744") @@ -2451,7 +1042,7 @@ index 548631e9f1..b31b01b87b 100644 // needed for SQS - using emq directly as localstack references emq v0.15.7 ie WITHOUT AWS trace header propagation implementation("org.elasticmq:elasticmq-rest-sqs_2.12:1.0.0") diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/testing/src/main/groovy/io/opentelemetry/instrumentation/awssdk/v1_11/AbstractAws1ClientTest.groovy b/instrumentation/aws-sdk/aws-sdk-1.11/testing/src/main/groovy/io/opentelemetry/instrumentation/awssdk/v1_11/AbstractAws1ClientTest.groovy -index 95e6ed8985..25ff9f5a70 100644 +index 95e6ed8985..990fc177bc 100644 --- a/instrumentation/aws-sdk/aws-sdk-1.11/testing/src/main/groovy/io/opentelemetry/instrumentation/awssdk/v1_11/AbstractAws1ClientTest.groovy +++ b/instrumentation/aws-sdk/aws-sdk-1.11/testing/src/main/groovy/io/opentelemetry/instrumentation/awssdk/v1_11/AbstractAws1ClientTest.groovy @@ -27,6 +27,24 @@ import com.amazonaws.services.rds.AmazonRDSClientBuilder @@ -2466,9 +1057,9 @@ index 95e6ed8985..25ff9f5a70 100644 +import com.amazonaws.services.bedrock.model.GetGuardrailRequest +import com.amazonaws.services.bedrockruntime.AmazonBedrockRuntimeClientBuilder +import com.amazonaws.services.bedrockruntime.model.InvokeModelRequest -+//import com.amazonaws.services.stepfunctions.AWSStepFunctionsClientBuilder -+//import com.amazonaws.services.stepfunctions.model.DescribeStateMachineRequest -+//import com.amazonaws.services.stepfunctions.model.DescribeActivityRequest ++import com.amazonaws.services.stepfunctions.AWSStepFunctionsClientBuilder ++import com.amazonaws.services.stepfunctions.model.DescribeStateMachineRequest ++import com.amazonaws.services.stepfunctions.model.DescribeActivityRequest +import com.amazonaws.services.sns.AmazonSNSClientBuilder +import com.amazonaws.services.sns.model.PublishRequest +import com.amazonaws.services.secretsmanager.AWSSecretsManagerClientBuilder @@ -2487,18 +1078,7 @@ index 95e6ed8985..25ff9f5a70 100644 import static io.opentelemetry.api.trace.SpanKind.CLIENT import static io.opentelemetry.api.trace.SpanKind.PRODUCER -@@ -130,8 +149,8 @@ abstract class AbstractAws1ClientTest extends InstrumentationSpecification { - - where: - service | operation | method | path | clientBuilder | call | additionalAttributes | body -- "S3" | "CreateBucket" | "PUT" | "/testbucket/" | AmazonS3ClientBuilder.standard().withPathStyleAccessEnabled(true) | { c -> c.createBucket("testbucket") } | ["aws.bucket.name": "testbucket"] | "" -- "S3" | "GetObject" | "GET" | "/someBucket/someKey" | AmazonS3ClientBuilder.standard().withPathStyleAccessEnabled(true) | { c -> c.getObject("someBucket", "someKey") } | ["aws.bucket.name": "someBucket"] | "" -+ //"S3" | "CreateBucket" | "PUT" | "/testbucket/" | AmazonS3ClientBuilder.standard().withPathStyleAccessEnabled(true) | { c -> c.createBucket("testbucket") } | ["aws.bucket.name": "testbucket"] | "" -+ //"S3" | "GetObject" | "GET" | "/someBucket/someKey" | AmazonS3ClientBuilder.standard().withPathStyleAccessEnabled(true) | { c -> c.getObject("someBucket", "someKey") } | ["aws.bucket.name": "someBucket"] | "" - "DynamoDBv2" | "CreateTable" | "POST" | "/" | AmazonDynamoDBClientBuilder.standard() | { c -> c.createTable(new CreateTableRequest("sometable", null)) } | ["aws.table.name": "sometable"] | "" - "Kinesis" | "DeleteStream" | "POST" | "/" | AmazonKinesisClientBuilder.standard() | { c -> c.deleteStream(new DeleteStreamRequest().withStreamName("somestream")) } | ["aws.stream.name": "somestream"] | "" - // Some users may implicitly subclass the request object to mimic a fluent style -@@ -156,6 +175,88 @@ abstract class AbstractAws1ClientTest extends InstrumentationSpecification { +@@ -156,6 +175,296 @@ abstract class AbstractAws1ClientTest extends InstrumentationSpecification { """ @@ -2534,25 +1114,233 @@ index 95e6ed8985..25ff9f5a70 100644 + "AWSBedrockAgent" | "GetAgent" | "GET" | "/" | AWSBedrockAgentClientBuilder.standard() | { c -> c.getAgent(new GetAgentRequest().withAgentId("agentId")) } | ["aws.bedrock.agent.id": "agentId"] | "" + "AWSBedrockAgent" | "GetKnowledgeBase" | "GET" | "/" | AWSBedrockAgentClientBuilder.standard() | { c -> c.getKnowledgeBase(new GetKnowledgeBaseRequest().withKnowledgeBaseId("knowledgeBaseId")) } | ["aws.bedrock.knowledge_base.id": "knowledgeBaseId"] | "" + "AWSBedrockAgent" | "GetDataSource" | "GET" | "/" | AWSBedrockAgentClientBuilder.standard() | { c -> c.getDataSource(new GetDataSourceRequest().withDataSourceId("datasourceId").withKnowledgeBaseId("knowledgeBaseId")) } | ["aws.bedrock.data_source.id": "datasourceId"] | "" -+ "BedrockRuntime" | "InvokeModel" | "POST" | "/" | AmazonBedrockRuntimeClientBuilder.standard() | -+ { c -> c.invokeModel( -+ new InvokeModelRequest().withModelId("anthropic.claude-v2").withBody(StandardCharsets.UTF_8.encode( -+ "{\"prompt\":\"Hello, world!\",\"temperature\":0.7,\"top_p\":0.9,\"max_tokens_to_sample\":100}\n" -+ ))) } | ["gen_ai.request.model": "anthropic.claude-v2", "gen_ai.system": "aws_bedrock"] | """ ++ "BedrockRuntime" | "InvokeModel" | "POST" | "/" | ++ AmazonBedrockRuntimeClientBuilder.standard() | ++ { c -> ++ c.invokeModel( ++ new InvokeModelRequest() ++ .withModelId("ai21.jamba-1-5-mini-v1:0") ++ .withBody(StandardCharsets.UTF_8.encode(''' ++ { ++ "messages": [{ ++ "role": "user", ++ "message": "Which LLM are you?" ++ }], ++ "max_tokens": 1000, ++ "top_p": 0.8, ++ "temperature": 0.7 ++ } ++ ''')) ++ ) ++ } | ++ [ ++ "gen_ai.request.model": "ai21.jamba-1-5-mini-v1:0", ++ "gen_ai.system": "aws_bedrock", ++ "gen_ai.request.max_tokens": "1000", ++ "gen_ai.request.temperature": "0.7", ++ "gen_ai.request.top_p": "0.8", ++ "gen_ai.response.finish_reasons": "[stop]", ++ "gen_ai.usage.input_tokens": "5", ++ "gen_ai.usage.output_tokens": "42" ++ ] | ++ ''' ++ { ++ "choices": [{ ++ "finish_reason": "stop" ++ }], ++ "usage": { ++ "prompt_tokens": 5, ++ "completion_tokens": 42 ++ } ++ } ++ ''' ++ "BedrockRuntime" | "InvokeModel" | "POST" | "/" | ++ AmazonBedrockRuntimeClientBuilder.standard() | ++ { c -> ++ c.invokeModel( ++ new InvokeModelRequest() ++ .withModelId("amazon.titan-text-premier-v1:0") ++ .withBody(StandardCharsets.UTF_8.encode(''' ++ { ++ "inputText": "Hello, world!", ++ "textGenerationConfig": { ++ "temperature": 0.7, ++ "topP": 0.9, ++ "maxTokenCount": 100, ++ "stopSequences": ["END"] ++ } ++ } ++ ''')) ++ ) ++ } | ++ [ ++ "gen_ai.request.model": "amazon.titan-text-premier-v1:0", ++ "gen_ai.system": "aws_bedrock", ++ "gen_ai.request.max_tokens": "100", ++ "gen_ai.request.temperature": "0.7", ++ "gen_ai.request.top_p": "0.9", ++ "gen_ai.response.finish_reasons": "[stop]", ++ "gen_ai.usage.input_tokens": "5", ++ "gen_ai.usage.output_tokens": "42" ++ ] | ++ ''' ++ { ++ "inputTextTokenCount": 5, ++ "results": [ + { -+ "completion": " Here is a simple explanation of black ", -+ "stop_reason": "length", -+ "stop": "holes" ++ "tokenCount": 42, ++ "outputText": "Hi! I'm Titan, an AI assistant. How can I help you today?", ++ "completionReason": "stop" + } -+ """ -+ //"AWSStepFunctions" | "DescribeStateMachine" | "POST" | "/" | AWSStepFunctionsClientBuilder.standard() -+ //| { c -> c.describeStateMachine(new DescribeStateMachineRequest().withStateMachineArn("stateMachineArn")) } -+ //| ["aws.stepfunctions.state_machine.arn": "stateMachineArn"] -+ //| "" -+ //"AWSStepFunctions" | "DescribeActivity" | "POST" | "/" | AWSStepFunctionsClientBuilder.standard() -+ //| { c -> c.describeActivity(new DescribeActivityRequest().withActivityArn("activityArn")) } -+ //| ["aws.stepfunctions.activity.arn": "activityArn"] -+ //| "" ++ ] ++ } ++ ''' ++ "BedrockRuntime" | "InvokeModel" | "POST" | "/" | ++ AmazonBedrockRuntimeClientBuilder.standard() | ++ { c -> ++ c.invokeModel( ++ new InvokeModelRequest() ++ .withModelId("anthropic.claude-3-5-sonnet-20241022-v2:0") ++ .withBody(StandardCharsets.UTF_8.encode(''' ++ { ++ "anthropic_version": "bedrock-2023-05-31", ++ "messages": [{ ++ "role": "user", ++ "content": "Hello, world" ++ }], ++ "max_tokens": 100, ++ "temperature": 0.7, ++ "top_p": 0.9 ++ } ++ ''')) ++ ) ++ } | ++ [ ++ "gen_ai.request.model": "anthropic.claude-3-5-sonnet-20241022-v2:0", ++ "gen_ai.system": "aws_bedrock", ++ "gen_ai.request.max_tokens": "100", ++ "gen_ai.request.temperature": "0.7", ++ "gen_ai.request.top_p": "0.9", ++ "gen_ai.response.finish_reasons": "[end_turn]", ++ "gen_ai.usage.input_tokens": "2095", ++ "gen_ai.usage.output_tokens": "503" ++ ] | ++ ''' ++ { ++ "stop_reason": "end_turn", ++ "usage": { ++ "input_tokens": 2095, ++ "output_tokens": 503 ++ } ++ } ++ ''' ++ "BedrockRuntime" | "InvokeModel" | "POST" | "/" | ++ AmazonBedrockRuntimeClientBuilder.standard() | ++ { c -> ++ c.invokeModel( ++ new InvokeModelRequest() ++ .withModelId("meta.llama3-70b-instruct-v1:0") ++ .withBody(StandardCharsets.UTF_8.encode(''' ++ { ++ "prompt": "<|begin_of_text|><|start_header_id|>user<|end_header_id|>\\\\nDescribe the purpose of a 'hello world' program in one line. <|eot_id|>\\\\n<|start_header_id|>assistant<|end_header_id|>\\\\n", ++ "max_gen_len": 128, ++ "temperature": 0.1, ++ "top_p": 0.9 ++ } ++ ''')) ++ ) ++ } | ++ [ ++ "gen_ai.request.model": "meta.llama3-70b-instruct-v1:0", ++ "gen_ai.system": "aws_bedrock", ++ "gen_ai.request.max_tokens": "128", ++ "gen_ai.request.temperature": "0.1", ++ "gen_ai.request.top_p": "0.9", ++ "gen_ai.response.finish_reasons": "[stop]", ++ "gen_ai.usage.input_tokens": "2095", ++ "gen_ai.usage.output_tokens": "503" ++ ] | ++ ''' ++ { ++ "prompt_token_count": 2095, ++ "generation_token_count": 503, ++ "stop_reason": "stop" ++ } ++ ''' ++ "BedrockRuntime" | "InvokeModel" | "POST" | "/" | ++ AmazonBedrockRuntimeClientBuilder.standard() | ++ { c -> ++ c.invokeModel( ++ new InvokeModelRequest() ++ .withModelId("cohere.command-r-v1:0") ++ .withBody(StandardCharsets.UTF_8.encode(''' ++ { ++ "message": "Convince me to write a LISP interpreter in one line.", ++ "temperature": 0.8, ++ "max_tokens": 4096, ++ "p": 0.45 ++ } ++ ''')) ++ ) ++ } | ++ [ ++ "gen_ai.request.model": "cohere.command-r-v1:0", ++ "gen_ai.system": "aws_bedrock", ++ "gen_ai.request.max_tokens": "4096", ++ "gen_ai.request.temperature": "0.8", ++ "gen_ai.request.top_p": "0.45", ++ "gen_ai.response.finish_reasons": "[COMPLETE]", ++ "gen_ai.usage.input_tokens": "9", ++ "gen_ai.usage.output_tokens": "2" ++ ] | ++ ''' ++ { ++ "text": "test-output", ++ "finish_reason": "COMPLETE" ++ } ++ ''' ++ "BedrockRuntime" | "InvokeModel" | "POST" | "/" | ++ AmazonBedrockRuntimeClientBuilder.standard() | ++ { c -> ++ c.invokeModel( ++ new InvokeModelRequest() ++ .withModelId("mistral.mistral-large-2402-v1:0") ++ .withBody(StandardCharsets.UTF_8.encode(''' ++ { ++ "prompt": "[INST] Describe the difference between a compiler and interpreter in one line. [/INST]\\\\n", ++ "max_tokens": 4096, ++ "temperature": 0.75, ++ "top_p": 0.25 ++ } ++ ''')) ++ ) ++ } | ++ [ ++ "gen_ai.request.model": "mistral.mistral-large-2402-v1:0", ++ "gen_ai.system": "aws_bedrock", ++ "gen_ai.request.max_tokens": "4096", ++ "gen_ai.request.temperature": "0.75", ++ "gen_ai.request.top_p": "0.25", ++ "gen_ai.response.finish_reasons": "[stop]", ++ "gen_ai.usage.input_tokens": "16", ++ "gen_ai.usage.output_tokens": "2" ++ ] | ++ ''' ++ { ++ "outputs": [{ ++ "text": "test-output", ++ "stop_reason": "stop" ++ }] ++ } ++ ''' ++ "AWSStepFunctions" | "DescribeStateMachine" | "POST" | "/" | AWSStepFunctionsClientBuilder.standard() ++ | { c -> c.describeStateMachine(new DescribeStateMachineRequest().withStateMachineArn("stateMachineArn")) } ++ | ["aws.stepfunctions.state_machine.arn": "stateMachineArn"] ++ | "" ++ "AWSStepFunctions" | "DescribeActivity" | "POST" | "/" | AWSStepFunctionsClientBuilder.standard() ++ | { c -> c.describeActivity(new DescribeActivityRequest().withActivityArn("activityArn")) } ++ | ["aws.stepfunctions.activity.arn": "activityArn"] ++ | "" + "SNS" | "Publish" | "POST" | "/" | AmazonSNSClientBuilder.standard() + | { c -> c.publish(new PublishRequest().withMessage("message").withTopicArn("topicArn")) } + | ["aws.sns.topic.arn": "topicArn"] @@ -2631,10 +1419,10 @@ index 081d542e76..4f71a06a57 100644 latestDepTestLibrary("software.amazon.awssdk:sqs:2.21.17") diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AwsExperimentalAttributes.java b/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AwsExperimentalAttributes.java new file mode 100644 -index 0000000000..e1cb180d75 +index 0000000000..9e9f9cf59f --- /dev/null +++ b/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AwsExperimentalAttributes.java -@@ -0,0 +1,47 @@ +@@ -0,0 +1,73 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 @@ -2663,6 +1451,23 @@ index 0000000000..e1cb180d75 + static final AttributeKey GEN_AI_MODEL = stringKey("gen_ai.request.model"); + static final AttributeKey GEN_AI_SYSTEM = stringKey("gen_ai.system"); + ++ static final AttributeKey GEN_AI_REQUEST_MAX_TOKENS = ++ stringKey("gen_ai.request.max_tokens"); ++ ++ static final AttributeKey GEN_AI_REQUEST_TEMPERATURE = ++ stringKey("gen_ai.request.temperature"); ++ ++ static final AttributeKey GEN_AI_REQUEST_TOP_P = stringKey("gen_ai.request.top_p"); ++ ++ static final AttributeKey GEN_AI_RESPONSE_FINISH_REASONS = ++ stringKey("gen_ai.response.finish_reasons"); ++ ++ static final AttributeKey GEN_AI_USAGE_INPUT_TOKENS = ++ stringKey("gen_ai.usage.input_tokens"); ++ ++ static final AttributeKey GEN_AI_USAGE_OUTPUT_TOKENS = ++ stringKey("gen_ai.usage.output_tokens"); ++ + static final AttributeKey AWS_STATE_MACHINE_ARN = + stringKey("aws.stepfunctions.state_machine.arn"); + @@ -2680,6 +1485,15 @@ index 0000000000..e1cb180d75 + static final AttributeKey AWS_LAMBDA_RESOURCE_ID = + stringKey("aws.lambda.resource_mapping.id"); + ++ static boolean isGenAiAttribute(String attributeKey) { ++ return attributeKey.equals(GEN_AI_REQUEST_MAX_TOKENS.getKey()) ++ || attributeKey.equals(GEN_AI_REQUEST_TEMPERATURE.getKey()) ++ || attributeKey.equals(GEN_AI_REQUEST_TOP_P.getKey()) ++ || attributeKey.equals(GEN_AI_RESPONSE_FINISH_REASONS.getKey()) ++ || attributeKey.equals(GEN_AI_USAGE_INPUT_TOKENS.getKey()) ++ || attributeKey.equals(GEN_AI_USAGE_OUTPUT_TOKENS.getKey()); ++ } ++ + private AwsExperimentalAttributes() {} +} diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AwsSdkRequest.java b/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AwsSdkRequest.java @@ -2758,10 +1572,10 @@ index 54253d0f7b..5326400f7e 100644 BatchGetItem( DYNAMODB, diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AwsSdkRequestType.java b/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AwsSdkRequestType.java -index 9062f2aa17..9511cd6f05 100644 +index 9062f2aa17..4cd468e095 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AwsSdkRequestType.java +++ b/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AwsSdkRequestType.java -@@ -5,17 +5,62 @@ +@@ -5,17 +5,76 @@ package io.opentelemetry.instrumentation.awssdk.v2_2; @@ -2783,6 +1597,12 @@ index 9062f2aa17..9511cd6f05 100644 +import static io.opentelemetry.instrumentation.awssdk.v2_2.AwsExperimentalAttributes.AWS_STREAM_NAME; +import static io.opentelemetry.instrumentation.awssdk.v2_2.AwsExperimentalAttributes.AWS_TABLE_NAME; +import static io.opentelemetry.instrumentation.awssdk.v2_2.AwsExperimentalAttributes.GEN_AI_MODEL; ++import static io.opentelemetry.instrumentation.awssdk.v2_2.AwsExperimentalAttributes.GEN_AI_REQUEST_MAX_TOKENS; ++import static io.opentelemetry.instrumentation.awssdk.v2_2.AwsExperimentalAttributes.GEN_AI_REQUEST_TEMPERATURE; ++import static io.opentelemetry.instrumentation.awssdk.v2_2.AwsExperimentalAttributes.GEN_AI_REQUEST_TOP_P; ++import static io.opentelemetry.instrumentation.awssdk.v2_2.AwsExperimentalAttributes.GEN_AI_RESPONSE_FINISH_REASONS; ++import static io.opentelemetry.instrumentation.awssdk.v2_2.AwsExperimentalAttributes.GEN_AI_USAGE_INPUT_TOKENS; ++import static io.opentelemetry.instrumentation.awssdk.v2_2.AwsExperimentalAttributes.GEN_AI_USAGE_OUTPUT_TOKENS; import static io.opentelemetry.instrumentation.awssdk.v2_2.FieldMapping.request; +import static io.opentelemetry.instrumentation.awssdk.v2_2.FieldMapping.response; @@ -2815,7 +1635,15 @@ index 9062f2aa17..9511cd6f05 100644 + BEDROCKKNOWLEDGEBASEOPERATION( + request(AWS_KNOWLEDGE_BASE_ID.getKey(), "knowledgeBaseId"), + response(AWS_KNOWLEDGE_BASE_ID.getKey(), "knowledgeBaseId")), -+ BEDROCKRUNTIME(request(GEN_AI_MODEL.getKey(), "modelId")), ++ BEDROCKRUNTIME( ++ request(GEN_AI_MODEL.getKey(), "modelId"), ++ request(GEN_AI_REQUEST_MAX_TOKENS.getKey(), "body"), ++ request(GEN_AI_REQUEST_TEMPERATURE.getKey(), "body"), ++ request(GEN_AI_REQUEST_TOP_P.getKey(), "body"), ++ request(GEN_AI_USAGE_INPUT_TOKENS.getKey(), "body"), ++ response(GEN_AI_RESPONSE_FINISH_REASONS.getKey(), "body"), ++ response(GEN_AI_USAGE_INPUT_TOKENS.getKey(), "body"), ++ response(GEN_AI_USAGE_OUTPUT_TOKENS.getKey(), "body")), + STEPFUNCTION( + request(AWS_STATE_MACHINE_ARN.getKey(), "stateMachineArn"), + request(AWS_STEP_FUNCTIONS_ACTIVITY_ARN.getKey(), "activityArn")), @@ -2828,6 +1656,263 @@ index 9062f2aa17..9511cd6f05 100644 // Wrapping in unmodifiableMap @SuppressWarnings("ImmutableEnumChecker") +diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/FieldMapper.java b/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/FieldMapper.java +index 569d0eb5ae..8f2d463237 100644 +--- a/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/FieldMapper.java ++++ b/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/FieldMapper.java +@@ -65,8 +65,13 @@ class FieldMapper { + for (int i = 1; i < path.size() && target != null; i++) { + target = next(target, path.get(i)); + } ++ String value; + if (target != null) { +- String value = serializer.serialize(target); ++ if (AwsExperimentalAttributes.isGenAiAttribute(fieldMapping.getAttribute())) { ++ value = serializer.serialize(fieldMapping.getAttribute(), target); ++ } else { ++ value = serializer.serialize(target); ++ } + if (!StringUtils.isEmpty(value)) { + span.setAttribute(fieldMapping.getAttribute(), value); + } +diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/Serializer.java b/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/Serializer.java +index 979ecb08e8..fb846cae6a 100644 +--- a/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/Serializer.java ++++ b/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/Serializer.java +@@ -5,13 +5,19 @@ + + package io.opentelemetry.instrumentation.awssdk.v2_2; + ++import com.fasterxml.jackson.core.JsonProcessingException; ++import com.fasterxml.jackson.databind.JsonNode; ++import com.fasterxml.jackson.databind.ObjectMapper; + import java.io.IOException; + import java.io.InputStream; + import java.util.Collection; + import java.util.Map; ++import java.util.Objects; + import java.util.Optional; + import java.util.stream.Collectors; ++import java.util.stream.Stream; + import javax.annotation.Nullable; ++import software.amazon.awssdk.core.SdkBytes; + import software.amazon.awssdk.core.SdkPojo; + import software.amazon.awssdk.http.ContentStreamProvider; + import software.amazon.awssdk.http.SdkHttpFullRequest; +@@ -21,6 +27,8 @@ import software.amazon.awssdk.utils.StringUtils; + + class Serializer { + ++ private static final ObjectMapper objectMapper = new ObjectMapper(); ++ + @Nullable + String serialize(Object target) { + +@@ -41,6 +49,41 @@ class Serializer { + return target.toString(); + } + ++ @Nullable ++ String serialize(String attributeName, Object target) { ++ try { ++ JsonNode jsonBody; ++ if (target instanceof SdkBytes) { ++ String jsonString = ((SdkBytes) target).asUtf8String(); ++ jsonBody = objectMapper.readTree(jsonString); ++ } else { ++ if (target != null) { ++ return target.toString(); ++ } ++ return null; ++ } ++ ++ switch (attributeName) { ++ case "gen_ai.request.max_tokens": ++ return getMaxTokens(jsonBody); ++ case "gen_ai.request.temperature": ++ return getTemperature(jsonBody); ++ case "gen_ai.request.top_p": ++ return getTopP(jsonBody); ++ case "gen_ai.response.finish_reasons": ++ return getFinishReasons(jsonBody); ++ case "gen_ai.usage.input_tokens": ++ return getInputTokens(jsonBody); ++ case "gen_ai.usage.output_tokens": ++ return getOutputTokens(jsonBody); ++ default: ++ return null; ++ } ++ } catch (JsonProcessingException e) { ++ return null; ++ } ++ } ++ + @Nullable + private static String serialize(SdkPojo sdkPojo) { + ProtocolMarshaller marshaller = +@@ -65,4 +108,162 @@ class Serializer { + String serialized = collection.stream().map(this::serialize).collect(Collectors.joining(",")); + return (StringUtils.isEmpty(serialized) ? null : "[" + serialized + "]"); + } ++ ++ @Nullable ++ private static String findFirstMatchingPath(JsonNode jsonBody, String... paths) { ++ if (jsonBody == null) { ++ return null; ++ } ++ ++ return Stream.of(paths) ++ .map( ++ path -> { ++ JsonNode node = jsonBody.at(path); ++ if (node != null && !node.isMissingNode()) { ++ return node.asText(); ++ } ++ return null; ++ }) ++ .filter(Objects::nonNull) ++ .findFirst() ++ .orElse(null); ++ } ++ ++ @Nullable ++ private static String approximateTokenCount(JsonNode jsonBody, String... textPaths) { ++ if (jsonBody == null) { ++ return null; ++ } ++ ++ return Stream.of(textPaths) ++ .map( ++ path -> { ++ JsonNode node = jsonBody.at(path); ++ if (node != null && !node.isMissingNode()) { ++ int tokenEstimate = (int) Math.ceil(node.asText().length() / 6.0); ++ return Integer.toString(tokenEstimate); ++ } ++ return null; ++ }) ++ .filter(Objects::nonNull) ++ .findFirst() ++ .orElse(null); ++ } ++ ++ // Model -> Path Mapping: ++ // Amazon Titan -> "/textGenerationConfig/maxTokenCount" ++ // Anthropic Claude -> "/max_tokens" ++ // Cohere Command -> "/max_tokens" ++ // Cohere Command R -> "/max_tokens" ++ // AI21 Jamba -> "/max_tokens" ++ // Meta Llama -> "/max_gen_len" ++ // Mistral AI -> "/max_tokens" ++ @Nullable ++ private static String getMaxTokens(JsonNode jsonBody) { ++ return findFirstMatchingPath( ++ jsonBody, "/textGenerationConfig/maxTokenCount", "/max_tokens", "/max_gen_len"); ++ } ++ ++ // Model -> Path Mapping: ++ // Amazon Titan -> "/textGenerationConfig/temperature" ++ // Anthropic Claude -> "/temperature" ++ // Cohere Command -> "/temperature" ++ // Cohere Command R -> "/temperature" ++ // AI21 Jamba -> "/temperature" ++ // Meta Llama -> "/temperature" ++ // Mistral AI -> "/temperature" ++ @Nullable ++ private static String getTemperature(JsonNode jsonBody) { ++ return findFirstMatchingPath(jsonBody, "/textGenerationConfig/temperature", "/temperature"); ++ } ++ ++ // Model -> Path Mapping: ++ // Amazon Titan -> "/textGenerationConfig/topP" ++ // Anthropic Claude -> "/top_p" ++ // Cohere Command -> "/p" ++ // Cohere Command R -> "/p" ++ // AI21 Jamba -> "/top_p" ++ // Meta Llama -> "/top_p" ++ // Mistral AI -> "/top_p" ++ @Nullable ++ private static String getTopP(JsonNode jsonBody) { ++ return findFirstMatchingPath(jsonBody, "/textGenerationConfig/topP", "/top_p", "/p"); ++ } ++ ++ // Model -> Path Mapping: ++ // Amazon Titan -> "/results/0/completionReason" ++ // Anthropic Claude -> "/stop_reason" ++ // Cohere Command -> "/generations/0/finish_reason" ++ // Cohere Command R -> "/finish_reason" ++ // AI21 Jamba -> "/choices/0/finish_reason" ++ // Meta Llama -> "/stop_reason" ++ // Mistral AI -> "/outputs/0/stop_reason" ++ @Nullable ++ private static String getFinishReasons(JsonNode jsonBody) { ++ String finishReason = ++ findFirstMatchingPath( ++ jsonBody, ++ "/results/0/completionReason", ++ "/stop_reason", ++ "/generations/0/finish_reason", ++ "/choices/0/finish_reason", ++ "/outputs/0/stop_reason", ++ "/finish_reason"); ++ ++ return finishReason != null ? "[" + finishReason + "]" : null; ++ } ++ ++ // Model -> Path Mapping: ++ // Amazon Titan -> "/inputTextTokenCount" ++ // Anthropic Claude -> "/usage/input_tokens" ++ // Cohere Command -> "/prompt" ++ // Cohere Command R -> "/message" ++ // AI21 Jamba -> "/usage/prompt_tokens" ++ // Meta Llama -> "/prompt_token_count" ++ // Mistral AI -> "/prompt" ++ @Nullable ++ private static String getInputTokens(JsonNode jsonBody) { ++ // Try direct tokens counts first ++ String directCount = ++ findFirstMatchingPath( ++ jsonBody, ++ "/inputTextTokenCount", ++ "/usage/input_tokens", ++ "/usage/prompt_tokens", ++ "/prompt_token_count"); ++ ++ if (directCount != null) { ++ return directCount; ++ } ++ ++ // Fall back to token approximation ++ return approximateTokenCount(jsonBody, "/prompt", "/message"); ++ } ++ ++ // Model -> Path Mapping: ++ // Amazon Titan -> "/results/0/tokenCount" ++ // Anthropic Claude -> "/usage/output_tokens" ++ // Cohere Command -> "/generations/0/text" ++ // Cohere Command R -> "/text" ++ // AI21 Jamba -> "/usage/completion_tokens" ++ // Meta Llama -> "/generation_token_count" ++ // Mistral AI -> "/outputs/0/text" ++ @Nullable ++ private static String getOutputTokens(JsonNode jsonBody) { ++ // Try direct token counts first ++ String directCount = ++ findFirstMatchingPath( ++ jsonBody, ++ "/results/0/tokenCount", ++ "/usage/output_tokens", ++ "/usage/completion_tokens", ++ "/generation_token_count"); ++ ++ if (directCount != null) { ++ return directCount; ++ } ++ ++ // Fall back to token approximation ++ return approximateTokenCount(jsonBody, "/outputs/0/text", "/text"); ++ } + } diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/TracingExecutionInterceptor.java b/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/TracingExecutionInterceptor.java index f717b1efc4..352b02093e 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/TracingExecutionInterceptor.java @@ -2972,204 +2057,6 @@ index 53390c8d85..692cd005eb 100644 } def "send #operation async request with builder #builder.class.getName() mocked response"() { -diff --git a/instrumentation/vaadin-14.2/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/vaadin/AbstractVaadinTest.java b/instrumentation/vaadin-14.2/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/vaadin/AbstractVaadinTest.java -index 161a574119..08070aa332 100644 ---- a/instrumentation/vaadin-14.2/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/vaadin/AbstractVaadinTest.java -+++ b/instrumentation/vaadin-14.2/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/vaadin/AbstractVaadinTest.java -@@ -5,17 +5,17 @@ - - package io.opentelemetry.javaagent.instrumentation.vaadin; - --import static org.assertj.core.api.Assertions.assertThat; --import static org.awaitility.Awaitility.await; -+// import static org.assertj.core.api.Assertions.assertThat; -+// import static org.awaitility.Awaitility.await; - - import com.vaadin.flow.server.Version; - import com.vaadin.flow.spring.annotation.EnableVaadin; --import io.opentelemetry.api.trace.SpanKind; -+// import io.opentelemetry.api.trace.SpanKind; - import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; - import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpServerUsingTest; - import io.opentelemetry.instrumentation.testing.junit.http.HttpServerInstrumentationExtension; --import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions; --import io.opentelemetry.sdk.trace.data.SpanData; -+// import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions; -+// import io.opentelemetry.sdk.trace.data.SpanData; - import java.io.File; - import java.io.IOException; - import java.io.InputStream; -@@ -23,17 +23,17 @@ import java.net.URI; - import java.net.URISyntaxException; - import java.nio.file.Files; - import java.nio.file.Path; --import java.time.Duration; -+// import java.time.Duration; - import java.util.HashMap; --import java.util.List; -+// import java.util.List; - import java.util.Map; - import org.junit.jupiter.api.AfterAll; - import org.junit.jupiter.api.BeforeAll; --import org.junit.jupiter.api.Test; -+// import org.junit.jupiter.api.Test; - import org.junit.jupiter.api.extension.RegisterExtension; --import org.openqa.selenium.By; -+// import org.openqa.selenium.By; - import org.openqa.selenium.chrome.ChromeOptions; --import org.openqa.selenium.remote.RemoteWebDriver; -+// import org.openqa.selenium.remote.RemoteWebDriver; - import org.slf4j.Logger; - import org.slf4j.LoggerFactory; - import org.springframework.boot.SpringApplication; -@@ -126,77 +126,77 @@ public abstract class AbstractVaadinTest - return "/xyz"; - } - -- private void waitForStart(RemoteWebDriver driver) { -- // In development mode ui javascript is compiled when application starts -- // this involves downloading and installing npm and a bunch of packages -- // and running webpack. Wait until all of this is done before starting test. -- driver.manage().timeouts().implicitlyWait(Duration.ofMinutes(3)); -- driver.get(address.resolve("main").toString()); -- // wait for page to load -- driver.findElement(By.id("main.label")); -- // clear traces so test would start from clean state -- testing.clearData(); -- -- driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(30)); -- } -- -- private RemoteWebDriver getWebDriver() { -- return new RemoteWebDriver(browser.getSeleniumAddress(), new ChromeOptions(), false); -- } -+ // private void waitForStart(RemoteWebDriver driver) { -+ // // In development mode ui javascript is compiled when application starts -+ // // this involves downloading and installing npm and a bunch of packages -+ // // and running webpack. Wait until all of this is done before starting test. -+ // driver.manage().timeouts().implicitlyWait(Duration.ofMinutes(3)); -+ // driver.get(address.resolve("main").toString()); -+ // // wait for page to load -+ // driver.findElement(By.id("main.label")); -+ // // clear traces so test would start from clean state -+ // testing.clearData(); -+ // -+ // driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(30)); -+ // } -+ -+ // private RemoteWebDriver getWebDriver() { -+ // return new RemoteWebDriver(browser.getSeleniumAddress(), new ChromeOptions(), false); -+ // } - - abstract void assertFirstRequest(); - -- private void assertButtonClick() { -- await() -- .untilAsserted( -- () -> { -- List> traces = testing.waitForTraces(1); -- assertThat(traces.get(0)) -- .satisfies( -- spans -> { -- OpenTelemetryAssertions.assertThat(spans.get(0)) -- .hasName("POST " + getContextPath() + "/main") -- .hasNoParent() -- .hasKind(SpanKind.SERVER); -- OpenTelemetryAssertions.assertThat(spans.get(1)) -- .hasName("SpringVaadinServletService.handleRequest") -- .hasParent(spans.get(0)) -- .hasKind(SpanKind.INTERNAL); -- // we don't assert all the handler spans as these vary between -- // vaadin versions -- OpenTelemetryAssertions.assertThat(spans.get(spans.size() - 2)) -- .hasName("UidlRequestHandler.handleRequest") -- .hasParent(spans.get(1)) -- .hasKind(SpanKind.INTERNAL); -- OpenTelemetryAssertions.assertThat(spans.get(spans.size() - 1)) -- .hasName("EventRpcHandler.handle/click") -- .hasParent(spans.get(spans.size() - 2)) -- .hasKind(SpanKind.INTERNAL); -- }); -- }); -- } -- -- @Test -- public void navigateFromMainToOtherView() { -- RemoteWebDriver driver = getWebDriver(); -- waitForStart(driver); -- -- // fetch the test page -- driver.get(address.resolve("main").toString()); -- -- // wait for page to load -- assertThat(driver.findElement(By.id("main.label")).getText()).isEqualTo("Main view"); -- assertFirstRequest(); -- -- testing.clearData(); -- -- // click a button to trigger calling java code in MainView -- driver.findElement(By.id("main.button")).click(); -- -- // wait for page to load -- assertThat(driver.findElement(By.id("other.label")).getText()).isEqualTo("Other view"); -- assertButtonClick(); -- -- driver.close(); -- } -+ // private void assertButtonClick() { -+ // await() -+ // .untilAsserted( -+ // () -> { -+ // List> traces = testing.waitForTraces(1); -+ // assertThat(traces.get(0)) -+ // .satisfies( -+ // spans -> { -+ // OpenTelemetryAssertions.assertThat(spans.get(0)) -+ // .hasName("POST " + getContextPath() + "/main") -+ // .hasNoParent() -+ // .hasKind(SpanKind.SERVER); -+ // OpenTelemetryAssertions.assertThat(spans.get(1)) -+ // .hasName("SpringVaadinServletService.handleRequest") -+ // .hasParent(spans.get(0)) -+ // .hasKind(SpanKind.INTERNAL); -+ // // we don't assert all the handler spans as these vary between -+ // // vaadin versions -+ // OpenTelemetryAssertions.assertThat(spans.get(spans.size() - 2)) -+ // .hasName("UidlRequestHandler.handleRequest") -+ // .hasParent(spans.get(1)) -+ // .hasKind(SpanKind.INTERNAL); -+ // OpenTelemetryAssertions.assertThat(spans.get(spans.size() - 1)) -+ // .hasName("EventRpcHandler.handle/click") -+ // .hasParent(spans.get(spans.size() - 2)) -+ // .hasKind(SpanKind.INTERNAL); -+ // }); -+ // }); -+ // } -+ -+ // @Test -+ // public void navigateFromMainToOtherView() { -+ // RemoteWebDriver driver = getWebDriver(); -+ // waitForStart(driver); -+ // -+ // // fetch the test page -+ // driver.get(address.resolve("main").toString()); -+ // -+ // // wait for page to load -+ // assertThat(driver.findElement(By.id("main.label")).getText()).isEqualTo("Main view"); -+ // assertFirstRequest(); -+ // -+ // testing.clearData(); -+ // -+ // // click a button to trigger calling java code in MainView -+ // driver.findElement(By.id("main.button")).click(); -+ // -+ // // wait for page to load -+ // assertThat(driver.findElement(By.id("other.label")).getText()).isEqualTo("Other view"); -+ // assertButtonClick(); -+ // -+ // driver.close(); -+ // } - } diff --git a/version.gradle.kts b/version.gradle.kts index fdf57bdbea..c38a2e00f3 100644 --- a/version.gradle.kts diff --git a/appsignals-tests/contract-tests/src/test/java/software/amazon/opentelemetry/appsignals/test/awssdk/base/AwsSdkBaseTest.java b/appsignals-tests/contract-tests/src/test/java/software/amazon/opentelemetry/appsignals/test/awssdk/base/AwsSdkBaseTest.java index 99a1732834..0f8652fb70 100644 --- a/appsignals-tests/contract-tests/src/test/java/software/amazon/opentelemetry/appsignals/test/awssdk/base/AwsSdkBaseTest.java +++ b/appsignals-tests/contract-tests/src/test/java/software/amazon/opentelemetry/appsignals/test/awssdk/base/AwsSdkBaseTest.java @@ -1798,8 +1798,8 @@ protected void doTestBedrockAgentDataSourceId() { 0.0); } - protected void doTestBedrockRuntimeModelId() { - var response = appClient.get("/bedrockruntime/invokeModel").aggregate().join(); + protected void doTestBedrockRuntimeAi21Jamba() { + var response = appClient.get("/bedrockruntime/invokeModel/ai21Jamba").aggregate().join(); var traces = mockCollectorClient.getTraces(); var metrics = mockCollectorClient.getMetrics( @@ -1809,9 +1809,9 @@ protected void doTestBedrockRuntimeModelId() { AppSignalsConstants.LATENCY_METRIC)); var localService = getApplicationOtelServiceName(); - var localOperation = "GET /bedrockruntime/invokeModel"; + var localOperation = "GET /bedrockruntime/invokeModel/ai21Jamba"; String type = "AWS::Bedrock::Model"; - String identifier = "anthropic.claude-v2"; + String identifier = "ai21.jamba-1-5-mini-v1:0"; assertSpanClientAttributes( traces, bedrockRuntimeSpanName("InvokeModel"), @@ -1828,7 +1828,371 @@ protected void doTestBedrockRuntimeModelId() { 200, List.of( assertAttribute( - SemanticConventionsConstants.GEN_AI_REQUEST_MODEL, "anthropic.claude-v2"))); + SemanticConventionsConstants.GEN_AI_REQUEST_MODEL, "ai21.jamba-1-5-mini-v1:0"), + assertAttribute(SemanticConventionsConstants.GEN_AI_REQUEST_TEMPERATURE, "0.7"), + assertAttribute(SemanticConventionsConstants.GEN_AI_REQUEST_TOP_P, "0.8"), + assertAttribute(SemanticConventionsConstants.GEN_AI_RESPONSE_FINISH_REASONS, "[stop]"), + assertAttribute(SemanticConventionsConstants.GEN_AI_USAGE_INPUT_TOKENS, "5"), + assertAttribute(SemanticConventionsConstants.GEN_AI_USAGE_OUTPUT_TOKENS, "42"))); + assertMetricClientAttributes( + metrics, + AppSignalsConstants.LATENCY_METRIC, + localService, + localOperation, + getBedrockRuntimeServiceName(), + "InvokeModel", + type, + identifier, + 5000.0); + assertMetricClientAttributes( + metrics, + AppSignalsConstants.FAULT_METRIC, + localService, + localOperation, + getBedrockRuntimeServiceName(), + "InvokeModel", + type, + identifier, + 0.0); + assertMetricClientAttributes( + metrics, + AppSignalsConstants.ERROR_METRIC, + localService, + localOperation, + getBedrockRuntimeServiceName(), + "InvokeModel", + type, + identifier, + 0.0); + } + + protected void doTestBedrockRuntimeAmazonTitan() { + var response = appClient.get("/bedrockruntime/invokeModel/amazonTitan").aggregate().join(); + var traces = mockCollectorClient.getTraces(); + var metrics = + mockCollectorClient.getMetrics( + Set.of( + AppSignalsConstants.ERROR_METRIC, + AppSignalsConstants.FAULT_METRIC, + AppSignalsConstants.LATENCY_METRIC)); + + var localService = getApplicationOtelServiceName(); + var localOperation = "GET /bedrockruntime/invokeModel/amazonTitan"; + String type = "AWS::Bedrock::Model"; + String identifier = "amazon.titan-text-premier-v1:0"; + assertSpanClientAttributes( + traces, + bedrockRuntimeSpanName("InvokeModel"), + getBedrockRuntimeRpcServiceName(), + localService, + localOperation, + getBedrockRuntimeServiceName(), + "InvokeModel", + type, + identifier, + "bedrock.test", + 8080, + "http://bedrock.test:8080", + 200, + List.of( + assertAttribute( + SemanticConventionsConstants.GEN_AI_REQUEST_MODEL, + "amazon.titan-text-premier-v1:0"), + assertAttribute(SemanticConventionsConstants.GEN_AI_REQUEST_MAX_TOKENS, "100"), + assertAttribute(SemanticConventionsConstants.GEN_AI_REQUEST_TEMPERATURE, "0.7"), + assertAttribute(SemanticConventionsConstants.GEN_AI_REQUEST_TOP_P, "0.9"), + assertAttribute( + SemanticConventionsConstants.GEN_AI_RESPONSE_FINISH_REASONS, "[FINISHED]"), + assertAttribute(SemanticConventionsConstants.GEN_AI_USAGE_INPUT_TOKENS, "10"), + assertAttribute(SemanticConventionsConstants.GEN_AI_USAGE_OUTPUT_TOKENS, "15"))); + assertMetricClientAttributes( + metrics, + AppSignalsConstants.LATENCY_METRIC, + localService, + localOperation, + getBedrockRuntimeServiceName(), + "InvokeModel", + type, + identifier, + 5000.0); + assertMetricClientAttributes( + metrics, + AppSignalsConstants.FAULT_METRIC, + localService, + localOperation, + getBedrockRuntimeServiceName(), + "InvokeModel", + type, + identifier, + 0.0); + assertMetricClientAttributes( + metrics, + AppSignalsConstants.ERROR_METRIC, + localService, + localOperation, + getBedrockRuntimeServiceName(), + "InvokeModel", + type, + identifier, + 0.0); + } + + protected void doTestBedrockRuntimeAnthropicClaude() { + var response = appClient.get("/bedrockruntime/invokeModel/anthropicClaude").aggregate().join(); + + var traces = mockCollectorClient.getTraces(); + var metrics = + mockCollectorClient.getMetrics( + Set.of( + AppSignalsConstants.ERROR_METRIC, + AppSignalsConstants.FAULT_METRIC, + AppSignalsConstants.LATENCY_METRIC)); + + var localService = getApplicationOtelServiceName(); + var localOperation = "GET /bedrockruntime/invokeModel/anthropicClaude"; + String type = "AWS::Bedrock::Model"; + String identifier = "anthropic.claude-3-haiku-20240307-v1:0"; + + assertSpanClientAttributes( + traces, + bedrockRuntimeSpanName("InvokeModel"), + getBedrockRuntimeRpcServiceName(), + localService, + localOperation, + getBedrockRuntimeServiceName(), + "InvokeModel", + type, + identifier, + "bedrock.test", + 8080, + "http://bedrock.test:8080", + 200, + List.of( + assertAttribute( + SemanticConventionsConstants.GEN_AI_REQUEST_MODEL, + "anthropic.claude-3-haiku-20240307-v1:0"), + assertAttribute(SemanticConventionsConstants.GEN_AI_REQUEST_MAX_TOKENS, "512"), + assertAttribute(SemanticConventionsConstants.GEN_AI_REQUEST_TEMPERATURE, "0.6"), + assertAttribute(SemanticConventionsConstants.GEN_AI_REQUEST_TOP_P, "0.53"), + assertAttribute( + SemanticConventionsConstants.GEN_AI_RESPONSE_FINISH_REASONS, "[end_turn]"), + assertAttribute(SemanticConventionsConstants.GEN_AI_USAGE_INPUT_TOKENS, "2095"), + assertAttribute(SemanticConventionsConstants.GEN_AI_USAGE_OUTPUT_TOKENS, "503"))); + assertMetricClientAttributes( + metrics, + AppSignalsConstants.LATENCY_METRIC, + localService, + localOperation, + getBedrockRuntimeServiceName(), + "InvokeModel", + type, + identifier, + 5000.0); + assertMetricClientAttributes( + metrics, + AppSignalsConstants.FAULT_METRIC, + localService, + localOperation, + getBedrockRuntimeServiceName(), + "InvokeModel", + type, + identifier, + 0.0); + assertMetricClientAttributes( + metrics, + AppSignalsConstants.ERROR_METRIC, + localService, + localOperation, + getBedrockRuntimeServiceName(), + "InvokeModel", + type, + identifier, + 0.0); + } + + protected void doTestBedrockRuntimeCohereCommandR() { + var response = appClient.get("/bedrockruntime/invokeModel/cohereCommandR").aggregate().join(); + + var traces = mockCollectorClient.getTraces(); + var metrics = + mockCollectorClient.getMetrics( + Set.of( + AppSignalsConstants.ERROR_METRIC, + AppSignalsConstants.FAULT_METRIC, + AppSignalsConstants.LATENCY_METRIC)); + + var localService = getApplicationOtelServiceName(); + var localOperation = "GET /bedrockruntime/invokeModel/cohereCommandR"; + String type = "AWS::Bedrock::Model"; + String identifier = "cohere.command-r-v1:0"; + + assertSpanClientAttributes( + traces, + bedrockRuntimeSpanName("InvokeModel"), + getBedrockRuntimeRpcServiceName(), + localService, + localOperation, + getBedrockRuntimeServiceName(), + "InvokeModel", + type, + identifier, + "bedrock.test", + 8080, + "http://bedrock.test:8080", + 200, + List.of( + assertAttribute( + SemanticConventionsConstants.GEN_AI_REQUEST_MODEL, "cohere.command-r-v1:0"), + assertAttribute(SemanticConventionsConstants.GEN_AI_REQUEST_MAX_TOKENS, "4096"), + assertAttribute(SemanticConventionsConstants.GEN_AI_REQUEST_TEMPERATURE, "0.8"), + assertAttribute(SemanticConventionsConstants.GEN_AI_REQUEST_TOP_P, "0.45"), + assertAttribute( + SemanticConventionsConstants.GEN_AI_RESPONSE_FINISH_REASONS, "[COMPLETE]"), + assertAttribute(SemanticConventionsConstants.GEN_AI_USAGE_INPUT_TOKENS, "9"), + assertAttribute(SemanticConventionsConstants.GEN_AI_USAGE_OUTPUT_TOKENS, "16"))); + assertMetricClientAttributes( + metrics, + AppSignalsConstants.LATENCY_METRIC, + localService, + localOperation, + getBedrockRuntimeServiceName(), + "InvokeModel", + type, + identifier, + 5000.0); + assertMetricClientAttributes( + metrics, + AppSignalsConstants.FAULT_METRIC, + localService, + localOperation, + getBedrockRuntimeServiceName(), + "InvokeModel", + type, + identifier, + 0.0); + assertMetricClientAttributes( + metrics, + AppSignalsConstants.ERROR_METRIC, + localService, + localOperation, + getBedrockRuntimeServiceName(), + "InvokeModel", + type, + identifier, + 0.0); + } + + protected void doTestBedrockRuntimeMetaLlama() { + var response = appClient.get("/bedrockruntime/invokeModel/metaLlama").aggregate().join(); + + var traces = mockCollectorClient.getTraces(); + var metrics = + mockCollectorClient.getMetrics( + Set.of( + AppSignalsConstants.ERROR_METRIC, + AppSignalsConstants.FAULT_METRIC, + AppSignalsConstants.LATENCY_METRIC)); + + var localService = getApplicationOtelServiceName(); + var localOperation = "GET /bedrockruntime/invokeModel/metaLlama"; + String type = "AWS::Bedrock::Model"; + String identifier = "meta.llama3-70b-instruct-v1:0"; + + assertSpanClientAttributes( + traces, + bedrockRuntimeSpanName("InvokeModel"), + getBedrockRuntimeRpcServiceName(), + localService, + localOperation, + getBedrockRuntimeServiceName(), + "InvokeModel", + type, + identifier, + "bedrock.test", + 8080, + "http://bedrock.test:8080", + 200, + List.of( + assertAttribute( + SemanticConventionsConstants.GEN_AI_REQUEST_MODEL, "meta.llama3-70b-instruct-v1:0"), + assertAttribute(SemanticConventionsConstants.GEN_AI_REQUEST_MAX_TOKENS, "128"), + assertAttribute(SemanticConventionsConstants.GEN_AI_REQUEST_TEMPERATURE, "0.1"), + assertAttribute(SemanticConventionsConstants.GEN_AI_REQUEST_TOP_P, "0.9"), + assertAttribute(SemanticConventionsConstants.GEN_AI_RESPONSE_FINISH_REASONS, "[stop]"), + assertAttribute(SemanticConventionsConstants.GEN_AI_USAGE_INPUT_TOKENS, "2095"), + assertAttribute(SemanticConventionsConstants.GEN_AI_USAGE_OUTPUT_TOKENS, "503"))); + assertMetricClientAttributes( + metrics, + AppSignalsConstants.LATENCY_METRIC, + localService, + localOperation, + getBedrockRuntimeServiceName(), + "InvokeModel", + type, + identifier, + 5000.0); + assertMetricClientAttributes( + metrics, + AppSignalsConstants.FAULT_METRIC, + localService, + localOperation, + getBedrockRuntimeServiceName(), + "InvokeModel", + type, + identifier, + 0.0); + assertMetricClientAttributes( + metrics, + AppSignalsConstants.ERROR_METRIC, + localService, + localOperation, + getBedrockRuntimeServiceName(), + "InvokeModel", + type, + identifier, + 0.0); + } + + protected void doTestBedrockRuntimeMistral() { + var response = appClient.get("/bedrockruntime/invokeModel/mistralAi").aggregate().join(); + + var traces = mockCollectorClient.getTraces(); + var metrics = + mockCollectorClient.getMetrics( + Set.of( + AppSignalsConstants.ERROR_METRIC, + AppSignalsConstants.FAULT_METRIC, + AppSignalsConstants.LATENCY_METRIC)); + + var localService = getApplicationOtelServiceName(); + var localOperation = "GET /bedrockruntime/invokeModel/mistralAi"; + String type = "AWS::Bedrock::Model"; + String identifier = "mistral.mistral-large-2402-v1:0"; + + assertSpanClientAttributes( + traces, + bedrockRuntimeSpanName("InvokeModel"), + getBedrockRuntimeRpcServiceName(), + localService, + localOperation, + getBedrockRuntimeServiceName(), + "InvokeModel", + type, + identifier, + "bedrock.test", + 8080, + "http://bedrock.test:8080", + 200, + List.of( + assertAttribute( + SemanticConventionsConstants.GEN_AI_REQUEST_MODEL, + "mistral.mistral-large-2402-v1:0"), + assertAttribute(SemanticConventionsConstants.GEN_AI_REQUEST_MAX_TOKENS, "4096"), + assertAttribute(SemanticConventionsConstants.GEN_AI_REQUEST_TEMPERATURE, "0.75"), + assertAttribute(SemanticConventionsConstants.GEN_AI_REQUEST_TOP_P, "0.25"), + assertAttribute(SemanticConventionsConstants.GEN_AI_RESPONSE_FINISH_REASONS, "[stop]"), + assertAttribute(SemanticConventionsConstants.GEN_AI_USAGE_INPUT_TOKENS, "15"), + assertAttribute(SemanticConventionsConstants.GEN_AI_USAGE_OUTPUT_TOKENS, "24"))); assertMetricClientAttributes( metrics, AppSignalsConstants.LATENCY_METRIC, diff --git a/appsignals-tests/contract-tests/src/test/java/software/amazon/opentelemetry/appsignals/test/awssdk/v1/AwsSdkV1Test.java b/appsignals-tests/contract-tests/src/test/java/software/amazon/opentelemetry/appsignals/test/awssdk/v1/AwsSdkV1Test.java index 3cf5a8982e..fa5a586c5d 100644 --- a/appsignals-tests/contract-tests/src/test/java/software/amazon/opentelemetry/appsignals/test/awssdk/v1/AwsSdkV1Test.java +++ b/appsignals-tests/contract-tests/src/test/java/software/amazon/opentelemetry/appsignals/test/awssdk/v1/AwsSdkV1Test.java @@ -217,8 +217,33 @@ void testBedrockAgentDataSourceId() { } @Test - void testBedrockRuntimeModelId() { - doTestBedrockRuntimeModelId(); + void testBedrockRuntimeAmazonTitan() { + doTestBedrockRuntimeAmazonTitan(); + } + + @Test + void testBedrockRuntimeAi21Jamba() { + doTestBedrockRuntimeAi21Jamba(); + } + + @Test + void testBedrockRuntimeAnthropicClaude() { + doTestBedrockRuntimeAnthropicClaude(); + } + + @Test + void testBedrockRuntimeCohereCommandR() { + doTestBedrockRuntimeCohereCommandR(); + } + + @Test + void testBedrockRuntimeMetaLlama() { + doTestBedrockRuntimeMetaLlama(); + } + + @Test + void testBedrockRuntimeMistral() { + doTestBedrockRuntimeMistral(); } @Test diff --git a/appsignals-tests/contract-tests/src/test/java/software/amazon/opentelemetry/appsignals/test/awssdk/v2/AwsSdkV2Test.java b/appsignals-tests/contract-tests/src/test/java/software/amazon/opentelemetry/appsignals/test/awssdk/v2/AwsSdkV2Test.java index a76736daf3..46c6b7e425 100644 --- a/appsignals-tests/contract-tests/src/test/java/software/amazon/opentelemetry/appsignals/test/awssdk/v2/AwsSdkV2Test.java +++ b/appsignals-tests/contract-tests/src/test/java/software/amazon/opentelemetry/appsignals/test/awssdk/v2/AwsSdkV2Test.java @@ -220,8 +220,33 @@ void testBedrockAgentDataSourceId() { } @Test - void testBedrockRuntimeModelId() { - doTestBedrockRuntimeModelId(); + void testBedrockRuntimeAmazonTitan() { + doTestBedrockRuntimeAmazonTitan(); + } + + @Test + void testBedrockRuntimeAi21Jamba() { + doTestBedrockRuntimeAi21Jamba(); + } + + @Test + void testBedrockRuntimeAnthropicClaude() { + doTestBedrockRuntimeAnthropicClaude(); + } + + @Test + void testBedrockRuntimeCohereCommandR() { + doTestBedrockRuntimeCohereCommandR(); + } + + @Test + void testBedrockRuntimeMetaLlama() { + doTestBedrockRuntimeMetaLlama(); + } + + @Test + void testBedrockRuntimeMistral() { + doTestBedrockRuntimeMistral(); } @Test @@ -236,8 +261,8 @@ void testBedrockAgentRuntimeAgentId() { // TODO: Enable testBedrockAgentRuntimeKnowledgeBaseId test after KnowledgeBaseId is supported in // OTEL BedrockAgentRuntime instrumentation - // @Test - // void testBedrockAgentRuntimeKnowledgeBaseId() { - // doTestBedrockAgentRuntimeKnowledgeBaseId(); - // } + @Test + void testBedrockAgentRuntimeKnowledgeBaseId() { + doTestBedrockAgentRuntimeKnowledgeBaseId(); + } } diff --git a/appsignals-tests/contract-tests/src/test/java/software/amazon/opentelemetry/appsignals/test/utils/SemanticConventionsConstants.java b/appsignals-tests/contract-tests/src/test/java/software/amazon/opentelemetry/appsignals/test/utils/SemanticConventionsConstants.java index 3fe1644dd5..f0cac6da46 100644 --- a/appsignals-tests/contract-tests/src/test/java/software/amazon/opentelemetry/appsignals/test/utils/SemanticConventionsConstants.java +++ b/appsignals-tests/contract-tests/src/test/java/software/amazon/opentelemetry/appsignals/test/utils/SemanticConventionsConstants.java @@ -63,6 +63,12 @@ public class SemanticConventionsConstants { public static final String AWS_AGENT_ID = "aws.bedrock.agent.id"; public static final String AWS_GUARDRAIL_ID = "aws.bedrock.guardrail.id"; public static final String GEN_AI_REQUEST_MODEL = "gen_ai.request.model"; + public static final String GEN_AI_REQUEST_MAX_TOKENS = "gen_ai.request.max_tokens"; + public static final String GEN_AI_REQUEST_TEMPERATURE = "gen_ai.request.temperature"; + public static final String GEN_AI_REQUEST_TOP_P = "gen_ai.request.top_p"; + public static final String GEN_AI_RESPONSE_FINISH_REASONS = "gen_ai.response.finish_reasons"; + public static final String GEN_AI_USAGE_INPUT_TOKENS = "gen_ai.usage.input_tokens"; + public static final String GEN_AI_USAGE_OUTPUT_TOKENS = "gen_ai.usage.output_tokens"; // kafka public static final String MESSAGING_CLIENT_ID = "messaging.client_id"; diff --git a/appsignals-tests/images/aws-sdk/aws-sdk-base/src/main/java/com/amazon/sampleapp/Utils.java b/appsignals-tests/images/aws-sdk/aws-sdk-base/src/main/java/com/amazon/sampleapp/Utils.java index f8f5313b26..c9264ed1c9 100644 --- a/appsignals-tests/images/aws-sdk/aws-sdk-base/src/main/java/com/amazon/sampleapp/Utils.java +++ b/appsignals-tests/images/aws-sdk/aws-sdk-base/src/main/java/com/amazon/sampleapp/Utils.java @@ -188,13 +188,69 @@ public static void setupInvokeModelRoute(int status) { post( "/model/:modelId/invoke", (req, res) -> { - ObjectNode jsonResponse = objectMapper.createObjectNode(); - jsonResponse.put("completion", "A simple completion token."); - jsonResponse.put("stop_reason", "stop_sequence"); - jsonResponse.put("stop", "stop_token"); - + String modelId = req.params(":modelId"); + ObjectMapper mapper = new ObjectMapper(); + ObjectNode jsonResponse = mapper.createObjectNode(); res.status(status); res.type("application/json"); + + if (modelId.contains("amazon.titan")) { + jsonResponse.put("inputTextTokenCount", 10); + + ArrayNode results = mapper.createArrayNode(); + ObjectNode result = mapper.createObjectNode(); + result.put("tokenCount", 15); + result.put("completionReason", "FINISHED"); + results.add(result); + + jsonResponse.set("results", results); + } else if (modelId.contains("ai21.jamba")) { + ArrayNode choices = mapper.createArrayNode(); + ObjectNode choice = mapper.createObjectNode(); + choice.put("finish_reason", "stop"); + + ObjectNode message = mapper.createObjectNode(); + message.put("content", "I am the AI21 Jamba language model"); + message.put("role", "assistant"); + choice.set("message", message); + + choices.add(choice); + jsonResponse.set("choices", choices); + + ObjectNode usage = mapper.createObjectNode(); + usage.put("prompt_tokens", 5); + usage.put("completion_tokens", 42); + jsonResponse.set("usage", usage); + } else if (modelId.contains("anthropic.claude")) { + jsonResponse.put("stop_reason", "end_turn"); + + ObjectNode usage = mapper.createObjectNode(); + usage.put("input_tokens", 2095); + usage.put("output_tokens", 503); + jsonResponse.set("usage", usage); + } else if (modelId.contains("cohere.command")) { + jsonResponse.put( + "text", + "LISP's elegant simplicity and powerful macro system make it perfect for building interpreters!"); + jsonResponse.put("finish_reason", "COMPLETE"); + } else if (modelId.contains("meta.llama")) { + jsonResponse.put("prompt_token_count", 2095); + jsonResponse.put("generation_token_count", 503); + jsonResponse.put("stop_reason", "stop"); + } else if (modelId.contains("mistral")) { + ArrayNode outputs = mapper.createArrayNode(); + ObjectNode output = mapper.createObjectNode(); + + output.put( + "text", + "A compiler translates the entire source code to machine code before execution, while an interpreter executes the code line by line in real-time."); + output.put("stop_reason", "stop"); + + outputs.add(output); + + jsonResponse.set("outputs", outputs); + } + return jsonResponse; }); } diff --git a/appsignals-tests/images/aws-sdk/aws-sdk-v1/src/main/java/com/amazon/sampleapp/App.java b/appsignals-tests/images/aws-sdk/aws-sdk-v1/src/main/java/com/amazon/sampleapp/App.java index 0a7498bf2a..ad5f7a73b4 100644 --- a/appsignals-tests/images/aws-sdk/aws-sdk-v1/src/main/java/com/amazon/sampleapp/App.java +++ b/appsignals-tests/images/aws-sdk/aws-sdk-v1/src/main/java/com/amazon/sampleapp/App.java @@ -56,12 +56,16 @@ import com.amazonaws.services.sqs.model.CreateQueueRequest; import com.amazonaws.services.sqs.model.ReceiveMessageRequest; import com.amazonaws.services.sqs.model.SendMessageRequest; +import com.fasterxml.jackson.databind.ObjectMapper; import java.io.File; import java.io.IOException; import java.net.http.HttpClient; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.time.Duration; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -578,6 +582,175 @@ private static void setupBedrock() { bedrockRuntimeClient.invokeModel(invokeModelRequest); return ""; }); + get( + "/bedrockruntime/invokeModel/ai21Jamba", + (req, res) -> { + setMainStatus(200); + String modelId = "ai21.jamba-1-5-mini-v1:0"; + + ObjectMapper mapper = new ObjectMapper(); + Map request = new HashMap<>(); + + List> messages = new ArrayList<>(); + Map message = new HashMap<>(); + message.put("role", "user"); + message.put("content", "Which LLM are you?"); + messages.add(message); + + request.put("messages", messages); + request.put("max_tokens", 1000); + request.put("top_p", 0.8); + request.put("temperature", 0.7); + + InvokeModelRequest invokeModelRequest = + new InvokeModelRequest() + .withModelId(modelId) + .withBody(StandardCharsets.UTF_8.encode(mapper.writeValueAsString(request))); + + var response = bedrockRuntimeClient.invokeModel(invokeModelRequest); + var responseBody = new String(response.getBody().array(), StandardCharsets.UTF_8); + + return ""; + }); + get( + "/bedrockruntime/invokeModel/amazonTitan", + (req, res) -> { + setMainStatus(200); + String modelId = "amazon.titan-text-premier-v1:0"; + + ObjectMapper mapper = new ObjectMapper(); + Map request = new HashMap<>(); + request.put("inputText", "Hello, world!"); + + Map config = new HashMap<>(); + config.put("temperature", 0.7); + config.put("topP", 0.9); + config.put("maxTokenCount", 100); + + request.put("textGenerationConfig", config); + + InvokeModelRequest invokeModelRequest = + new InvokeModelRequest() + .withModelId(modelId) + .withBody(StandardCharsets.UTF_8.encode(mapper.writeValueAsString(request))); + + var response = bedrockRuntimeClient.invokeModel(invokeModelRequest); + var responseBody = new String(response.getBody().array(), StandardCharsets.UTF_8); + + return ""; + }); + get( + "/bedrockruntime/invokeModel/anthropicClaude", + (req, res) -> { + setMainStatus(200); + String modelId = "anthropic.claude-3-haiku-20240307-v1:0"; + + ObjectMapper mapper = new ObjectMapper(); + Map request = new HashMap<>(); + + List> messages = new ArrayList<>(); + Map message = new HashMap<>(); + message.put("role", "user"); + message.put("content", "Describe a cache in one line"); + messages.add(message); + + request.put("messages", messages); + request.put("anthropic_version", "bedrock-2023-05-31"); + request.put("max_tokens", 512); + request.put("top_p", 0.53); + request.put("temperature", 0.6); + + InvokeModelRequest invokeModelRequest = + new InvokeModelRequest() + .withModelId(modelId) + .withBody(StandardCharsets.UTF_8.encode(mapper.writeValueAsString(request))); + + var response = bedrockRuntimeClient.invokeModel(invokeModelRequest); + var responseBody = new String(response.getBody().array(), StandardCharsets.UTF_8); + + return ""; + }); + get( + "/bedrockruntime/invokeModel/cohereCommandR", + (req, res) -> { + setMainStatus(200); + String modelId = "cohere.command-r-v1:0"; + + ObjectMapper mapper = new ObjectMapper(); + Map request = new HashMap<>(); + + request.put("message", "Convince me to write a LISP interpreter in one line"); + request.put("temperature", 0.8); + request.put("max_tokens", 4096); + request.put("p", 0.45); + + InvokeModelRequest invokeModelRequest = + new InvokeModelRequest() + .withModelId(modelId) + .withBody(StandardCharsets.UTF_8.encode(mapper.writeValueAsString(request))); + + var response = bedrockRuntimeClient.invokeModel(invokeModelRequest); + var responseBody = new String(response.getBody().array(), StandardCharsets.UTF_8); + + return ""; + }); + get( + "/bedrockruntime/invokeModel/metaLlama", + (req, res) -> { + setMainStatus(200); + String modelId = "meta.llama3-70b-instruct-v1:0"; + + ObjectMapper mapper = new ObjectMapper(); + Map request = new HashMap<>(); + + String prompt = "Describe the purpose of a 'hello world' program in one line"; + String instruction = + String.format( + "<|begin_of_text|><|start_header_id|>user<|end_header_id|>\n%s<|eot_id|>\n<|start_header_id|>assistant<|end_header_id|>\n", + prompt); + + request.put("prompt", instruction); + request.put("max_gen_len", 128); + request.put("temperature", 0.1); + request.put("top_p", 0.9); + + InvokeModelRequest invokeModelRequest = + new InvokeModelRequest() + .withModelId(modelId) + .withBody(StandardCharsets.UTF_8.encode(mapper.writeValueAsString(request))); + + var response = bedrockRuntimeClient.invokeModel(invokeModelRequest); + var responseBody = new String(response.getBody().array(), StandardCharsets.UTF_8); + + return ""; + }); + get( + "/bedrockruntime/invokeModel/mistralAi", + (req, res) -> { + setMainStatus(200); + String modelId = "mistral.mistral-large-2402-v1:0"; + + ObjectMapper mapper = new ObjectMapper(); + Map request = new HashMap<>(); + + String prompt = "Describe the difference between a compiler and interpreter in one line."; + String instruction = String.format("[INST] %s [/INST]\n", prompt); + + request.put("prompt", instruction); + request.put("max_tokens", 4096); + request.put("temperature", 0.75); + request.put("top_p", 0.25); + + InvokeModelRequest invokeModelRequest = + new InvokeModelRequest() + .withModelId(modelId) + .withBody(StandardCharsets.UTF_8.encode(mapper.writeValueAsString(request))); + + var response = bedrockRuntimeClient.invokeModel(invokeModelRequest); + var responseBody = new String(response.getBody().array(), StandardCharsets.UTF_8); + + return ""; + }); get( "/bedrockagent/get-data-source", diff --git a/appsignals-tests/images/aws-sdk/aws-sdk-v2/src/main/java/com/amazon/sampleapp/App.java b/appsignals-tests/images/aws-sdk/aws-sdk-v2/src/main/java/com/amazon/sampleapp/App.java index bc43402e9d..2982135fd4 100644 --- a/appsignals-tests/images/aws-sdk/aws-sdk-v2/src/main/java/com/amazon/sampleapp/App.java +++ b/appsignals-tests/images/aws-sdk/aws-sdk-v2/src/main/java/com/amazon/sampleapp/App.java @@ -28,6 +28,9 @@ import java.net.http.HttpClient; import java.nio.charset.Charset; import java.time.Duration; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -601,6 +604,169 @@ private static void setupBedrock() { return ""; }); get( + "/bedrockruntime/invokeModel/ai21Jamba", + (req, res) -> { + setMainStatus(200); + + ObjectMapper mapper = new ObjectMapper(); + Map request = new HashMap<>(); + + List> messages = new ArrayList<>(); + Map message = new HashMap<>(); + message.put("role", "user"); + message.put("content", "Which LLM are you?"); + messages.add(message); + + request.put("messages", messages); + request.put("max_tokens", 1000); + request.put("top_p", 0.8); + request.put("temperature", 0.7); + + InvokeModelRequest invokeModelRequest = + InvokeModelRequest.builder() + .modelId("ai21.jamba-1-5-mini-v1:0") + .body(SdkBytes.fromUtf8String(mapper.writeValueAsString(request))) + .build(); + + bedrockRuntimeClient.invokeModel(invokeModelRequest); + + return ""; + }); + get( + "/bedrockruntime/invokeModel/amazonTitan", + (req, res) -> { + setMainStatus(200); + + ObjectMapper mapper = new ObjectMapper(); + Map request = new HashMap<>(); + request.put("inputText", "Hello, world!"); + + Map config = new HashMap<>(); + config.put("temperature", 0.7); + config.put("topP", 0.9); + config.put("maxTokenCount", 100); + + request.put("textGenerationConfig", config); + + InvokeModelRequest invokeModelRequest = + InvokeModelRequest.builder() + .modelId("amazon.titan-text-premier-v1:0") + .body(SdkBytes.fromUtf8String(mapper.writeValueAsString(request))) + .build(); + + bedrockRuntimeClient.invokeModel(invokeModelRequest); + + return ""; + }); + get( + "/bedrockruntime/invokeModel/anthropicClaude", + (req, res) -> { + setMainStatus(200); + + ObjectMapper mapper = new ObjectMapper(); + Map request = new HashMap<>(); + + List> messages = new ArrayList<>(); + Map message = new HashMap<>(); + message.put("role", "user"); + message.put("content", "Describe a cache in one line"); + messages.add(message); + + request.put("messages", messages); + request.put("anthropic_version", "bedrock-2023-05-31"); + request.put("max_tokens", 512); + request.put("top_p", 0.53); + request.put("temperature", 0.6); + + InvokeModelRequest invokeModelRequest = + InvokeModelRequest.builder() + .modelId("anthropic.claude-3-haiku-20240307-v1:0") + .body(SdkBytes.fromUtf8String(mapper.writeValueAsString(request))) + .build(); + + bedrockRuntimeClient.invokeModel(invokeModelRequest); + + return ""; + }); + get( + "/bedrockruntime/invokeModel/cohereCommandR", + (req, res) -> { + setMainStatus(200); + + ObjectMapper mapper = new ObjectMapper(); + Map request = new HashMap<>(); + + request.put("message", "Convince me to write a LISP interpreter in one line"); + request.put("temperature", 0.8); + request.put("max_tokens", 4096); + request.put("p", 0.45); + + InvokeModelRequest invokeModelRequest = + InvokeModelRequest.builder() + .modelId("cohere.command-r-v1:0") + .body(SdkBytes.fromUtf8String(mapper.writeValueAsString(request))) + .build(); + + bedrockRuntimeClient.invokeModel(invokeModelRequest); + + return ""; + }); + get( + "/bedrockruntime/invokeModel/metaLlama", + (req, res) -> { + setMainStatus(200); + + ObjectMapper mapper = new ObjectMapper(); + Map request = new HashMap<>(); + + String prompt = "Describe the purpose of a 'hello world' program in one line"; + String instruction = + String.format( + "<|begin_of_text|><|start_header_id|>user<|end_header_id|>\n%s<|eot_id|>\n<|start_header_id|>assistant<|end_header_id|>\n", + prompt); + + request.put("prompt", instruction); + request.put("max_gen_len", 128); + request.put("temperature", 0.1); + request.put("top_p", 0.9); + + InvokeModelRequest invokeModelRequest = + InvokeModelRequest.builder() + .modelId("meta.llama3-70b-instruct-v1:0") + .body(SdkBytes.fromUtf8String(mapper.writeValueAsString(request))) + .build(); + + bedrockRuntimeClient.invokeModel(invokeModelRequest); + + return ""; + }); + get( + "/bedrockruntime/invokeModel/mistralAi", + (req, res) -> { + setMainStatus(200); + + ObjectMapper mapper = new ObjectMapper(); + Map request = new HashMap<>(); + + String prompt = "Describe the difference between a compiler and interpreter in one line."; + String instruction = String.format("[INST] %s [/INST]\n", prompt); + + request.put("prompt", instruction); + request.put("max_tokens", 4096); + request.put("temperature", 0.75); + request.put("top_p", 0.25); + + InvokeModelRequest invokeModelRequest = + InvokeModelRequest.builder() + .modelId("mistral.mistral-large-2402-v1:0") + .body(SdkBytes.fromUtf8String(mapper.writeValueAsString(request))) + .build(); + + bedrockRuntimeClient.invokeModel(invokeModelRequest); + + return ""; + }); + get( "/bedrockagent/get-data-source", (req, res) -> { setMainStatus(200);