Skip to content

Commit 580105e

Browse files
rename attribute key, update sqs url parser, modify cross account impl logic
1 parent 884c442 commit 580105e

File tree

6 files changed

+484
-188
lines changed

6 files changed

+484
-188
lines changed

awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsAttributeKeys.java

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ final class AwsAttributeKeys {
2222

2323
private AwsAttributeKeys() {}
2424

25+
static final AttributeKey<String> AWS_AUTH_ACCESS_KEY =
26+
AttributeKey.stringKey("aws.auth.account.access_key");
27+
28+
static final AttributeKey<String> AWS_AUTH_REGION = AttributeKey.stringKey("aws.auth.region");
29+
2530
static final AttributeKey<String> AWS_SPAN_KIND = AttributeKey.stringKey("aws.span.kind");
2631

2732
static final AttributeKey<String> AWS_LOCAL_SERVICE = AttributeKey.stringKey("aws.local.service");
@@ -35,6 +40,15 @@ private AwsAttributeKeys() {}
3540
static final AttributeKey<String> AWS_REMOTE_OPERATION =
3641
AttributeKey.stringKey("aws.remote.operation");
3742

43+
static final AttributeKey<String> AWS_REMOTE_RESOURCE_ACCESS_KEY =
44+
AttributeKey.stringKey("aws.remote.resource.account.access_key");
45+
46+
static final AttributeKey<String> AWS_REMOTE_RESOURCE_ACCOUNT_ID =
47+
AttributeKey.stringKey("aws.remote.resource.account.id");
48+
49+
static final AttributeKey<String> AWS_REMOTE_RESOURCE_REGION =
50+
AttributeKey.stringKey("aws.remote.resource.region");
51+
3852
static final AttributeKey<String> AWS_REMOTE_RESOURCE_IDENTIFIER =
3953
AttributeKey.stringKey("aws.remote.resource.identifier");
4054

@@ -44,15 +58,6 @@ private AwsAttributeKeys() {}
4458
static final AttributeKey<String> AWS_REMOTE_RESOURCE_TYPE =
4559
AttributeKey.stringKey("aws.remote.resource.type");
4660

47-
static final AttributeKey<String> AWS_REMOTE_RESOURCE_ACCOUNT_ID =
48-
AttributeKey.stringKey("aws.remote.resource.account.id");
49-
50-
static final AttributeKey<String> AWS_REMOTE_RESOURCE_ACCESS_KEY =
51-
AttributeKey.stringKey("aws.remote.resource.account.access_key");
52-
53-
static final AttributeKey<String> AWS_REMOTE_RESOURCE_REGION =
54-
AttributeKey.stringKey("aws.remote.resource.region");
55-
5661
static final AttributeKey<String> AWS_REMOTE_DB_USER =
5762
AttributeKey.stringKey("aws.remote.db.user");
5863

@@ -73,7 +78,7 @@ private AwsAttributeKeys() {}
7378
static final AttributeKey<String> AWS_SECRET_ARN =
7479
AttributeKey.stringKey("aws.secretsmanager.secret.arn");
7580

76-
static final AttributeKey<String> AWS_LAMBDA_ARN =
81+
static final AttributeKey<String> AWS_LAMBDA_FUNCTION_ARN =
7782
AttributeKey.stringKey("aws.lambda.function.arn");
7883

7984
static final AttributeKey<String> AWS_LAMBDA_RESOURCE_ID =

awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsMetricAttributeGenerator.java

Lines changed: 102 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,15 @@
4646
import static io.opentelemetry.semconv.SemanticAttributes.SERVER_SOCKET_PORT;
4747
import static io.opentelemetry.semconv.SemanticAttributes.URL_FULL;
4848
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;
4951
import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_BUCKET_NAME;
5052
import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_CLOUDFORMATION_PRIMARY_IDENTIFIER;
5153
import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_DATA_SOURCE_ID;
5254
import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_GUARDRAIL_ARN;
5355
import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_GUARDRAIL_ID;
5456
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;
5658
import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_LAMBDA_RESOURCE_ID;
5759
import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_LOCAL_OPERATION;
5860
import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_LOCAL_SERVICE;
@@ -79,9 +81,7 @@
7981
import static software.amazon.opentelemetry.javaagent.providers.AwsSpanProcessingUtil.MAX_KEYWORD_LENGTH;
8082
import static software.amazon.opentelemetry.javaagent.providers.AwsSpanProcessingUtil.SQL_DIALECT_PATTERN;
8183
import static software.amazon.opentelemetry.javaagent.providers.AwsSpanProcessingUtil.UNKNOWN_OPERATION;
82-
import static software.amazon.opentelemetry.javaagent.providers.AwsSpanProcessingUtil.UNKNOWN_REMOTE_ACCOUNT_ID;
8384
import static software.amazon.opentelemetry.javaagent.providers.AwsSpanProcessingUtil.UNKNOWN_REMOTE_OPERATION;
84-
import static software.amazon.opentelemetry.javaagent.providers.AwsSpanProcessingUtil.UNKNOWN_REMOTE_REGION;
8585
import static software.amazon.opentelemetry.javaagent.providers.AwsSpanProcessingUtil.UNKNOWN_REMOTE_SERVICE;
8686
import static software.amazon.opentelemetry.javaagent.providers.AwsSpanProcessingUtil.isAwsSDKSpan;
8787
import static software.amazon.opentelemetry.javaagent.providers.AwsSpanProcessingUtil.isDBSpan;
@@ -137,6 +137,8 @@ final class AwsMetricAttributeGenerator implements MetricAttributeGenerator {
137137
private static final String NORMALIZED_SNS_SERVICE_NAME = "AWS::SNS";
138138
private static final String NORMALIZED_SECRETSMANAGER_SERVICE_NAME = "AWS::SecretsManager";
139139
private static final String NORMALIZED_LAMBDA_SERVICE_NAME = "AWS::Lambda";
140+
141+
// List of resource arns to extract cross-account information
140142
private static final List<AttributeKey<String>> ARN_ATTRIBUTES =
141143
List.of(
142144
AWS_TABLE_ARN,
@@ -146,7 +148,7 @@ final class AwsMetricAttributeGenerator implements MetricAttributeGenerator {
146148
AWS_STEP_FUNCTIONS_ACTIVITY_ARN,
147149
AWS_STATE_MACHINE_ARN,
148150
AWS_GUARDRAIL_ARN,
149-
AWS_LAMBDA_ARN);
151+
AWS_LAMBDA_FUNCTION_ARN);
150152

151153
// Special DEPENDENCY attribute value if GRAPHQL_OPERATION_TYPE attribute key is present.
152154
private static final String GRAPHQL = "graphql";
@@ -186,10 +188,13 @@ private Attributes generateDependencyMetricAttributes(SpanData span, Resource re
186188
setService(resource, span, builder);
187189
setEgressOperation(span, builder);
188190
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+
}
193198
setSpanKindForDependency(span, builder);
194199
setHttpStatus(span, builder);
195200
setRemoteDbUser(span, builder);
@@ -461,7 +466,8 @@ private static String normalizeRemoteServiceName(SpanData span, String serviceNa
461466
* href="https://docs.aws.amazon.com/cloudcontrolapi/latest/userguide/supported-resources.html">AWS
462467
* Cloud Control resource format</a>.
463468
*/
464-
private static void setRemoteResourceTypeAndIdentifier(SpanData span, AttributesBuilder builder) {
469+
private static boolean setRemoteResourceTypeAndIdentifier(
470+
SpanData span, AttributesBuilder builder) {
465471
Optional<String> remoteResourceType = Optional.empty();
466472
Optional<String> remoteResourceIdentifier = Optional.empty();
467473
Optional<String> cloudformationPrimaryIdentifier = Optional.empty();
@@ -471,10 +477,20 @@ private static void setRemoteResourceTypeAndIdentifier(SpanData span, Attributes
471477
remoteResourceType = Optional.of(NORMALIZED_DYNAMO_DB_SERVICE_NAME + "::Table");
472478
remoteResourceIdentifier =
473479
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))));
474485
} else if (isKeyPresent(span, AWS_STREAM_NAME)) {
475486
remoteResourceType = Optional.of(NORMALIZED_KINESIS_SERVICE_NAME + "::Stream");
476487
remoteResourceIdentifier =
477488
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))));
478494
} else if (isKeyPresent(span, AWS_BUCKET_NAME)) {
479495
remoteResourceType = Optional.of(NORMALIZED_S3_SERVICE_NAME + "::Bucket");
480496
remoteResourceIdentifier =
@@ -558,77 +574,113 @@ private static void setRemoteResourceTypeAndIdentifier(SpanData span, Attributes
558574
builder.put(AWS_REMOTE_RESOURCE_TYPE, remoteResourceType.get());
559575
builder.put(AWS_REMOTE_RESOURCE_IDENTIFIER, remoteResourceIdentifier.get());
560576
builder.put(AWS_CLOUDFORMATION_PRIMARY_IDENTIFIER, cloudformationPrimaryIdentifier.get());
577+
return true;
561578
}
579+
return false;
562580
}
563581

