From 287139b92953ed44ff5c28f72938fc45255c2e5e Mon Sep 17 00:00:00 2001 From: Thomas Pierce Date: Tue, 9 Apr 2024 21:31:33 +0000 Subject: [PATCH 1/3] Metric Schema and config name changes In this commit, we are removing RemoteTarget and replacing with RemoteResourceIdentifier and RemoteResourceType. Further, we are formatting RemoteService, and the content of the RemoteResource attributes such that they align with AWS Cloud Control resource names. In addition to these changes, we are modifying the config names used for enabling and configuring Application Signals to use the full feature name, "application.signals". --- .../test/awssdk/base/AwsSdkBaseTest.java | 325 ++++++++++++------ .../test/awssdk/v1/AwsSdkV1Test.java | 20 -- .../test/awssdk/v2/AwsSdkV2Test.java | 19 - .../test/base/ContractTestBase.java | 4 +- .../test/utils/AppSignalsConstants.java | 3 +- .../AwsAppSignalsCustomizerProvider.java | 29 +- .../javaagent/providers/AwsAttributeKeys.java | 12 +- .../AwsMetricAttributeGenerator.java | 125 ++++--- .../javaagent/providers/SqsUrlParser.java | 171 ++------- .../AwsMetricAttributeGeneratorTest.java | 159 ++------- .../javaagent/providers/SqsUrlParserTest.java | 82 +++++ 11 files changed, 470 insertions(+), 479 deletions(-) create mode 100644 awsagentprovider/src/test/java/software/amazon/opentelemetry/javaagent/providers/SqsUrlParserTest.java 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 9cb019509c..246951e104 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 @@ -92,14 +92,6 @@ protected String getApplicationWaitPattern() { protected abstract String getKinesisSpanNamePrefix(); - protected abstract String getS3ServiceName(); - - protected abstract String getDynamoDbServiceName(); - - protected abstract String getSqsServiceName(); - - protected abstract String getKinesisServiceName(); - protected abstract String getS3RpcServiceName(); protected abstract String getDynamoDbRpcServiceName(); @@ -108,6 +100,22 @@ protected String getApplicationWaitPattern() { protected abstract String getKinesisRpcServiceName(); + private String getS3ServiceName() { + return "AWS::S3"; + } + + private String getDynamoDbServiceName() { + return "AWS::DynamoDB"; + } + + private String getSqsServiceName() { + return "AWS::SQS"; + } + + private String getKinesisServiceName() { + return "AWS::Kinesis"; + } + private String s3SpanName(String operation) { return String.format("%s.%s", getS3SpanNamePrefix(), operation); } @@ -198,7 +206,8 @@ private void assertSpanClientAttributes( String localOperation, String service, String method, - String target, + String type, + String identifier, String peerName, int peerPort, String url, @@ -215,7 +224,8 @@ private void assertSpanClientAttributes( localOperation, service, method, - target, + type, + identifier, peerName, peerPort, url, @@ -231,7 +241,8 @@ private void assertSpanProducerAttributes( String localOperation, String service, String method, - String target, + String type, + String identifier, String peerName, int peerPort, String url, @@ -247,7 +258,8 @@ private void assertSpanProducerAttributes( localOperation, service, method, - target, + type, + identifier, peerName, peerPort, url, @@ -294,7 +306,8 @@ private void assertSpanAttributes( String localOperation, String service, String method, - String target, + String type, + String identifier, String peerName, int peerPort, String url, @@ -316,7 +329,8 @@ private void assertSpanAttributes( localOperation, service, method, - target, + type, + identifier, awsSpanKind); for (var assertion : extraAssertions) { assertThat(spanAttributes).satisfiesOnlyOnce(assertion); @@ -330,7 +344,8 @@ private void assertAwsAttributes( String localOperation, String service, String operation, - String target, + String type, + String identifier, String spanKind) { var assertions = @@ -341,8 +356,11 @@ private void assertAwsAttributes( .satisfiesOnlyOnce(assertAttribute(AppSignalsConstants.AWS_REMOTE_OPERATION, operation)) .satisfiesOnlyOnce(assertAttribute(AppSignalsConstants.AWS_REMOTE_SERVICE, service)) .satisfiesOnlyOnce(assertAttribute(AppSignalsConstants.AWS_SPAN_KIND, spanKind)); - if (target != null) { - assertions.satisfiesOnlyOnce(assertAttribute(AppSignalsConstants.AWS_REMOTE_TARGET, target)); + if (type != null && identifier != null) { + assertions.satisfiesOnlyOnce( + assertAttribute(AppSignalsConstants.AWS_REMOTE_RESOURCE_TYPE, type)); + assertions.satisfiesOnlyOnce( + assertAttribute(AppSignalsConstants.AWS_REMOTE_RESOURCE_IDENTIFIER, identifier)); } } @@ -365,7 +383,8 @@ protected void assertMetricClientAttributes( String localOperation, String service, String method, - String target, + String type, + String identifier, Double expectedSum) { assertMetricAttributes( resourceScopeMetrics, @@ -375,7 +394,8 @@ protected void assertMetricClientAttributes( localOperation, service, method, - target, + type, + identifier, expectedSum); } @@ -386,7 +406,8 @@ protected void assertMetricProducerAttributes( String localOperation, String service, String method, - String target, + String type, + String identifier, Double expectedSum) { assertMetricAttributes( resourceScopeMetrics, @@ -396,7 +417,8 @@ protected void assertMetricProducerAttributes( localOperation, service, method, - target, + type, + identifier, expectedSum); } @@ -407,7 +429,8 @@ protected void assertMetricConsumerAttributes( String localOperation, String service, String method, - String target, + String type, + String identifier, Double expectedSum) { assertMetricAttributes( resourceScopeMetrics, @@ -417,7 +440,8 @@ protected void assertMetricConsumerAttributes( localOperation, service, method, - target, + type, + identifier, expectedSum); } @@ -429,7 +453,8 @@ protected void assertMetricAttributes( String localOperation, String service, String method, - String target, + String type, + String identifier, Double expectedSum) { assertThat(resourceScopeMetrics) .anySatisfy( @@ -447,7 +472,8 @@ protected void assertMetricAttributes( localOperation, service, method, - target, + type, + identifier, spanKind); if (expectedSum != null) { double actualSum = dataPoint.getSum(); @@ -476,7 +502,8 @@ protected void doTestS3CreateBucket() throws Exception { var localService = getApplicationOtelServiceName(); var localOperation = "GET /s3/createbucket/:bucketname"; - var target = "::s3:::create-bucket"; + var type = "AWS::S3::Bucket"; + var identifier = "create-bucket"; assertSpanClientAttributes( traces, @@ -486,7 +513,8 @@ protected void doTestS3CreateBucket() throws Exception { localOperation, getS3ServiceName(), "CreateBucket", - target, + type, + identifier, "create-bucket.s3.localstack", 4566, "http://create-bucket.s3.localstack:4566", @@ -499,7 +527,8 @@ protected void doTestS3CreateBucket() throws Exception { localOperation, getS3ServiceName(), "CreateBucket", - target, + type, + identifier, 5000.0); assertMetricClientAttributes( metrics, @@ -508,7 +537,8 @@ protected void doTestS3CreateBucket() throws Exception { localOperation, getS3ServiceName(), "CreateBucket", - target, + type, + identifier, 0.0); assertMetricClientAttributes( metrics, @@ -517,7 +547,8 @@ protected void doTestS3CreateBucket() throws Exception { localOperation, getS3ServiceName(), "CreateBucket", - target, + type, + identifier, 0.0); } @@ -534,7 +565,8 @@ protected void doTestS3CreateObject() throws Exception { var localService = getApplicationOtelServiceName(); var localOperation = "GET /s3/createobject/:bucketname/:objectname"; - var target = "::s3:::put-object"; + var type = "AWS::S3::Bucket"; + var identifier = "put-object"; assertSpanClientAttributes( traces, @@ -544,7 +576,8 @@ protected void doTestS3CreateObject() throws Exception { localOperation, getS3ServiceName(), "PutObject", - target, + type, + identifier, "put-object.s3.localstack", 4566, "http://put-object.s3.localstack:4566", @@ -557,7 +590,8 @@ protected void doTestS3CreateObject() throws Exception { localOperation, getS3ServiceName(), "PutObject", - target, + type, + identifier, 5000.0); assertMetricClientAttributes( metrics, @@ -566,7 +600,8 @@ protected void doTestS3CreateObject() throws Exception { localOperation, getS3ServiceName(), "PutObject", - target, + type, + identifier, 0.0); assertMetricClientAttributes( metrics, @@ -575,7 +610,8 @@ protected void doTestS3CreateObject() throws Exception { localOperation, getS3ServiceName(), "PutObject", - target, + type, + identifier, 0.0); } @@ -591,7 +627,8 @@ protected void doTestS3GetObject() throws Exception { var localService = getApplicationOtelServiceName(); var localOperation = "GET /s3/getobject/:bucketName/:objectname"; - var target = "::s3:::get-object"; + var type = "AWS::S3::Bucket"; + var identifier = "get-object"; assertSpanClientAttributes( traces, @@ -601,7 +638,8 @@ protected void doTestS3GetObject() throws Exception { localOperation, getS3ServiceName(), "GetObject", - target, + type, + identifier, "get-object.s3.localstack", 4566, "http://get-object.s3.localstack:4566", @@ -614,7 +652,8 @@ protected void doTestS3GetObject() throws Exception { localOperation, getS3ServiceName(), "GetObject", - target, + type, + identifier, 5000.0); assertMetricClientAttributes( metrics, @@ -623,7 +662,8 @@ protected void doTestS3GetObject() throws Exception { localOperation, getS3ServiceName(), "GetObject", - target, + type, + identifier, 0.0); assertMetricClientAttributes( metrics, @@ -632,7 +672,8 @@ protected void doTestS3GetObject() throws Exception { localOperation, getS3ServiceName(), "GetObject", - target, + type, + identifier, 0.0); } @@ -648,7 +689,8 @@ protected void doTestS3Error() { var localService = getApplicationOtelServiceName(); var localOperation = "GET /s3/error"; - var target = "::s3:::error-bucket"; + var type = "AWS::S3::Bucket"; + var identifier = "error-bucket"; assertSpanClientAttributes( traces, @@ -658,7 +700,8 @@ protected void doTestS3Error() { localOperation, getS3ServiceName(), "GetObject", - target, + type, + identifier, "error-bucket.s3.test", 8080, "http://error-bucket.s3.test:8080", @@ -671,7 +714,8 @@ protected void doTestS3Error() { localOperation, getS3ServiceName(), "GetObject", - target, + type, + identifier, 5000.0); assertMetricClientAttributes( metrics, @@ -680,7 +724,8 @@ protected void doTestS3Error() { localOperation, getS3ServiceName(), "GetObject", - target, + type, + identifier, 0.0); assertMetricClientAttributes( metrics, @@ -689,7 +734,8 @@ protected void doTestS3Error() { localOperation, getS3ServiceName(), "GetObject", - target, + type, + identifier, 1.0); } @@ -705,7 +751,8 @@ protected void doTestS3Fault() { var localService = getApplicationOtelServiceName(); var localOperation = "GET /s3/fault"; - var target = "::s3:::fault-bucket"; + var type = "AWS::S3::Bucket"; + var identifier = "fault-bucket"; assertSpanClientAttributes( traces, @@ -715,7 +762,8 @@ protected void doTestS3Fault() { localOperation, getS3ServiceName(), "GetObject", - target, + type, + identifier, "fault-bucket.s3.test", 8080, "http://fault-bucket.s3.test:8080", @@ -728,7 +776,8 @@ protected void doTestS3Fault() { localOperation, getS3ServiceName(), "GetObject", - target, + type, + identifier, 5000.0); assertMetricClientAttributes( metrics, @@ -737,7 +786,8 @@ protected void doTestS3Fault() { localOperation, getS3ServiceName(), "GetObject", - target, + type, + identifier, 1.0); assertMetricClientAttributes( metrics, @@ -746,7 +796,8 @@ protected void doTestS3Fault() { localOperation, getS3ServiceName(), "GetObject", - target, + type, + identifier, 0.0); } @@ -770,7 +821,8 @@ protected void doTestDynamoDbCreateTable() { var localService = getApplicationOtelServiceName(); var localOperation = "GET /ddb/createtable/:tablename"; - var target = "::dynamodb:::table/some-table"; + var type = "AWS::DynamoDB::Table"; + var identifier = "some-table"; assertSpanClientAttributes( traces, @@ -780,7 +832,8 @@ protected void doTestDynamoDbCreateTable() { localOperation, getDynamoDbServiceName(), "CreateTable", - target, + type, + identifier, "localstack", 4566, "http://localstack:4566", @@ -793,7 +846,8 @@ protected void doTestDynamoDbCreateTable() { localOperation, getDynamoDbServiceName(), "CreateTable", - target, + type, + identifier, 20000.0); assertMetricClientAttributes( metrics, @@ -802,7 +856,8 @@ protected void doTestDynamoDbCreateTable() { localOperation, getDynamoDbServiceName(), "CreateTable", - target, + type, + identifier, 0.0); assertMetricClientAttributes( metrics, @@ -811,7 +866,8 @@ protected void doTestDynamoDbCreateTable() { localOperation, getDynamoDbServiceName(), "CreateTable", - target, + type, + identifier, 0.0); } @@ -827,7 +883,8 @@ protected void doTestDynamoDbPutItem() { var localService = getApplicationOtelServiceName(); var localOperation = "GET /ddb/putitem/:tablename/:partitionkey"; - var target = "::dynamodb:::table/putitem-table"; + var type = "AWS::DynamoDB::Table"; + var identifier = "putitem-table"; assertSpanClientAttributes( traces, @@ -837,7 +894,8 @@ protected void doTestDynamoDbPutItem() { localOperation, getDynamoDbServiceName(), "PutItem", - target, + type, + identifier, "localstack", 4566, "http://localstack:4566", @@ -850,7 +908,8 @@ protected void doTestDynamoDbPutItem() { localOperation, getDynamoDbServiceName(), "PutItem", - target, + type, + identifier, 5000.0); assertMetricClientAttributes( metrics, @@ -859,7 +918,8 @@ protected void doTestDynamoDbPutItem() { localOperation, getDynamoDbServiceName(), "PutItem", - target, + type, + identifier, 0.0); assertMetricClientAttributes( metrics, @@ -868,7 +928,8 @@ protected void doTestDynamoDbPutItem() { localOperation, getDynamoDbServiceName(), "PutItem", - target, + type, + identifier, 0.0); } @@ -884,7 +945,8 @@ protected void doTestDynamoDbError() throws Exception { var localService = getApplicationOtelServiceName(); var localOperation = "GET /ddb/error"; - var target = "::dynamodb:::table/nonexistanttable"; + var type = "AWS::DynamoDB::Table"; + var identifier = "nonexistanttable"; assertSpanClientAttributes( traces, @@ -894,7 +956,8 @@ protected void doTestDynamoDbError() throws Exception { localOperation, getDynamoDbServiceName(), "PutItem", - target, + type, + identifier, "error.test", 8080, "http://error.test:8080", @@ -907,7 +970,8 @@ protected void doTestDynamoDbError() throws Exception { localOperation, getDynamoDbServiceName(), "PutItem", - target, + type, + identifier, 5000.0); assertMetricClientAttributes( metrics, @@ -916,7 +980,8 @@ protected void doTestDynamoDbError() throws Exception { localOperation, getDynamoDbServiceName(), "PutItem", - target, + type, + identifier, 0.0); assertMetricClientAttributes( metrics, @@ -925,7 +990,8 @@ protected void doTestDynamoDbError() throws Exception { localOperation, getDynamoDbServiceName(), "PutItem", - target, + type, + identifier, 1.0); } @@ -947,7 +1013,8 @@ protected void doTestDynamoDbFault() throws Exception { var localService = getApplicationOtelServiceName(); var localOperation = "GET /ddb/fault"; - var target = "::dynamodb:::table/nonexistanttable"; + var type = "AWS::DynamoDB::Table"; + var identifier = "nonexistanttable"; assertSpanClientAttributes( traces, @@ -957,7 +1024,8 @@ protected void doTestDynamoDbFault() throws Exception { localOperation, getDynamoDbServiceName(), "PutItem", - target, + type, + identifier, "fault.test", 8080, "http://fault.test:8080", @@ -970,7 +1038,8 @@ protected void doTestDynamoDbFault() throws Exception { localOperation, getDynamoDbServiceName(), "PutItem", - target, + type, + identifier, 20000.0); assertMetricClientAttributes( metrics, @@ -979,7 +1048,8 @@ protected void doTestDynamoDbFault() throws Exception { localOperation, getDynamoDbServiceName(), "PutItem", - target, + type, + identifier, 1.0); assertMetricClientAttributes( metrics, @@ -988,7 +1058,8 @@ protected void doTestDynamoDbFault() throws Exception { localOperation, getDynamoDbServiceName(), "PutItem", - target, + type, + identifier, 0.0); } @@ -1004,7 +1075,8 @@ protected void doTestSQSCreateQueue() throws Exception { var localService = getApplicationOtelServiceName(); var localOperation = "GET /sqs/createqueue/:queuename"; - var target = "::sqs:::some-queue"; + var type = "AWS::SQS::Queue"; + var identifier = "some-queue"; assertSpanClientAttributes( traces, @@ -1014,7 +1086,8 @@ protected void doTestSQSCreateQueue() throws Exception { localOperation, getSqsServiceName(), "CreateQueue", - target, + type, + identifier, "localstack", 4566, "http://localstack:4566", @@ -1027,7 +1100,8 @@ protected void doTestSQSCreateQueue() throws Exception { localOperation, getSqsServiceName(), "CreateQueue", - target, + type, + identifier, 5000.0); assertMetricClientAttributes( metrics, @@ -1036,7 +1110,8 @@ protected void doTestSQSCreateQueue() throws Exception { localOperation, getSqsServiceName(), "CreateQueue", - target, + type, + identifier, 0.0); assertMetricClientAttributes( metrics, @@ -1045,7 +1120,8 @@ protected void doTestSQSCreateQueue() throws Exception { localOperation, getSqsServiceName(), "CreateQueue", - target, + type, + identifier, 0.0); } @@ -1062,7 +1138,8 @@ protected void doTestSQSSendMessage() throws Exception { var localService = getApplicationOtelServiceName(); var localOperation = "GET /sqs/publishqueue/:queuename"; // SendMessage does not capture aws.queue.name - String target = null; + String type = null; + String identifier = null; assertSpanProducerAttributes( traces, @@ -1072,7 +1149,8 @@ protected void doTestSQSSendMessage() throws Exception { localOperation, getSqsServiceName(), "SendMessage", - target, + type, + identifier, "localstack", 4566, "http://localstack:4566", @@ -1086,7 +1164,8 @@ protected void doTestSQSSendMessage() throws Exception { localOperation, getSqsServiceName(), "SendMessage", - target, + type, + identifier, 5000.0); assertMetricProducerAttributes( metrics, @@ -1095,7 +1174,8 @@ protected void doTestSQSSendMessage() throws Exception { localOperation, getSqsServiceName(), "SendMessage", - target, + type, + identifier, 0.0); assertMetricProducerAttributes( metrics, @@ -1104,7 +1184,8 @@ protected void doTestSQSSendMessage() throws Exception { localOperation, getSqsServiceName(), "SendMessage", - target, + type, + identifier, 0.0); } @@ -1125,7 +1206,8 @@ protected void doTestSQSReceiveMessage() throws Exception { var localService = getApplicationOtelServiceName(); var localOperation = "InternalOperation"; // ReceiveMessage does not capture aws.queue.name - String target = null; + String type = null; + String identifier = null; // Consumer traces for SQS behave like a Server span (they create the local aws service // attributes), but have RPC attributes like a client span. assertSpanConsumerAttributes( @@ -1148,7 +1230,8 @@ protected void doTestSQSReceiveMessage() throws Exception { localOperation, getSqsServiceName(), "ReceiveMessage", - target, + type, + identifier, 5000.0); assertMetricConsumerAttributes( metrics, @@ -1157,7 +1240,8 @@ protected void doTestSQSReceiveMessage() throws Exception { localOperation, getSqsServiceName(), "ReceiveMessage", - target, + type, + identifier, 0.0); } @@ -1174,7 +1258,8 @@ protected void doTestSQSError() throws Exception { var localService = getApplicationOtelServiceName(); var localOperation = "GET /sqs/error"; // SendMessage does not capture aws.queue.name - String target = null; + String type = null; + String identifier = null; assertSpanProducerAttributes( traces, @@ -1184,7 +1269,8 @@ protected void doTestSQSError() throws Exception { localOperation, getSqsServiceName(), "SendMessage", - target, + type, + identifier, "error.test", 8080, "http://error.test:8080", @@ -1198,7 +1284,8 @@ protected void doTestSQSError() throws Exception { localOperation, getSqsServiceName(), "SendMessage", - target, + type, + identifier, 5000.0); assertMetricProducerAttributes( metrics, @@ -1207,7 +1294,8 @@ protected void doTestSQSError() throws Exception { localOperation, getSqsServiceName(), "SendMessage", - target, + type, + identifier, 0.0); assertMetricProducerAttributes( metrics, @@ -1216,7 +1304,8 @@ protected void doTestSQSError() throws Exception { localOperation, getSqsServiceName(), "SendMessage", - target, + type, + identifier, 1.0); } @@ -1233,7 +1322,8 @@ protected void doTestSQSFault() throws Exception { var localService = getApplicationOtelServiceName(); var localOperation = "GET /sqs/fault"; // SendMessage does not capture aws.queue.name - String target = null; + String type = null; + String identifier = null; assertSpanProducerAttributes( traces, @@ -1243,7 +1333,8 @@ protected void doTestSQSFault() throws Exception { localOperation, getSqsServiceName(), "SendMessage", - target, + type, + identifier, "fault.test", 8080, "http://fault.test:8080", @@ -1257,7 +1348,8 @@ protected void doTestSQSFault() throws Exception { localOperation, getSqsServiceName(), "SendMessage", - target, + type, + identifier, 5000.0); assertMetricProducerAttributes( metrics, @@ -1266,7 +1358,8 @@ protected void doTestSQSFault() throws Exception { localOperation, getSqsServiceName(), "SendMessage", - target, + type, + identifier, 1.0); assertMetricProducerAttributes( metrics, @@ -1275,7 +1368,8 @@ protected void doTestSQSFault() throws Exception { localOperation, getSqsServiceName(), "SendMessage", - target, + type, + identifier, 0.0); } @@ -1291,7 +1385,8 @@ protected void doTestKinesisPutRecord() throws Exception { var localService = getApplicationOtelServiceName(); var localOperation = "GET /kinesis/putrecord/:streamname"; - var target = "::kinesis:::stream/my-stream"; + var type = "AWS::Kinesis::Stream"; + var identifier = "my-stream"; assertSpanClientAttributes( traces, @@ -1301,7 +1396,8 @@ protected void doTestKinesisPutRecord() throws Exception { localOperation, getKinesisServiceName(), "PutRecord", - target, + type, + identifier, "localstack", 4566, "http://localstack:4566", @@ -1314,7 +1410,8 @@ protected void doTestKinesisPutRecord() throws Exception { localOperation, getKinesisServiceName(), "PutRecord", - target, + type, + identifier, 5000.0); assertMetricClientAttributes( metrics, @@ -1323,7 +1420,8 @@ protected void doTestKinesisPutRecord() throws Exception { localOperation, getKinesisServiceName(), "PutRecord", - target, + type, + identifier, 0.0); assertMetricClientAttributes( metrics, @@ -1332,7 +1430,8 @@ protected void doTestKinesisPutRecord() throws Exception { localOperation, getKinesisServiceName(), "PutRecord", - target, + type, + identifier, 0.0); } @@ -1348,7 +1447,8 @@ protected void doTestKinesisError() throws Exception { var localService = getApplicationOtelServiceName(); var localOperation = "GET /kinesis/error"; - var target = "::kinesis:::stream/nonexistantstream"; + var type = "AWS::Kinesis::Stream"; + var identifier = "nonexistantstream"; assertSpanClientAttributes( traces, @@ -1358,7 +1458,8 @@ protected void doTestKinesisError() throws Exception { localOperation, getKinesisServiceName(), "PutRecord", - target, + type, + identifier, "error.test", 8080, "http://error.test:8080", @@ -1372,7 +1473,8 @@ protected void doTestKinesisError() throws Exception { localOperation, getKinesisServiceName(), "PutRecord", - target, + type, + identifier, 5000.0); assertMetricClientAttributes( metrics, @@ -1381,7 +1483,8 @@ protected void doTestKinesisError() throws Exception { localOperation, getKinesisServiceName(), "PutRecord", - target, + type, + identifier, 0.0); assertMetricClientAttributes( metrics, @@ -1390,7 +1493,8 @@ protected void doTestKinesisError() throws Exception { localOperation, getKinesisServiceName(), "PutRecord", - target, + type, + identifier, 1.0); } @@ -1406,7 +1510,8 @@ protected void doTestKinesisFault() throws Exception { var localService = getApplicationOtelServiceName(); var localOperation = "GET /kinesis/fault"; - var target = "::kinesis:::stream/faultstream"; + var type = "AWS::Kinesis::Stream"; + var identifier = "faultstream"; assertSpanClientAttributes( traces, @@ -1416,7 +1521,8 @@ protected void doTestKinesisFault() throws Exception { localOperation, getKinesisServiceName(), "PutRecord", - target, + type, + identifier, "fault.test", 8080, "http://fault.test:8080", @@ -1429,7 +1535,8 @@ protected void doTestKinesisFault() throws Exception { localOperation, getKinesisServiceName(), "PutRecord", - target, + type, + identifier, 5000.0); assertMetricClientAttributes( metrics, @@ -1438,7 +1545,8 @@ protected void doTestKinesisFault() throws Exception { localOperation, getKinesisServiceName(), "PutRecord", - target, + type, + identifier, 1.0); assertMetricClientAttributes( metrics, @@ -1447,7 +1555,8 @@ protected void doTestKinesisFault() throws Exception { localOperation, getKinesisServiceName(), "PutRecord", - target, + type, + identifier, 0.0); } } 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 1730b83a5c..b2be003658 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 @@ -57,26 +57,6 @@ protected String getKinesisSpanNamePrefix() { return "Kinesis"; } - @Override - protected String getS3ServiceName() { - return "AWS.SDK.Amazon S3"; - } - - @Override - protected String getDynamoDbServiceName() { - return "AWS.SDK.AmazonDynamoDBv2"; - } - - @Override - protected String getSqsServiceName() { - return "AWS.SDK.AmazonSQS"; - } - - @Override - protected String getKinesisServiceName() { - return "AWS.SDK.AmazonKinesis"; - } - protected String getS3RpcServiceName() { return "Amazon S3"; } 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 b6c6d500fe..1e99836d6b 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 @@ -56,25 +56,6 @@ protected String getKinesisSpanNamePrefix() { return "Kinesis"; } - @Override - protected String getS3ServiceName() { - return "AWS.SDK.S3"; - } - - @Override - protected String getDynamoDbServiceName() { - return "AWS.SDK.DynamoDb"; - } - - @Override - protected String getSqsServiceName() { - return "AWS.SDK.Sqs"; - } - - protected String getKinesisServiceName() { - return "AWS.SDK.Kinesis"; - } - @Override protected String getS3RpcServiceName() { return "S3"; diff --git a/appsignals-tests/contract-tests/src/test/java/software/amazon/opentelemetry/appsignals/test/base/ContractTestBase.java b/appsignals-tests/contract-tests/src/test/java/software/amazon/opentelemetry/appsignals/test/base/ContractTestBase.java index 5e81f7ddce..16bd1af119 100644 --- a/appsignals-tests/contract-tests/src/test/java/software/amazon/opentelemetry/appsignals/test/base/ContractTestBase.java +++ b/appsignals-tests/contract-tests/src/test/java/software/amazon/opentelemetry/appsignals/test/base/ContractTestBase.java @@ -78,11 +78,11 @@ public abstract class ContractTestBase { .waitingFor(getApplicationWaitCondition()) .withEnv("JAVA_TOOL_OPTIONS", "-javaagent:/opentelemetry-javaagent-all.jar") .withEnv("OTEL_METRIC_EXPORT_INTERVAL", "100") // 100 ms - .withEnv("OTEL_AWS_APP_SIGNALS_ENABLED", "true") + .withEnv("OTEL_AWS_APPLICATION_SIGNALS_ENABLED", "true") .withEnv("OTEL_METRICS_EXPORTER", "none") .withEnv("OTEL_BSP_SCHEDULE_DELAY", "0") // Don't wait to export spans to the collector .withEnv( - "OTEL_AWS_APP_SIGNALS_EXPORTER_ENDPOINT", + "OTEL_AWS_APPLICATION_SIGNALS_EXPORTER_ENDPOINT", "http://" + COLLECTOR_HOSTNAME + ":" + COLLECTOR_PORT) .withEnv( "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT", diff --git a/appsignals-tests/contract-tests/src/test/java/software/amazon/opentelemetry/appsignals/test/utils/AppSignalsConstants.java b/appsignals-tests/contract-tests/src/test/java/software/amazon/opentelemetry/appsignals/test/utils/AppSignalsConstants.java index 425679efcc..74138cac73 100644 --- a/appsignals-tests/contract-tests/src/test/java/software/amazon/opentelemetry/appsignals/test/utils/AppSignalsConstants.java +++ b/appsignals-tests/contract-tests/src/test/java/software/amazon/opentelemetry/appsignals/test/utils/AppSignalsConstants.java @@ -29,6 +29,7 @@ public class AppSignalsConstants { public static final String AWS_LOCAL_OPERATION = "aws.local.operation"; public static final String AWS_REMOTE_SERVICE = "aws.remote.service"; public static final String AWS_REMOTE_OPERATION = "aws.remote.operation"; - public static final String AWS_REMOTE_TARGET = "aws.remote.target"; + public static final String AWS_REMOTE_RESOURCE_TYPE = "aws.remote.resource.type"; + public static final String AWS_REMOTE_RESOURCE_IDENTIFIER = "aws.remote.resource.identifier"; public static final String AWS_SPAN_KIND = "aws.span.kind"; } diff --git a/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsAppSignalsCustomizerProvider.java b/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsAppSignalsCustomizerProvider.java index 8cc54add9f..9e61275024 100644 --- a/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsAppSignalsCustomizerProvider.java +++ b/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsAppSignalsCustomizerProvider.java @@ -52,14 +52,24 @@ * * *

You can control when these customizations are applied using the property - * otel.aws.app.signals.enabled or the environment variable OTEL_AWS_APP_SIGNALS_ENABLED. This flag - * is disabled by default. + * otel.aws.application.signals.enabled or the environment variable + * OTEL_AWS_APPLICATION_SIGNALS_ENABLED. This flag is disabled by default. */ public class AwsAppSignalsCustomizerProvider implements AutoConfigurationCustomizerProvider { private static final Duration DEFAULT_METRIC_EXPORT_INTERVAL = Duration.ofMinutes(1); private static final Logger logger = Logger.getLogger(AwsAppSignalsCustomizerProvider.class.getName()); + private static final String SMP_ENABLED_CONFIG = "otel.smp.enabled"; + private static final String APP_SIGNALS_ENABLED_CONFIG = "otel.aws.app.signals.enabled"; + private static final String APPLICATION_SIGNALS_ENABLED_CONFIG = + "otel.aws.application.signals.enabled"; + private static final String SMP_EXPORTER_ENDPOINT_CONFIG = "otel.aws.smp.exporter.endpoint"; + private static final String APP_SIGNALS_EXPORTER_ENDPOINT_CONFIG = + "otel.aws.app.signals.exporter.endpoint"; + private static final String APPLICATION_SIGNALS_EXPORTER_ENDPOINT_CONFIG = + "otel.aws.application.signals.exporter.endpoint"; + public void customize(AutoConfigurationCustomizer autoConfiguration) { autoConfiguration.addSamplerCustomizer(this::customizeSampler); autoConfiguration.addTracerProviderCustomizer(this::customizeTracerProviderBuilder); @@ -68,7 +78,9 @@ public void customize(AutoConfigurationCustomizer autoConfiguration) { private boolean isAppSignalsEnabled(ConfigProperties configProps) { return configProps.getBoolean( - "otel.aws.app.signals.enabled", configProps.getBoolean("otel.smp.enabled", false)); + APPLICATION_SIGNALS_ENABLED_CONFIG, + configProps.getBoolean( + APP_SIGNALS_ENABLED_CONFIG, configProps.getBoolean(SMP_ENABLED_CONFIG, false))); } private Sampler customizeSampler(Sampler sampler, ConfigProperties configProps) { @@ -141,9 +153,10 @@ public MetricExporter createExporter(ConfigProperties configProps) { if (protocol.equals(OtlpConfigUtil.PROTOCOL_HTTP_PROTOBUF)) { appSignalsEndpoint = configProps.getString( - "otel.aws.app.signals.exporter.endpoint", + APPLICATION_SIGNALS_EXPORTER_ENDPOINT_CONFIG, configProps.getString( - "otel.aws.smp.exporter.endpoint", "http://localhost:4316/v1/metrics")); + APP_SIGNALS_EXPORTER_ENDPOINT_CONFIG, + configProps.getString(SMP_EXPORTER_ENDPOINT_CONFIG, "http://localhost:4316/v1/metrics"))); logger.log(Level.FINE, String.format("AppSignals export endpoint: %s", appSignalsEndpoint)); return OtlpHttpMetricExporter.builder() .setEndpoint(appSignalsEndpoint) @@ -153,8 +166,10 @@ public MetricExporter createExporter(ConfigProperties configProps) { } else if (protocol.equals(OtlpConfigUtil.PROTOCOL_GRPC)) { appSignalsEndpoint = configProps.getString( - "otel.aws.app.signals.exporter.endpoint", - configProps.getString("otel.aws.smp.exporter.endpoint", "http://localhost:4315")); + APPLICATION_SIGNALS_EXPORTER_ENDPOINT_CONFIG, + configProps.getString( + APP_SIGNALS_EXPORTER_ENDPOINT_CONFIG, + configProps.getString(SMP_EXPORTER_ENDPOINT_CONFIG, "http://localhost:4315"))); logger.log(Level.FINE, String.format("AppSignals export endpoint: %s", appSignalsEndpoint)); return OtlpGrpcMetricExporter.builder() .setEndpoint(appSignalsEndpoint) diff --git a/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsAttributeKeys.java b/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsAttributeKeys.java index 1126dabae5..5859b9854b 100644 --- a/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsAttributeKeys.java +++ b/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsAttributeKeys.java @@ -35,21 +35,25 @@ private AwsAttributeKeys() {} static final AttributeKey AWS_REMOTE_OPERATION = AttributeKey.stringKey("aws.remote.operation"); - static final AttributeKey AWS_REMOTE_TARGET = AttributeKey.stringKey("aws.remote.target"); + static final AttributeKey AWS_REMOTE_RESOURCE_IDENTIFIER = + AttributeKey.stringKey("aws.remote.resource.identifier"); + + static final AttributeKey AWS_REMOTE_RESOURCE_TYPE = + AttributeKey.stringKey("aws.remote.resource.type"); static final AttributeKey AWS_SDK_DESCENDANT = AttributeKey.stringKey("aws.sdk.descendant"); + static final AttributeKey AWS_CONSUMER_PARENT_SPAN_KIND = + AttributeKey.stringKey("aws.consumer.parent.span.kind"); + // use the same AWS Resource attribute name defined by OTel java auto-instr for aws_sdk_v_1_1 // TODO: all AWS specific attributes should be defined in semconv package and reused cross all // otel packages. Related sim - // https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/8710 - static final AttributeKey AWS_BUCKET_NAME = AttributeKey.stringKey("aws.bucket.name"); static final AttributeKey AWS_QUEUE_URL = AttributeKey.stringKey("aws.queue.url"); static final AttributeKey AWS_QUEUE_NAME = AttributeKey.stringKey("aws.queue.name"); static final AttributeKey AWS_STREAM_NAME = AttributeKey.stringKey("aws.stream.name"); static final AttributeKey AWS_TABLE_NAME = AttributeKey.stringKey("aws.table.name"); - static final AttributeKey AWS_CONSUMER_PARENT_SPAN_KIND = - AttributeKey.stringKey("aws.consumer.parent.span.kind"); } diff --git a/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsMetricAttributeGenerator.java b/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsMetricAttributeGenerator.java index ad0c97f97b..34b979597a 100644 --- a/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsMetricAttributeGenerator.java +++ b/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsMetricAttributeGenerator.java @@ -40,8 +40,9 @@ import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_QUEUE_NAME; import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_QUEUE_URL; import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_REMOTE_OPERATION; +import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_REMOTE_RESOURCE_IDENTIFIER; +import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_REMOTE_RESOURCE_TYPE; import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_REMOTE_SERVICE; -import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_REMOTE_TARGET; import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_SPAN_KIND; import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_STREAM_NAME; import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_TABLE_NAME; @@ -88,6 +89,12 @@ final class AwsMetricAttributeGenerator implements MetricAttributeGenerator { private static final Logger logger = Logger.getLogger(AwsMetricAttributeGenerator.class.getName()); + // Normalized remote service names for supported AWS services + private static final String NORMALIZED_DYNAMO_DB_SERVICE_NAME = "AWS::DynamoDB"; + private static final String NORMALIZED_KINESIS_SERVICE_NAME = "AWS::Kinesis"; + private static final String NORMALIZED_S3_SERVICE_NAME = "AWS::S3"; + private static final String NORMALIZED_SQS_SERVICE_NAME = "AWS::SQS"; + // Special DEPENDENCY attribute value if GRAPHQL_OPERATION_TYPE attribute key is present. private static final String GRAPHQL = "graphql"; @@ -129,51 +136,13 @@ private Attributes generateDependencyMetricAttributes(SpanData span, Resource re setService(resource, span, builder); setEgressOperation(span, builder); setRemoteServiceAndOperation(span, builder); - setRemoteTarget(span, builder); + setRemoteResourceTypeAndIdentifier(span, builder); setSpanKindForDependency(span, builder); setHttpStatus(span, builder); return builder.build(); } - private static void setRemoteTarget(SpanData span, AttributesBuilder builder) { - Optional remoteTarget = getRemoteTarget(span); - remoteTarget.ifPresent(s -> builder.put(AWS_REMOTE_TARGET, s)); - } - - /** - * RemoteTarget attribute {@link AwsAttributeKeys#AWS_REMOTE_TARGET} is used to store the resource - * name of the remote invokes, such as S3 bucket name, mysql table name, etc. TODO: currently only - * support AWS resource name, will be extended to support the general remote targets, such as - * ActiveMQ name, etc. - */ - private static Optional getRemoteTarget(SpanData span) { - if (isKeyPresent(span, AWS_BUCKET_NAME)) { - return Optional.ofNullable("::s3:::" + span.getAttributes().get(AWS_BUCKET_NAME)); - } - - if (isKeyPresent(span, AWS_QUEUE_URL)) { - String arn = SqsUrlParser.getSqsRemoteTarget(span.getAttributes().get(AWS_QUEUE_URL)); - - if (arn != null) { - return Optional.ofNullable(arn); - } - } - - if (isKeyPresent(span, AWS_QUEUE_NAME)) { - return Optional.ofNullable("::sqs:::" + span.getAttributes().get(AWS_QUEUE_NAME)); - } - - if (isKeyPresent(span, AWS_STREAM_NAME)) { - return Optional.ofNullable("::kinesis:::stream/" + span.getAttributes().get(AWS_STREAM_NAME)); - } - - if (isKeyPresent(span, AWS_TABLE_NAME)) { - return Optional.ofNullable("::dynamodb:::table/" + span.getAttributes().get(AWS_TABLE_NAME)); - } - return Optional.empty(); - } - /** Service is always derived from {@link ResourceAttributes#SERVICE_NAME} */ private static void setService(Resource resource, SpanData span, AttributesBuilder builder) { String service = resource.getAttribute(SERVICE_NAME); @@ -213,14 +182,6 @@ private static void setEgressOperation(SpanData span, AttributesBuilder builder) builder.put(AWS_LOCAL_OPERATION, operation); } - // add `AWS.SDK.` as prefix to indicate the metrics resulted from current span is from AWS SDK - private static String normalizeServiceName(SpanData span, String serviceName) { - if (AwsSpanProcessingUtil.isAwsSDKSpan(span)) { - return "AWS.SDK." + serviceName; - } - return serviceName; - } - /** * Remote attributes (only for Client and Producer spans) are generated based on low-cardinality * span attributes, in priority order. @@ -265,7 +226,7 @@ private static void setRemoteServiceAndOperation(SpanData span, AttributesBuilde remoteService = getRemoteService(span, AWS_REMOTE_SERVICE); remoteOperation = getRemoteOperation(span, AWS_REMOTE_OPERATION); } else if (isKeyPresent(span, RPC_SERVICE) || isKeyPresent(span, RPC_METHOD)) { - remoteService = normalizeServiceName(span, getRemoteService(span, RPC_SERVICE)); + remoteService = normalizeRemoteServiceName(span, getRemoteService(span, RPC_SERVICE)); remoteOperation = getRemoteOperation(span, RPC_METHOD); } else if (isKeyPresent(span, DB_SYSTEM) || isKeyPresent(span, DB_OPERATION) @@ -365,6 +326,72 @@ private static String generateRemoteService(SpanData span) { return remoteService; } + /** + * If the span is an AWS SDK span, normalize the name to align with AWS + * Cloud Control resource format as much as possible, with special attention to services we + * can detect remote resource information for. Long term, we would like to normalize service name + * in the upstream. + */ + private static String normalizeRemoteServiceName(SpanData span, String serviceName) { + if (AwsSpanProcessingUtil.isAwsSDKSpan(span)) { + switch (serviceName) { + case "AmazonDynamoDBv2": // AWS SDK v1 + case "DynamoDb": // AWS SDK v2 + return NORMALIZED_DYNAMO_DB_SERVICE_NAME; + case "AmazonKinesis": // AWS SDK v1 + case "Kinesis": // AWS SDK v2 + return NORMALIZED_KINESIS_SERVICE_NAME; + case "Amazon S3": // AWS SDK v1 + case "S3": // AWS SDK v2 + return NORMALIZED_S3_SERVICE_NAME; + case "AmazonSQS": // AWS SDK v1 + case "Sqs": // AWS SDK v2 + return NORMALIZED_SQS_SERVICE_NAME; + default: + return "AWS::" + serviceName; + } + } + return serviceName; + } + + /** + * Remote resource attributes {@link AwsAttributeKeys#AWS_REMOTE_RESOURCE_TYPE} and {@link + * AwsAttributeKeys#AWS_REMOTE_RESOURCE_IDENTIFIER} are used to store information about the + * resource associated with the remote invocation, such as S3 bucket name, etc. We should only + * ever set both type and identifier or neither. + * + *

AWS resources type and identifier adhere to AWS + * Cloud Control resource format. + */ + private static void setRemoteResourceTypeAndIdentifier(SpanData span, AttributesBuilder builder) { + Optional remoteResourceType = Optional.empty(); + Optional remoteResourceIdentifier = Optional.empty(); + + if (isKeyPresent(span, AWS_TABLE_NAME)) { + remoteResourceType = Optional.of(NORMALIZED_DYNAMO_DB_SERVICE_NAME + "::Table"); + remoteResourceIdentifier = Optional.ofNullable(span.getAttributes().get(AWS_TABLE_NAME)); + } else if (isKeyPresent(span, AWS_STREAM_NAME)) { + remoteResourceType = Optional.of(NORMALIZED_KINESIS_SERVICE_NAME + "::Stream"); + remoteResourceIdentifier = Optional.ofNullable(span.getAttributes().get(AWS_STREAM_NAME)); + } else if (isKeyPresent(span, AWS_BUCKET_NAME)) { + remoteResourceType = Optional.of(NORMALIZED_S3_SERVICE_NAME + "::Bucket"); + remoteResourceIdentifier = Optional.ofNullable(span.getAttributes().get(AWS_BUCKET_NAME)); + } else if (isKeyPresent(span, AWS_QUEUE_NAME)) { + remoteResourceType = Optional.of(NORMALIZED_SQS_SERVICE_NAME + "::Queue"); + remoteResourceIdentifier = Optional.ofNullable(span.getAttributes().get(AWS_QUEUE_NAME)); + } else if (isKeyPresent(span, AWS_QUEUE_URL)) { + remoteResourceType = Optional.of(NORMALIZED_SQS_SERVICE_NAME + "::Queue"); + remoteResourceIdentifier = SqsUrlParser.getQueueName(span.getAttributes().get(AWS_QUEUE_URL)); + } + + if (remoteResourceType.isPresent() && remoteResourceIdentifier.isPresent()) { + builder.put(AWS_REMOTE_RESOURCE_TYPE, remoteResourceType.get()); + builder.put(AWS_REMOTE_RESOURCE_IDENTIFIER, remoteResourceIdentifier.get()); + } + } + /** Span kind is needed for differentiating metrics in the EMF exporter */ private static void setSpanKindForService(SpanData span, AttributesBuilder builder) { String spanKind = span.getKind().name(); diff --git a/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/SqsUrlParser.java b/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/SqsUrlParser.java index 7620bf88dd..e69ccb091a 100644 --- a/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/SqsUrlParser.java +++ b/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/SqsUrlParser.java @@ -15,108 +15,33 @@ package software.amazon.opentelemetry.javaagent.providers; +import java.util.Optional; + public class SqsUrlParser { - private static final char ARN_DELIMETER = ':'; private static final String HTTP_SCHEMA = "http://"; private static final String HTTPS_SCHEMA = "https://"; - public static String getSqsRemoteTarget(String sqsUrl) { - sqsUrl = stripSchemaFromUrl(sqsUrl); - - if (!isSqsUrl(sqsUrl) && !isLegacySqsUrl(sqsUrl) && !isCustomUrl(sqsUrl)) { - return null; - } - - String region = getRegion(sqsUrl); - String accountId = getAccountId(sqsUrl); - String partition = getPartition(sqsUrl); - String queueName = getQueueName(sqsUrl); - - StringBuilder remoteTarget = new StringBuilder(); - - if (region == null && accountId == null && partition == null && queueName == null) { - return null; - } - - if (region != null && accountId != null && partition != null && queueName != null) { - remoteTarget.append("arn"); - } - - remoteTarget - .append(ARN_DELIMETER) - .append(nullToEmpty(partition)) - .append(ARN_DELIMETER) - .append("sqs") - .append(ARN_DELIMETER) - .append(nullToEmpty(region)) - .append(ARN_DELIMETER) - .append(nullToEmpty(accountId)) - .append(ARN_DELIMETER) - .append(queueName); - - return remoteTarget.toString(); - } - - private static String stripSchemaFromUrl(String url) { - return url.replace(HTTP_SCHEMA, "").replace(HTTPS_SCHEMA, ""); - } - - private static String getRegion(String sqsUrl) { - if (sqsUrl == null) { - return null; - } - - if (sqsUrl.startsWith("queue.amazonaws.com/")) { - return "us-east-1"; - } else if (isSqsUrl(sqsUrl)) { - return getRegionFromSqsUrl(sqsUrl); - } else if (isLegacySqsUrl(sqsUrl)) { - return getRegionFromLegacySqsUrl(sqsUrl); - } else { - return null; - } - } - - private static boolean isSqsUrl(String sqsUrl) { - String[] split = sqsUrl.split("/"); - - return split.length == 3 - && split[0].startsWith("sqs.") - && split[0].endsWith(".amazonaws.com") - && isAccountId(split[1]) - && isValidQueueName(split[2]); - } - - private static boolean isLegacySqsUrl(String sqsUrl) { - String[] split = sqsUrl.split("/"); - - return split.length == 3 - && split[0].endsWith(".queue.amazonaws.com") - && isAccountId(split[1]) - && isValidQueueName(split[2]); - } - - private static boolean isCustomUrl(String sqsUrl) { - String[] split = sqsUrl.split("/"); - return split.length == 3 && isAccountId(split[1]) && isValidQueueName(split[2]); - } - - private static boolean isValidQueueName(String input) { - if (input.length() == 0 || input.length() > 80) { - return false; + /** + * Best-effort logic to extract queue name from an HTTP url. This method should only be used with + * a string that is, with reasonably high confidence, an SQS queue URL. Handles new/legacy/some + * custom URLs. Essentially, we require that the URL should have exactly three parts, delimited by + * /'s (excluding schema), the second part should be a 12-digit account id, and the third part + * should be a valid queue name, per SQS naming conventions. + */ + public static Optional getQueueName(String url) { + if (url == null) { + return Optional.empty(); } - - for (Character c : input.toCharArray()) { - if (c != '_' && c != '-' && !Character.isAlphabetic(c) && !Character.isDigit(c)) { - return false; - } + url = url.replace(HTTP_SCHEMA, "").replace(HTTPS_SCHEMA, ""); + String[] splitUrl = url.split("/"); + if (splitUrl.length == 3 && isAccountId(splitUrl[1]) && isValidQueueName(splitUrl[2])) { + return Optional.of(splitUrl[2]); } - - return true; + return Optional.empty(); } private static boolean isAccountId(String input) { - if (input.length() != 12) { + if (input == null || input.length() != 12) { return false; } @@ -129,61 +54,17 @@ private static boolean isAccountId(String input) { return true; } - private static String getRegionFromSqsUrl(String sqsUrl) { - String[] split = sqsUrl.split("\\."); - - if (split.length >= 2) { - return split[1]; - } - - return null; - } - - private static String getRegionFromLegacySqsUrl(String sqsUrl) { - String[] split = sqsUrl.split("\\."); - return split[0]; - } - - private static String getAccountId(String sqsUrl) { - if (sqsUrl == null) { - return null; - } - - String[] split = sqsUrl.split("/"); - if (split.length >= 2) { - return split[1]; - } - - return null; - } - - private static String getPartition(String sqsUrl) { - String region = getRegion(sqsUrl); - - if (region == null) { - return null; - } - - if (region.startsWith("us-gov-")) { - return "aws-us-gov"; - } else if (region.startsWith("cn-")) { - return "aws-cn"; - } else { - return "aws"; + private static boolean isValidQueueName(String input) { + if (input == null || input.length() == 0 || input.length() > 80) { + return false; } - } - private static String getQueueName(String sqsUrl) { - String[] split = sqsUrl.split("/"); - - if (split.length >= 3) { - return split[2]; + for (Character c : input.toCharArray()) { + if (c != '_' && c != '-' && !Character.isAlphabetic(c) && !Character.isDigit(c)) { + return false; + } } - return null; - } - - private static String nullToEmpty(String input) { - return input == null ? "" : input; + return true; } } diff --git a/awsagentprovider/src/test/java/software/amazon/opentelemetry/javaagent/providers/AwsMetricAttributeGeneratorTest.java b/awsagentprovider/src/test/java/software/amazon/opentelemetry/javaagent/providers/AwsMetricAttributeGeneratorTest.java index f3d813ecfd..f07b1c04a8 100644 --- a/awsagentprovider/src/test/java/software/amazon/opentelemetry/javaagent/providers/AwsMetricAttributeGeneratorTest.java +++ b/awsagentprovider/src/test/java/software/amazon/opentelemetry/javaagent/providers/AwsMetricAttributeGeneratorTest.java @@ -27,8 +27,9 @@ import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_QUEUE_NAME; import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_QUEUE_URL; import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_REMOTE_OPERATION; +import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_REMOTE_RESOURCE_IDENTIFIER; +import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_REMOTE_RESOURCE_TYPE; import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_REMOTE_SERVICE; -import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_REMOTE_TARGET; import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_SPAN_KIND; import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_STREAM_NAME; import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_TABLE_NAME; @@ -652,131 +653,43 @@ public void testPeerServiceDoesNotOverrideAwsRemoteService() { } @Test - public void testClientSpanWithRemoteTargetAttributes() { + public void testClientSpanWithRemoteResourceAttributes() { // Validate behaviour of aws bucket name attribute, then remove it. mockAttribute(AWS_BUCKET_NAME, "aws_s3_bucket_name"); - validateRemoteTargetAttributes(AWS_REMOTE_TARGET, "::s3:::aws_s3_bucket_name"); + validateRemoteResourceAttributes("AWS::S3::Bucket", "aws_s3_bucket_name"); mockAttribute(AWS_BUCKET_NAME, null); // Validate behaviour of AWS_QUEUE_NAME attribute, then remove it. mockAttribute(AWS_QUEUE_NAME, "aws_queue_name"); - validateRemoteTargetAttributes(AWS_REMOTE_TARGET, "::sqs:::aws_queue_name"); + validateRemoteResourceAttributes("AWS::SQS::Queue", "aws_queue_name"); mockAttribute(AWS_QUEUE_NAME, null); // Validate behaviour of having both AWS_QUEUE_NAME and AWS_QUEUE_URL attribute, then remove - // them. + // them. Queue name is more reliable than queue URL, so we prefer to use name over URL. mockAttribute(AWS_QUEUE_URL, "https://sqs.us-east-2.amazonaws.com/123456789012/Queue"); mockAttribute(AWS_QUEUE_NAME, "aws_queue_name"); - validateRemoteTargetAttributes(AWS_REMOTE_TARGET, "arn:aws:sqs:us-east-2:123456789012:Queue"); + validateRemoteResourceAttributes("AWS::SQS::Queue", "aws_queue_name"); mockAttribute(AWS_QUEUE_URL, null); mockAttribute(AWS_QUEUE_NAME, null); // Valid queue name with invalid queue URL, we should default to using the queue name. mockAttribute(AWS_QUEUE_URL, "invalidUrl"); mockAttribute(AWS_QUEUE_NAME, "aws_queue_name"); - validateRemoteTargetAttributes(AWS_REMOTE_TARGET, "::sqs:::aws_queue_name"); + validateRemoteResourceAttributes("AWS::SQS::Queue", "aws_queue_name"); mockAttribute(AWS_QUEUE_URL, null); mockAttribute(AWS_QUEUE_NAME, null); // Validate behaviour of AWS_STREAM_NAME attribute, then remove it. mockAttribute(AWS_STREAM_NAME, "aws_stream_name"); - validateRemoteTargetAttributes(AWS_REMOTE_TARGET, "::kinesis:::stream/aws_stream_name"); + validateRemoteResourceAttributes("AWS::Kinesis::Stream", "aws_stream_name"); mockAttribute(AWS_STREAM_NAME, null); // Validate behaviour of AWS_TABLE_NAME attribute, then remove it. mockAttribute(AWS_TABLE_NAME, "aws_table_name"); - validateRemoteTargetAttributes(AWS_REMOTE_TARGET, "::dynamodb:::table/aws_table_name"); + validateRemoteResourceAttributes("AWS::DynamoDB::Table", "aws_table_name"); mockAttribute(AWS_TABLE_NAME, null); } - @Test - public void testSqsClientSpanBasicUrls() { - testSqsUrl( - "https://sqs.us-east-1.amazonaws.com/123412341234/Q_Name-5", - "arn:aws:sqs:us-east-1:123412341234:Q_Name-5"); - testSqsUrl( - "https://sqs.af-south-1.amazonaws.com/999999999999/-_ThisIsValid", - "arn:aws:sqs:af-south-1:999999999999:-_ThisIsValid"); - testSqsUrl( - "http://sqs.eu-west-3.amazonaws.com/000000000000/FirstQueue", - "arn:aws:sqs:eu-west-3:000000000000:FirstQueue"); - testSqsUrl( - "sqs.sa-east-1.amazonaws.com/123456781234/SecondQueue", - "arn:aws:sqs:sa-east-1:123456781234:SecondQueue"); - } - - @Test - public void testSqsClientSpanUsGovUrls() { - testSqsUrl( - "https://sqs.us-gov-east-1.amazonaws.com/123456789012/MyQueue", - "arn:aws-us-gov:sqs:us-gov-east-1:123456789012:MyQueue"); - testSqsUrl( - "sqs.us-gov-west-1.amazonaws.com/112233445566/Queue", - "arn:aws-us-gov:sqs:us-gov-west-1:112233445566:Queue"); - } - - @Test - public void testSqsClientSpanLegacyFormatUrls() { - testSqsUrl( - "https://ap-northeast-2.queue.amazonaws.com/123456789012/MyQueue", - "arn:aws:sqs:ap-northeast-2:123456789012:MyQueue"); - testSqsUrl( - "http://cn-northwest-1.queue.amazonaws.com/123456789012/MyQueue", - "arn:aws-cn:sqs:cn-northwest-1:123456789012:MyQueue"); - testSqsUrl( - "http://cn-north-1.queue.amazonaws.com/123456789012/MyQueue", - "arn:aws-cn:sqs:cn-north-1:123456789012:MyQueue"); - testSqsUrl( - "ap-south-1.queue.amazonaws.com/123412341234/MyLongerQueueNameHere", - "arn:aws:sqs:ap-south-1:123412341234:MyLongerQueueNameHere"); - testSqsUrl( - "https://us-gov-east-1.queue.amazonaws.com/123456789012/MyQueue", - "arn:aws-us-gov:sqs:us-gov-east-1:123456789012:MyQueue"); - } - - @Test - public void testSqsClientSpanNorthVirginiaLegacyUrl() { - testSqsUrl( - "https://queue.amazonaws.com/123456789012/MyQueue", - "arn:aws:sqs:us-east-1:123456789012:MyQueue"); - } - - @Test - public void testSqsClientSpanCustomUrls() { - testSqsUrl("http://127.0.0.1:1212/123456789012/MyQueue", "::sqs::123456789012:MyQueue"); - testSqsUrl("https://127.0.0.1:1212/123412341234/RRR", "::sqs::123412341234:RRR"); - testSqsUrl("127.0.0.1:1212/123412341234/QQ", "::sqs::123412341234:QQ"); - testSqsUrl("https://amazon.com/123412341234/BB", "::sqs::123412341234:BB"); - } - - @Test - public void testSqsClientSpanLongUrls() { - String queueName = "a".repeat(80); - testSqsUrl( - "http://127.0.0.1:1212/123456789012/" + queueName, "::sqs::123456789012:" + queueName); - - String queueNameTooLong = "a".repeat(81); - testSqsUrl("http://127.0.0.1:1212/123456789012/" + queueNameTooLong, null); - } - - @Test - public void testClientSpanSqsInvalidOrEmptyUrls() { - testSqsUrl(null, null); - testSqsUrl("", null); - testSqsUrl("invalidUrl", null); - testSqsUrl("https://www.amazon.com", null); - testSqsUrl("https://sqs.us-east-1.amazonaws.com/123412341234/.", null); - testSqsUrl("https://sqs.us-east-1.amazonaws.com/12/Queue", null); - testSqsUrl("https://sqs.us-east-1.amazonaws.com/A/A", null); - testSqsUrl("https://sqs.us-east-1.amazonaws.com/123412341234/A/ThisShouldNotBeHere", null); - } - - private void testSqsUrl(String sqsUrl, String expectedRemoteTarget) { - mockAttribute(AWS_QUEUE_URL, sqsUrl); - validateRemoteTargetAttributes(AWS_REMOTE_TARGET, expectedRemoteTarget); - mockAttribute(AWS_QUEUE_URL, null); - } - @Test public void testHttpStatusAttributeNotAwsSdk() { validateHttpStatusWithThrowable(new ThrowableWithMethodGetStatusCode(500), null); @@ -887,31 +800,32 @@ private void validatePeerServiceDoesOverride(AttributeKey remoteServiceK mockAttribute(PEER_SERVICE, null); } - private void validateRemoteTargetAttributes( - AttributeKey remoteTargetKey, String remoteTarget) { - // Client, Producer and Consumer spans should generate the expected RemoteTarget attribute + private void validateRemoteResourceAttributes(String type, String identifier) { + // Client, Producer and Consumer spans should generate the expected remote resource attributes when(spanDataMock.getKind()).thenReturn(SpanKind.CLIENT); Attributes actualAttributes = GENERATOR.generateMetricAttributeMapFromSpan(spanDataMock, resource).get(DEPENDENCY_METRIC); - assertThat(actualAttributes.get(remoteTargetKey)).isEqualTo(remoteTarget); + assertThat(actualAttributes.get(AWS_REMOTE_RESOURCE_TYPE)).isEqualTo(type); + assertThat(actualAttributes.get(AWS_REMOTE_RESOURCE_IDENTIFIER)).isEqualTo(identifier); when(spanDataMock.getKind()).thenReturn(SpanKind.PRODUCER); actualAttributes = GENERATOR.generateMetricAttributeMapFromSpan(spanDataMock, resource).get(DEPENDENCY_METRIC); - - assertThat(actualAttributes.get(remoteTargetKey)).isEqualTo(remoteTarget); + assertThat(actualAttributes.get(AWS_REMOTE_RESOURCE_TYPE)).isEqualTo(type); + assertThat(actualAttributes.get(AWS_REMOTE_RESOURCE_IDENTIFIER)).isEqualTo(identifier); when(spanDataMock.getKind()).thenReturn(SpanKind.CONSUMER); actualAttributes = GENERATOR.generateMetricAttributeMapFromSpan(spanDataMock, resource).get(DEPENDENCY_METRIC); - assertThat(actualAttributes.get(remoteTargetKey)).isEqualTo(remoteTarget); - assertThat(actualAttributes.get(remoteTargetKey)).isEqualTo(remoteTarget); + assertThat(actualAttributes.get(AWS_REMOTE_RESOURCE_TYPE)).isEqualTo(type); + assertThat(actualAttributes.get(AWS_REMOTE_RESOURCE_IDENTIFIER)).isEqualTo(identifier); - // Server span should not generate RemoteTarget attribute + // Server span should not generate remote resource attributes when(spanDataMock.getKind()).thenReturn(SpanKind.SERVER); actualAttributes = GENERATOR.generateMetricAttributeMapFromSpan(spanDataMock, resource).get(SERVICE_METRIC); - assertThat(actualAttributes.get(remoteTargetKey)).isEqualTo(null); + assertThat(actualAttributes.get(AWS_REMOTE_RESOURCE_TYPE)).isEqualTo(null); + assertThat(actualAttributes.get(AWS_REMOTE_RESOURCE_IDENTIFIER)).isEqualTo(null); } private void validateHttpStatusWithThrowable(Throwable throwable, Long expectedStatusCode) { @@ -948,7 +862,7 @@ private void validateHttpStatusForNonLocalRootWithThrowableForClient( } @Test - public void testNormalizeServiceNameNonAwsSdkSpan() { + public void testNormalizeRemoteServiceName_NoNormalization() { String serviceName = "non aws service"; mockAttribute(RPC_SERVICE, serviceName); when(spanDataMock.getKind()).thenReturn(SpanKind.CLIENT); @@ -959,31 +873,28 @@ public void testNormalizeServiceNameNonAwsSdkSpan() { } @Test - public void testNormalizeServiceNameAwsSdkV1Span() { - String serviceName = "Amazon S3"; - mockAttribute(RPC_SYSTEM, "aws-api"); - mockAttribute(RPC_SERVICE, serviceName); - when(spanDataMock.getInstrumentationScopeInfo()) - .thenReturn(InstrumentationScopeInfo.create("io.opentelemetry.aws-sdk-1.11 1.28.0-alpha")); - when(spanDataMock.getKind()).thenReturn(SpanKind.CLIENT); + public void testNormalizeRemoteServiceName_AwsSdk() { + // AWS SDK V1 + testAwsSdkServiceNormalization("AmazonDynamoDBv2", "AWS::DynamoDB"); + testAwsSdkServiceNormalization("AmazonKinesis", "AWS::Kinesis"); + testAwsSdkServiceNormalization("Amazon S3", "AWS::S3"); + testAwsSdkServiceNormalization("AmazonSQS", "AWS::SQS"); - Attributes actualAttributes = - GENERATOR.generateMetricAttributeMapFromSpan(spanDataMock, resource).get(DEPENDENCY_METRIC); - assertThat(actualAttributes.get(AWS_REMOTE_SERVICE)).isEqualTo("AWS.SDK.Amazon S3"); + // AWS SDK V2 + testAwsSdkServiceNormalization("DynamoDb", "AWS::DynamoDB"); + testAwsSdkServiceNormalization("Kinesis", "AWS::Kinesis"); + testAwsSdkServiceNormalization("S3", "AWS::S3"); + testAwsSdkServiceNormalization("Sqs", "AWS::SQS"); } - @Test - public void testNormalizeServiceNameAwsSdkV2Span() { - String serviceName = "DynamoDb"; + private void testAwsSdkServiceNormalization(String serviceName, String expectedRemoteService) { mockAttribute(RPC_SYSTEM, "aws-api"); mockAttribute(RPC_SERVICE, serviceName); - when(spanDataMock.getInstrumentationScopeInfo()) - .thenReturn(InstrumentationScopeInfo.create("io.opentelemetry.aws-sdk-2.2 1.28.0-alpha")); when(spanDataMock.getKind()).thenReturn(SpanKind.CLIENT); Attributes actualAttributes = GENERATOR.generateMetricAttributeMapFromSpan(spanDataMock, resource).get(DEPENDENCY_METRIC); - assertThat(actualAttributes.get(AWS_REMOTE_SERVICE)).isEqualTo("AWS.SDK.DynamoDb"); + assertThat(actualAttributes.get(AWS_REMOTE_SERVICE)).isEqualTo(expectedRemoteService); } @Test diff --git a/awsagentprovider/src/test/java/software/amazon/opentelemetry/javaagent/providers/SqsUrlParserTest.java b/awsagentprovider/src/test/java/software/amazon/opentelemetry/javaagent/providers/SqsUrlParserTest.java new file mode 100644 index 0000000000..551d02b7b5 --- /dev/null +++ b/awsagentprovider/src/test/java/software/amazon/opentelemetry/javaagent/providers/SqsUrlParserTest.java @@ -0,0 +1,82 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package software.amazon.opentelemetry.javaagent.providers; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Optional; +import org.junit.jupiter.api.Test; + +public class SqsUrlParserTest { + + @Test + public void testSqsClientSpanBasicUrls() { + validate("https://sqs.us-east-1.amazonaws.com/123412341234/Q_Name-5", "Q_Name-5"); + validate("https://sqs.af-south-1.amazonaws.com/999999999999/-_ThisIsValid", "-_ThisIsValid"); + validate("http://sqs.eu-west-3.amazonaws.com/000000000000/FirstQueue", "FirstQueue"); + validate("sqs.sa-east-1.amazonaws.com/123456781234/SecondQueue", "SecondQueue"); + } + + @Test + public void testSqsClientSpanLegacyFormatUrls() { + validate("https://ap-northeast-2.queue.amazonaws.com/123456789012/MyQueue", "MyQueue"); + validate("http://cn-northwest-1.queue.amazonaws.com/123456789012/MyQueue", "MyQueue"); + validate("http://cn-north-1.queue.amazonaws.com/123456789012/MyQueue", "MyQueue"); + validate( + "ap-south-1.queue.amazonaws.com/123412341234/MyLongerQueueNameHere", + "MyLongerQueueNameHere"); + validate("https://queue.amazonaws.com/123456789012/MyQueue", "MyQueue"); + } + + @Test + public void testSqsClientSpanCustomUrls() { + validate("http://127.0.0.1:1212/123456789012/MyQueue", "MyQueue"); + validate("https://127.0.0.1:1212/123412341234/RRR", "RRR"); + validate("127.0.0.1:1212/123412341234/QQ", "QQ"); + validate("https://amazon.com/123412341234/BB", "BB"); + } + + @Test + public void testSqsClientSpanLongUrls() { + String queueName = "a".repeat(80); + validate("http://127.0.0.1:1212/123456789012/" + queueName, queueName); + + String queueNameTooLong = "a".repeat(81); + validate("http://127.0.0.1:1212/123456789012/" + queueNameTooLong, null); + } + + @Test + public void testClientSpanSqsInvalidOrEmptyUrls() { + validate(null, null); + validate("", null); + validate(" ", null); + validate("/", null); + validate("//", null); + validate("///", null); + validate("//asdf", null); + validate("/123412341234/as&df", null); + validate("invalidUrl", null); + validate("https://www.amazon.com", null); + validate("https://sqs.us-east-1.amazonaws.com/123412341234/.", null); + validate("https://sqs.us-east-1.amazonaws.com/12/Queue", null); + validate("https://sqs.us-east-1.amazonaws.com/A/A", null); + validate("https://sqs.us-east-1.amazonaws.com/123412341234/A/ThisShouldNotBeHere", null); + } + + private void validate(String url, String expectedName) { + assertThat(SqsUrlParser.getQueueName(url)).isEqualTo(Optional.ofNullable(expectedName)); + } +} From 49056e7cd21d2ea20635e88117083abcecade747 Mon Sep 17 00:00:00 2001 From: Thomas Pierce Date: Wed, 10 Apr 2024 19:17:08 +0000 Subject: [PATCH 2/3] Update awsagentprovider code to only reference Application Signals --- ...ApplicationSignalsCustomizerProvider.java} | 51 +++++++++++-------- ...re.spi.AutoConfigurationCustomizerProvider | 2 +- 2 files changed, 31 insertions(+), 22 deletions(-) rename awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/{AwsAppSignalsCustomizerProvider.java => AwsApplicationSignalsCustomizerProvider.java} (81%) diff --git a/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsAppSignalsCustomizerProvider.java b/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsApplicationSignalsCustomizerProvider.java similarity index 81% rename from awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsAppSignalsCustomizerProvider.java rename to awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsApplicationSignalsCustomizerProvider.java index 9e61275024..1dd9c6b44c 100644 --- a/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsAppSignalsCustomizerProvider.java +++ b/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsApplicationSignalsCustomizerProvider.java @@ -55,10 +55,11 @@ * otel.aws.application.signals.enabled or the environment variable * OTEL_AWS_APPLICATION_SIGNALS_ENABLED. This flag is disabled by default. */ -public class AwsAppSignalsCustomizerProvider implements AutoConfigurationCustomizerProvider { +public class AwsApplicationSignalsCustomizerProvider + implements AutoConfigurationCustomizerProvider { private static final Duration DEFAULT_METRIC_EXPORT_INTERVAL = Duration.ofMinutes(1); private static final Logger logger = - Logger.getLogger(AwsAppSignalsCustomizerProvider.class.getName()); + Logger.getLogger(AwsApplicationSignalsCustomizerProvider.class.getName()); private static final String SMP_ENABLED_CONFIG = "otel.smp.enabled"; private static final String APP_SIGNALS_ENABLED_CONFIG = "otel.aws.app.signals.enabled"; @@ -76,7 +77,7 @@ public void customize(AutoConfigurationCustomizer autoConfiguration) { autoConfiguration.addSpanExporterCustomizer(this::customizeSpanExporter); } - private boolean isAppSignalsEnabled(ConfigProperties configProps) { + private boolean isApplicationSignalsEnabled(ConfigProperties configProps) { return configProps.getBoolean( APPLICATION_SIGNALS_ENABLED_CONFIG, configProps.getBoolean( @@ -84,7 +85,7 @@ private boolean isAppSignalsEnabled(ConfigProperties configProps) { } private Sampler customizeSampler(Sampler sampler, ConfigProperties configProps) { - if (isAppSignalsEnabled(configProps)) { + if (isApplicationSignalsEnabled(configProps)) { return AlwaysRecordSampler.create(sampler); } return sampler; @@ -92,26 +93,28 @@ private Sampler customizeSampler(Sampler sampler, ConfigProperties configProps) private SdkTracerProviderBuilder customizeTracerProviderBuilder( SdkTracerProviderBuilder tracerProviderBuilder, ConfigProperties configProps) { - if (isAppSignalsEnabled(configProps)) { - logger.info("AWS AppSignals enabled"); + if (isApplicationSignalsEnabled(configProps)) { + logger.info("AWS Application Signals enabled"); Duration exportInterval = configProps.getDuration("otel.metric.export.interval", DEFAULT_METRIC_EXPORT_INTERVAL); logger.log( - Level.FINE, String.format("AppSignals Metrics export interval: %s", exportInterval)); + Level.FINE, + String.format("AWS Application Signals Metrics export interval: %s", exportInterval)); // Cap export interval to 60 seconds. This is currently required for metrics-trace correlation // to work correctly. if (exportInterval.compareTo(DEFAULT_METRIC_EXPORT_INTERVAL) > 0) { exportInterval = DEFAULT_METRIC_EXPORT_INTERVAL; logger.log( Level.INFO, - String.format("AWS AppSignals metrics export interval capped to %s", exportInterval)); + String.format( + "AWS Application Signals metrics export interval capped to %s", exportInterval)); } // Construct and set local and remote attributes span processor tracerProviderBuilder.addSpanProcessor( AttributePropagatingSpanProcessorBuilder.create().build()); // Construct meterProvider MetricExporter metricsExporter = - AppSignalsExporterProvider.INSTANCE.createExporter(configProps); + ApplicationSignalsExporterProvider.INSTANCE.createExporter(configProps); MetricReader metricReader = PeriodicMetricReader.builder(metricsExporter).setInterval(exportInterval).build(); @@ -121,7 +124,7 @@ private SdkTracerProviderBuilder customizeTracerProviderBuilder( .setResource(ResourceHolder.getResource()) .registerMetricReader(metricReader) .build(); - // Construct and set AppSignals metrics processor + // Construct and set application signals metrics processor SpanProcessor spanMetricsProcessor = AwsSpanMetricsProcessorBuilder.create(meterProvider, ResourceHolder.getResource()) .build(); @@ -132,7 +135,7 @@ private SdkTracerProviderBuilder customizeTracerProviderBuilder( private SpanExporter customizeSpanExporter( SpanExporter spanExporter, ConfigProperties configProps) { - if (isAppSignalsEnabled(configProps)) { + if (isApplicationSignalsEnabled(configProps)) { return AwsMetricAttributesSpanExporterBuilder.create( spanExporter, ResourceHolder.getResource()) .build(); @@ -141,43 +144,49 @@ private SpanExporter customizeSpanExporter( return spanExporter; } - private enum AppSignalsExporterProvider { + private enum ApplicationSignalsExporterProvider { INSTANCE; public MetricExporter createExporter(ConfigProperties configProps) { String protocol = OtlpConfigUtil.getOtlpProtocol(OtlpConfigUtil.DATA_TYPE_METRICS, configProps); - logger.log(Level.FINE, String.format("AppSignals export protocol: %s", protocol)); + logger.log( + Level.FINE, String.format("AWS Application Signals export protocol: %s", protocol)); - String appSignalsEndpoint; + String applicationSignalsEndpoint; if (protocol.equals(OtlpConfigUtil.PROTOCOL_HTTP_PROTOBUF)) { - appSignalsEndpoint = + applicationSignalsEndpoint = configProps.getString( APPLICATION_SIGNALS_EXPORTER_ENDPOINT_CONFIG, configProps.getString( APP_SIGNALS_EXPORTER_ENDPOINT_CONFIG, configProps.getString(SMP_EXPORTER_ENDPOINT_CONFIG, "http://localhost:4316/v1/metrics"))); - logger.log(Level.FINE, String.format("AppSignals export endpoint: %s", appSignalsEndpoint)); + logger.log( + Level.FINE, + String.format("AWS Application Signals export endpoint: %s", applicationSignalsEndpoint)); return OtlpHttpMetricExporter.builder() - .setEndpoint(appSignalsEndpoint) + .setEndpoint(applicationSignalsEndpoint) .setDefaultAggregationSelector(this::getAggregation) .setAggregationTemporalitySelector(AggregationTemporalitySelector.deltaPreferred()) .build(); } else if (protocol.equals(OtlpConfigUtil.PROTOCOL_GRPC)) { - appSignalsEndpoint = + applicationSignalsEndpoint = configProps.getString( APPLICATION_SIGNALS_EXPORTER_ENDPOINT_CONFIG, configProps.getString( APP_SIGNALS_EXPORTER_ENDPOINT_CONFIG, configProps.getString(SMP_EXPORTER_ENDPOINT_CONFIG, "http://localhost:4315"))); - logger.log(Level.FINE, String.format("AppSignals export endpoint: %s", appSignalsEndpoint)); + logger.log( + Level.FINE, + String.format("AWS Application Signals export endpoint: %s", applicationSignalsEndpoint)); return OtlpGrpcMetricExporter.builder() - .setEndpoint(appSignalsEndpoint) + .setEndpoint(applicationSignalsEndpoint) .setDefaultAggregationSelector(this::getAggregation) .setAggregationTemporalitySelector(AggregationTemporalitySelector.deltaPreferred()) .build(); } - throw new ConfigurationException("Unsupported AppSignals export protocol: " + protocol); + throw new ConfigurationException( + "Unsupported AWS Application Signals export protocol: " + protocol); } private Aggregation getAggregation(InstrumentType instrumentType) { diff --git a/awsagentprovider/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider b/awsagentprovider/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider index f71f62ab51..6cc27f1de4 100644 --- a/awsagentprovider/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider +++ b/awsagentprovider/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider @@ -15,4 +15,4 @@ software.amazon.opentelemetry.javaagent.providers.AwsAgentPropertiesCustomizerProvider software.amazon.opentelemetry.javaagent.providers.AwsTracerCustomizerProvider -software.amazon.opentelemetry.javaagent.providers.AwsAppSignalsCustomizerProvider +software.amazon.opentelemetry.javaagent.providers.AwsApplicationSignalsCustomizerProvider From b61037fe18a7d4d9eca63700157eaf043039c969 Mon Sep 17 00:00:00 2001 From: Thomas Pierce Date: Thu, 9 May 2024 08:42:23 -0700 Subject: [PATCH 3/3] Spotless --- .../AwsApplicationSignalsCustomizerProvider.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsApplicationSignalsCustomizerProvider.java b/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsApplicationSignalsCustomizerProvider.java index 1dd9c6b44c..2d16135352 100644 --- a/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsApplicationSignalsCustomizerProvider.java +++ b/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsApplicationSignalsCustomizerProvider.java @@ -160,10 +160,12 @@ public MetricExporter createExporter(ConfigProperties configProps) { APPLICATION_SIGNALS_EXPORTER_ENDPOINT_CONFIG, configProps.getString( APP_SIGNALS_EXPORTER_ENDPOINT_CONFIG, - configProps.getString(SMP_EXPORTER_ENDPOINT_CONFIG, "http://localhost:4316/v1/metrics"))); + configProps.getString( + SMP_EXPORTER_ENDPOINT_CONFIG, "http://localhost:4316/v1/metrics"))); logger.log( Level.FINE, - String.format("AWS Application Signals export endpoint: %s", applicationSignalsEndpoint)); + String.format( + "AWS Application Signals export endpoint: %s", applicationSignalsEndpoint)); return OtlpHttpMetricExporter.builder() .setEndpoint(applicationSignalsEndpoint) .setDefaultAggregationSelector(this::getAggregation) @@ -178,7 +180,8 @@ public MetricExporter createExporter(ConfigProperties configProps) { configProps.getString(SMP_EXPORTER_ENDPOINT_CONFIG, "http://localhost:4315"))); logger.log( Level.FINE, - String.format("AWS Application Signals export endpoint: %s", applicationSignalsEndpoint)); + String.format( + "AWS Application Signals export endpoint: %s", applicationSignalsEndpoint)); return OtlpGrpcMetricExporter.builder() .setEndpoint(applicationSignalsEndpoint) .setDefaultAggregationSelector(this::getAggregation)