46
46
import static io .opentelemetry .semconv .SemanticAttributes .SERVER_SOCKET_PORT ;
47
47
import static io .opentelemetry .semconv .SemanticAttributes .URL_FULL ;
48
48
import static software .amazon .opentelemetry .javaagent .providers .AwsAttributeKeys .AWS_AGENT_ID ;
49
+ import static software .amazon .opentelemetry .javaagent .providers .AwsAttributeKeys .AWS_AUTH_ACCESS_KEY ;
50
+ import static software .amazon .opentelemetry .javaagent .providers .AwsAttributeKeys .AWS_AUTH_REGION ;
49
51
import static software .amazon .opentelemetry .javaagent .providers .AwsAttributeKeys .AWS_BUCKET_NAME ;
50
52
import static software .amazon .opentelemetry .javaagent .providers .AwsAttributeKeys .AWS_CLOUDFORMATION_PRIMARY_IDENTIFIER ;
51
53
import static software .amazon .opentelemetry .javaagent .providers .AwsAttributeKeys .AWS_DATA_SOURCE_ID ;
52
54
import static software .amazon .opentelemetry .javaagent .providers .AwsAttributeKeys .AWS_GUARDRAIL_ARN ;
53
55
import static software .amazon .opentelemetry .javaagent .providers .AwsAttributeKeys .AWS_GUARDRAIL_ID ;
54
56
import static software .amazon .opentelemetry .javaagent .providers .AwsAttributeKeys .AWS_KNOWLEDGE_BASE_ID ;
55
- import static software .amazon .opentelemetry .javaagent .providers .AwsAttributeKeys .AWS_LAMBDA_ARN ;
57
+ import static software .amazon .opentelemetry .javaagent .providers .AwsAttributeKeys .AWS_LAMBDA_FUNCTION_ARN ;
56
58
import static software .amazon .opentelemetry .javaagent .providers .AwsAttributeKeys .AWS_LAMBDA_RESOURCE_ID ;
57
59
import static software .amazon .opentelemetry .javaagent .providers .AwsAttributeKeys .AWS_LOCAL_OPERATION ;
58
60
import static software .amazon .opentelemetry .javaagent .providers .AwsAttributeKeys .AWS_LOCAL_SERVICE ;
79
81
import static software .amazon .opentelemetry .javaagent .providers .AwsSpanProcessingUtil .MAX_KEYWORD_LENGTH ;
80
82
import static software .amazon .opentelemetry .javaagent .providers .AwsSpanProcessingUtil .SQL_DIALECT_PATTERN ;
81
83
import static software .amazon .opentelemetry .javaagent .providers .AwsSpanProcessingUtil .UNKNOWN_OPERATION ;
82
- import static software .amazon .opentelemetry .javaagent .providers .AwsSpanProcessingUtil .UNKNOWN_REMOTE_ACCOUNT_ID ;
83
84
import static software .amazon .opentelemetry .javaagent .providers .AwsSpanProcessingUtil .UNKNOWN_REMOTE_OPERATION ;
84
- import static software .amazon .opentelemetry .javaagent .providers .AwsSpanProcessingUtil .UNKNOWN_REMOTE_REGION ;
85
85
import static software .amazon .opentelemetry .javaagent .providers .AwsSpanProcessingUtil .UNKNOWN_REMOTE_SERVICE ;
86
86
import static software .amazon .opentelemetry .javaagent .providers .AwsSpanProcessingUtil .isAwsSDKSpan ;
87
87
import static software .amazon .opentelemetry .javaagent .providers .AwsSpanProcessingUtil .isDBSpan ;
@@ -137,6 +137,8 @@ final class AwsMetricAttributeGenerator implements MetricAttributeGenerator {
137
137
private static final String NORMALIZED_SNS_SERVICE_NAME = "AWS::SNS" ;
138
138
private static final String NORMALIZED_SECRETSMANAGER_SERVICE_NAME = "AWS::SecretsManager" ;
139
139
private static final String NORMALIZED_LAMBDA_SERVICE_NAME = "AWS::Lambda" ;
140
+
141
+ // List of resource arns to extract cross-account information
140
142
private static final List <AttributeKey <String >> ARN_ATTRIBUTES =
141
143
List .of (
142
144
AWS_TABLE_ARN ,
@@ -146,7 +148,7 @@ final class AwsMetricAttributeGenerator implements MetricAttributeGenerator {
146
148
AWS_STEP_FUNCTIONS_ACTIVITY_ARN ,
147
149
AWS_STATE_MACHINE_ARN ,
148
150
AWS_GUARDRAIL_ARN ,
149
- AWS_LAMBDA_ARN );
151
+ AWS_LAMBDA_FUNCTION_ARN );
150
152
151
153
// Special DEPENDENCY attribute value if GRAPHQL_OPERATION_TYPE attribute key is present.
152
154
private static final String GRAPHQL = "graphql" ;
@@ -186,10 +188,13 @@ private Attributes generateDependencyMetricAttributes(SpanData span, Resource re
186
188
setService (resource , span , builder );
187
189
setEgressOperation (span , builder );
188
190
setRemoteServiceAndOperation (span , builder );
189
- setRemoteResourceTypeAndIdentifier (span , builder );
190
- setRemoteResourceAccessKeyAndRegion (span , builder );
191
- setRemoteResourceAccountIdAndRegion (
192
- span , builder ); // Overwrite remote region from STS with one from resource ARN
191
+ boolean isRemoteResourceIdentifierPresent = setRemoteResourceTypeAndIdentifier (span , builder );
192
+ if (isRemoteResourceIdentifierPresent ) {
193
+ boolean isAccountIdAndRegionPresent = setRemoteResourceAccountIdAndRegion (span , builder );
194
+ if (!isAccountIdAndRegionPresent ) {
195
+ setRemoteResourceAccessKeyAndRegion (span , builder );
196
+ }
197
+ }
193
198
setSpanKindForDependency (span , builder );
194
199
setHttpStatus (span , builder );
195
200
setRemoteDbUser (span , builder );
@@ -461,7 +466,8 @@ private static String normalizeRemoteServiceName(SpanData span, String serviceNa
461
466
* href="https://docs.aws.amazon.com/cloudcontrolapi/latest/userguide/supported-resources.html">AWS
462
467
* Cloud Control resource format</a>.
463
468
*/
464
- private static void setRemoteResourceTypeAndIdentifier (SpanData span , AttributesBuilder builder ) {
469
+ private static boolean setRemoteResourceTypeAndIdentifier (
470
+ SpanData span , AttributesBuilder builder ) {
465
471
Optional <String > remoteResourceType = Optional .empty ();
466
472
Optional <String > remoteResourceIdentifier = Optional .empty ();
467
473
Optional <String > cloudformationPrimaryIdentifier = Optional .empty ();
@@ -471,10 +477,20 @@ private static void setRemoteResourceTypeAndIdentifier(SpanData span, Attributes
471
477
remoteResourceType = Optional .of (NORMALIZED_DYNAMO_DB_SERVICE_NAME + "::Table" );
472
478
remoteResourceIdentifier =
473
479
Optional .ofNullable (escapeDelimiters (span .getAttributes ().get (AWS_TABLE_NAME )));
480
+ } else if (isKeyPresent (span , AWS_TABLE_ARN )) {
481
+ remoteResourceType = Optional .of (NORMALIZED_DYNAMO_DB_SERVICE_NAME + "::Table" );
482
+ remoteResourceIdentifier =
483
+ getDynamodbTableNameFromArn (
484
+ Optional .ofNullable (escapeDelimiters (span .getAttributes ().get (AWS_TABLE_ARN ))));
474
485
} else if (isKeyPresent (span , AWS_STREAM_NAME )) {
475
486
remoteResourceType = Optional .of (NORMALIZED_KINESIS_SERVICE_NAME + "::Stream" );
476
487
remoteResourceIdentifier =
477
488
Optional .ofNullable (escapeDelimiters (span .getAttributes ().get (AWS_STREAM_NAME )));
489
+ } else if (isKeyPresent (span , AWS_STREAM_ARN )) {
490
+ remoteResourceType = Optional .of (NORMALIZED_KINESIS_SERVICE_NAME + "::Stream" );
491
+ remoteResourceIdentifier =
492
+ getKinesisStreamNameFromArn (
493
+ Optional .ofNullable (escapeDelimiters (span .getAttributes ().get (AWS_STREAM_ARN ))));
478
494
} else if (isKeyPresent (span , AWS_BUCKET_NAME )) {
479
495
remoteResourceType = Optional .of (NORMALIZED_S3_SERVICE_NAME + "::Bucket" );
480
496
remoteResourceIdentifier =
@@ -558,77 +574,113 @@ private static void setRemoteResourceTypeAndIdentifier(SpanData span, Attributes
558
574
builder .put (AWS_REMOTE_RESOURCE_TYPE , remoteResourceType .get ());
559
575
builder .put (AWS_REMOTE_RESOURCE_IDENTIFIER , remoteResourceIdentifier .get ());
560
576
builder .put (AWS_CLOUDFORMATION_PRIMARY_IDENTIFIER , cloudformationPrimaryIdentifier .get ());
577
+ return true ;
561
578
}
579
+ return false ;
562
580
}
563
581
564
582
private static void setRemoteResourceAccessKeyAndRegion (
565
583
SpanData span , AttributesBuilder builder ) {
566
- if (isKeyPresent (span , AWS_REMOTE_RESOURCE_ACCESS_KEY )) {
567
- String remoteAccessKey = span .getAttributes ().get (AWS_REMOTE_RESOURCE_ACCESS_KEY );
568
- builder .put (AWS_REMOTE_RESOURCE_ACCESS_KEY , remoteAccessKey );
584
+ if (isKeyPresent (span , AWS_AUTH_ACCESS_KEY )) {
585
+ String remoteResourceAccessKey = span .getAttributes ().get (AWS_AUTH_ACCESS_KEY );
586
+ builder .put (AWS_REMOTE_RESOURCE_ACCESS_KEY , remoteResourceAccessKey );
569
587
}
570
588
571
- if (isKeyPresent (span , AWS_REMOTE_RESOURCE_REGION )) {
572
- String remoteRegion = span .getAttributes ().get (AWS_REMOTE_RESOURCE_REGION );
573
- builder .put (AWS_REMOTE_RESOURCE_REGION , remoteRegion );
589
+ if (isKeyPresent (span , AWS_AUTH_REGION )) {
590
+ String remoteResourceRegion = span .getAttributes ().get (AWS_AUTH_REGION );
591
+ builder .put (AWS_REMOTE_RESOURCE_REGION , remoteResourceRegion );
574
592
}
575
593
}
576
594
577
- private static void setRemoteResourceAccountIdAndRegion (
595
+ private static boolean setRemoteResourceAccountIdAndRegion (
578
596
SpanData span , AttributesBuilder builder ) {
579
- String remoteAccountId = UNKNOWN_REMOTE_ACCOUNT_ID ;
580
- String remoteRegion = UNKNOWN_REMOTE_REGION ;
597
+ Optional < String > remoteResourceAccountId = Optional . empty () ;
598
+ Optional < String > remoteResourceRegion = Optional . empty () ;
581
599
582
600
if (isKeyPresent (span , AWS_QUEUE_URL )) {
583
- try {
584
- // queue url format: https://sqs.{region}.amazonaws.com/{account_id}/{queue_name}
585
- String queueUrl = span .getAttributes ().get (AWS_QUEUE_URL );
586
- queueUrl = queueUrl .replace ("https://" , "" );
587
-
588
- String [] parts = queueUrl .split ("/" );
589
- remoteAccountId = parts [1 ];
590
-
591
- String domain = parts [0 ];
592
- String [] domainParts = domain .split ("\\ ." );
593
- remoteRegion = domainParts [1 ];
594
- } catch (Exception e ) {
595
- logger .log (
596
- Level .WARNING ,
597
- "Remote resource account id and region cannot be populated due to invalid url" ,
598
- e );
599
- }
601
+ String url = escapeDelimiters (span .getAttributes ().get (AWS_QUEUE_URL ));
602
+ remoteResourceAccountId = SqsUrlParser .getAccountId (url );
603
+ remoteResourceRegion = SqsUrlParser .getRegion (url );
600
604
} else {
601
605
for (AttributeKey <String > attributeKey : ARN_ATTRIBUTES ) {
602
606
if (isKeyPresent (span , attributeKey )) {
603
- String arn = span .getAttributes ().get (attributeKey );
604
- remoteAccountId = arn .split (":" )[4 ];
605
- remoteRegion = arn .split (":" )[3 ];
606
- break ;
607
+ String stringArn = escapeDelimiters (span .getAttributes ().get (attributeKey ));
608
+ try {
609
+ Arn resourceArn = Arn .fromString (stringArn );
610
+ remoteResourceAccountId = Optional .of (resourceArn .getAccountId ());
611
+ remoteResourceRegion = Optional .of (resourceArn .getRegion ());
612
+ } catch (IllegalArgumentException e ) {
613
+ logger .log (
614
+ Level .FINE ,
615
+ String .format (
616
+ "Could not parse ARN to extract cross-account information: %s" , stringArn ));
617
+ }
607
618
}
608
619
}
609
620
}
610
621
611
- if (!remoteAccountId .equals (UNKNOWN_REMOTE_ACCOUNT_ID )
612
- && !remoteRegion .equals (UNKNOWN_REMOTE_REGION )) {
613
- builder .put (AWS_REMOTE_RESOURCE_ACCOUNT_ID , remoteAccountId );
614
- builder .put (AWS_REMOTE_RESOURCE_REGION , remoteRegion );
615
- builder .remove (AWS_REMOTE_RESOURCE_ACCESS_KEY );
622
+ if (remoteResourceAccountId .isPresent () && remoteResourceRegion .isPresent ()) {
623
+ builder .put (AWS_REMOTE_RESOURCE_ACCOUNT_ID , remoteResourceAccountId .get ());
624
+ builder .put (AWS_REMOTE_RESOURCE_REGION , remoteResourceRegion .get ());
625
+ return true ;
626
+ }
627
+ return false ;
628
+ }
629
+
630
+ private static Optional <String > getKinesisStreamNameFromArn (Optional <String > stringArn ) {
631
+ try {
632
+ Arn resourceArn = Arn .fromString (stringArn .get ());
633
+ return Optional .of (resourceArn .getResource ().toString ().split (":" )[1 ]);
634
+ } catch (IllegalArgumentException e ) {
635
+ logger .log (
636
+ Level .FINE , String .format ("Could not parse Kinesis stream name from ARN: %s" , stringArn ));
637
+ }
638
+ return Optional .empty ();
639
+ }
640
+
641
+ private static Optional <String > getDynamodbTableNameFromArn (Optional <String > stringArn ) {
642
+ try {
643
+ Arn resourceArn = Arn .fromString (stringArn .get ());
644
+ return Optional .of (resourceArn .getResource ().toString ().split (":" )[1 ]);
645
+ } catch (IllegalArgumentException e ) {
646
+ logger .log (
647
+ Level .FINE , String .format ("Could not parse DynamoDB table name from ARN: %s" , stringArn ));
616
648
}
649
+ return Optional .empty ();
617
650
}
618
651
619
652
private static Optional <String > getSecretsManagerResourceNameFromArn (Optional <String > stringArn ) {
620
- Arn resourceArn = Arn .fromString (stringArn .get ());
621
- return Optional .of (resourceArn .getResource ().toString ().split (":" )[1 ]);
653
+ try {
654
+ Arn resourceArn = Arn .fromString (stringArn .get ());
655
+ return Optional .of (resourceArn .getResource ().toString ().split (":" )[1 ]);
656
+ } catch (IllegalArgumentException e ) {
657
+ logger .log (
658
+ Level .FINE ,
659
+ String .format ("Could not parse Secrets Manager resource name from ARN: %s" , stringArn ));
660
+ }
661
+ return Optional .empty ();
622
662
}
623
663
624
664
private static Optional <String > getSfnResourceNameFromArn (Optional <String > stringArn ) {
625
- Arn resourceArn = Arn .fromString (stringArn .get ());
626
- return Optional .of (resourceArn .getResource ().toString ().split (":" )[1 ]);
665
+ try {
666
+ Arn resourceArn = Arn .fromString (stringArn .get ());
667
+ return Optional .of (resourceArn .getResource ().toString ().split (":" )[1 ]);
668
+ } catch (IllegalArgumentException e ) {
669
+ logger .log (
670
+ Level .FINE , String .format ("Could not parse Sfn resource name from ARN: %s" , stringArn ));
671
+ }
672
+ return Optional .empty ();
627
673
}
628
674
629
675
private static Optional <String > getSnsResourceNameFromArn (Optional <String > stringArn ) {
630
- Arn resourceArn = Arn .fromString (stringArn .get ());
631
- return Optional .of (resourceArn .getResource ().toString ());
676
+ try {
677
+ Arn resourceArn = Arn .fromString (stringArn .get ());
678
+ return Optional .of (resourceArn .getResource ().toString ());
679
+ } catch (IllegalArgumentException e ) {
680
+ logger .log (
681
+ Level .FINE , String .format ("Could not parse Sfn resource name from ARN: %s" , stringArn ));
682
+ }
683
+ return Optional .empty ();
632
684
}
633
685
634
686
/**
0 commit comments