564582
private static void setRemoteResourceAccessKeyAndRegion(
565583
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);
569587
}
570588

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);
574592
}
575593
}
576594

577-
private static void setRemoteResourceAccountIdAndRegion(
595+
private static boolean setRemoteResourceAccountIdAndRegion(
578596
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();
581599

582600
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);
600604
} else {
601605
for (AttributeKey<String> attributeKey : ARN_ATTRIBUTES) {
602606
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+
}
607618
}
608619
}
609620
}
610621

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));
616648
}
649+
return Optional.empty();
617650
}
618651

619652
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();
622662
}
623663

624664
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();
627673
}
628674

629675
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();
632684
}
633685

634686
/**

awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsSpanProcessingUtil.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,6 @@ final class AwsSpanProcessingUtil {
5252
static final String UNKNOWN_OPERATION = "UnknownOperation";
5353
static final String UNKNOWN_REMOTE_SERVICE = "UnknownRemoteService";
5454
static final String UNKNOWN_REMOTE_OPERATION = "UnknownRemoteOperation";
55-
static final String UNKNOWN_REMOTE_ACCOUNT_ID = "UnknownRemoteAccountId";
56-
static final String UNKNOWN_REMOTE_REGION = "UnknownRemoteRegion";
5755
static final String INTERNAL_OPERATION = "InternalOperation";
5856
static final String LOCAL_ROOT = "LOCAL_ROOT";
5957
static final String SQS_RECEIVE_MESSAGE_SPAN_NAME = "Sqs.ReceiveMessage";

awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/SqsUrlParser.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,42 @@ public static Optional<String> getQueueName(String url) {
4040
return Optional.empty();
4141
}
4242

43+
public static Optional<String> getAccountId(String url) {
44+
if (url == null) {
45+
return Optional.empty();
46+
}
47+
url = url.replace(HTTP_SCHEMA, "").replace(HTTPS_SCHEMA, "");
48+
if (isValidSqsUrl(url)) {
49+
String[] splitUrl = url.split("/");
50+
return Optional.of(splitUrl[1]);
51+
}
52+
return Optional.empty();
53+
}
54+
55+
public static Optional<String> getRegion(String url) {
56+
if (url == null) {
57+
return Optional.empty();
58+
}
59+
url = url.replace(HTTP_SCHEMA, "").replace(HTTPS_SCHEMA, "");
60+
if (isValidSqsUrl(url)) {
61+
String[] splitUrl = url.split("/");
62+
String domain = splitUrl[0];
63+
String[] domainParts = domain.split("\\.");
64+
if (domainParts.length == 4) {
65+
return Optional.of(domainParts[1]);
66+
}
67+
}
68+
return Optional.empty();
69+
}
70+
71+
private static boolean isValidSqsUrl(String url) {
72+
String[] splitUrl = url.split("/");
73+
return splitUrl.length == 3
74+
&& splitUrl[0].startsWith("sqs")
75+
&& isAccountId(splitUrl[1])
76+
&& isValidQueueName(splitUrl[2]);
77+
}
78+
4379
private static boolean isAccountId(String input) {
4480
if (input == null || input.length() != 12) {
4581
return false;

0 commit comments

Comments
 (0)