From 1f21e65d27383ecc20ae4cbf3c0587b1bad6f1ff Mon Sep 17 00:00:00 2001 From: Michele Mancioppi Date: Fri, 26 Jan 2024 13:27:31 +0100 Subject: [PATCH 1/7] AWS ECS Detector: set cloud.account.id, cloud.availability_zone and cloud.region --- .../contrib/aws/resource/EcsResource.java | 47 +++++++++++++++++-- .../contrib/aws/resource/EcsResourceTest.java | 6 +++ 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/aws-resources/src/main/java/io/opentelemetry/contrib/aws/resource/EcsResource.java b/aws-resources/src/main/java/io/opentelemetry/contrib/aws/resource/EcsResource.java index badc358f4..b7124f319 100644 --- a/aws-resources/src/main/java/io/opentelemetry/contrib/aws/resource/EcsResource.java +++ b/aws-resources/src/main/java/io/opentelemetry/contrib/aws/resource/EcsResource.java @@ -47,6 +47,24 @@ private static Resource buildResource() { return buildResource(System.getenv(), new SimpleHttpClient()); } + @Nullable + private static String getAccountId(@Nullable String arn) { + if (arn != null) { + return arn.split(":")[4]; + } + + return null; + } + + @Nullable + private static String getRegion(@Nullable String arn) { + if (arn != null) { + return arn.split(":")[3]; + } + + return null; + } + // Visible for testing static Resource buildResource(Map sysEnv, SimpleHttpClient httpClient) { // Note: If V4 is set V3 is set as well, so check V4 first. @@ -107,9 +125,14 @@ static void parseResponse( return; } + String accountId = null; + String region = null; while (parser.nextToken() != JsonToken.END_OBJECT) { String value = parser.nextTextValue(); switch (parser.getCurrentName()) { + case "AvailabilityZone": + attrBuilders.put(ResourceAttributes.CLOUD_AVAILABILITY_ZONE, value); + break; case "DockerId": attrBuilders.put(ResourceAttributes.CONTAINER_ID, value); break; @@ -117,6 +140,12 @@ static void parseResponse( attrBuilders.put(ResourceAttributes.CONTAINER_NAME, value); break; case "ContainerARN": + if (accountId == null) { + accountId = getAccountId(value); + } + if (region == null) { + region = getRegion(value); + } attrBuilders.put(ResourceAttributes.AWS_ECS_CONTAINER_ARN, value); logArnBuilder.setContainerArn(value); break; @@ -146,6 +175,12 @@ static void parseResponse( logArnBuilder.setRegion(value); break; case "TaskARN": + if (accountId == null) { + accountId = getAccountId(value); + } + if (region == null) { + region = getRegion(value); + } attrBuilders.put(ResourceAttributes.AWS_ECS_TASK_ARN, value); break; case "LaunchType": @@ -162,6 +197,14 @@ static void parseResponse( break; } } + + if (accountId != null) { + attrBuilders.put(ResourceAttributes.CLOUD_ACCOUNT_ID, accountId); + } + + if (region != null) { + attrBuilders.put(ResourceAttributes.CLOUD_REGION, region); + } } private EcsResource() {} @@ -193,9 +236,7 @@ void setLogStreamName(@Nullable String logStreamName) { } void setContainerArn(@Nullable String containerArn) { - if (containerArn != null) { - account = containerArn.split(":")[4]; - } + account = getAccountId(containerArn); } Optional getLogGroupArn() { diff --git a/aws-resources/src/test/java/io/opentelemetry/contrib/aws/resource/EcsResourceTest.java b/aws-resources/src/test/java/io/opentelemetry/contrib/aws/resource/EcsResourceTest.java index e822d88b0..b0b9ac463 100644 --- a/aws-resources/src/test/java/io/opentelemetry/contrib/aws/resource/EcsResourceTest.java +++ b/aws-resources/src/test/java/io/opentelemetry/contrib/aws/resource/EcsResourceTest.java @@ -51,6 +51,9 @@ void testCreateAttributesV3() throws IOException { .containsOnly( entry(ResourceAttributes.CLOUD_PROVIDER, "aws"), entry(ResourceAttributes.CLOUD_PLATFORM, "aws_ecs"), + entry(ResourceAttributes.CLOUD_ACCOUNT_ID, "012345678910"), + entry(ResourceAttributes.CLOUD_REGION, "us-east-2"), + entry(ResourceAttributes.CLOUD_AVAILABILITY_ZONE, "us-east-2b"), entry(ResourceAttributes.CONTAINER_NAME, "ecs-nginx-5-nginx-curl-ccccb9f49db0dfe0d901"), entry( ResourceAttributes.CONTAINER_ID, @@ -85,6 +88,9 @@ void testCreateAttributesV4() throws IOException { .containsOnly( entry(ResourceAttributes.CLOUD_PROVIDER, "aws"), entry(ResourceAttributes.CLOUD_PLATFORM, "aws_ecs"), + entry(ResourceAttributes.CLOUD_ACCOUNT_ID, "111122223333"), + entry(ResourceAttributes.CLOUD_REGION, "us-west-2"), + entry(ResourceAttributes.CLOUD_AVAILABILITY_ZONE, "us-west-2d"), entry(ResourceAttributes.CONTAINER_NAME, "ecs-curltest-26-curl-cca48e8dcadd97805600"), entry( ResourceAttributes.CONTAINER_ID, From 4d113e6bde62091dda1613eb12e000945ef10a4a Mon Sep 17 00:00:00 2001 From: Michele Mancioppi Date: Fri, 26 Jan 2024 14:27:28 +0100 Subject: [PATCH 2/7] fix lint --- .../contrib/aws/resource/EcsResource.java | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/aws-resources/src/main/java/io/opentelemetry/contrib/aws/resource/EcsResource.java b/aws-resources/src/main/java/io/opentelemetry/contrib/aws/resource/EcsResource.java index b7124f319..31a5b54c6 100644 --- a/aws-resources/src/main/java/io/opentelemetry/contrib/aws/resource/EcsResource.java +++ b/aws-resources/src/main/java/io/opentelemetry/contrib/aws/resource/EcsResource.java @@ -47,24 +47,6 @@ private static Resource buildResource() { return buildResource(System.getenv(), new SimpleHttpClient()); } - @Nullable - private static String getAccountId(@Nullable String arn) { - if (arn != null) { - return arn.split(":")[4]; - } - - return null; - } - - @Nullable - private static String getRegion(@Nullable String arn) { - if (arn != null) { - return arn.split(":")[3]; - } - - return null; - } - // Visible for testing static Resource buildResource(Map sysEnv, SimpleHttpClient httpClient) { // Note: If V4 is set V3 is set as well, so check V4 first. @@ -117,6 +99,24 @@ static void fetchMetadata( } } + @Nullable + private static String getAccountId(@Nullable String arn) { + if (arn != null) { + return arn.split(":")[4]; + } + + return null; + } + + @Nullable + private static String getRegion(@Nullable String arn) { + if (arn != null) { + return arn.split(":")[3]; + } + + return null; + } + static void parseResponse( JsonParser parser, AttributesBuilder attrBuilders, LogArnBuilder logArnBuilder) throws IOException { From d6dd717f6ac8dcb7615552f1f9683977dd9241f2 Mon Sep 17 00:00:00 2001 From: Michele Mancioppi Date: Fri, 2 Feb 2024 17:56:00 +0100 Subject: [PATCH 3/7] fix: harden ARN parsing --- .../contrib/aws/resource/EcsResource.java | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/aws-resources/src/main/java/io/opentelemetry/contrib/aws/resource/EcsResource.java b/aws-resources/src/main/java/io/opentelemetry/contrib/aws/resource/EcsResource.java index 31a5b54c6..d498766cd 100644 --- a/aws-resources/src/main/java/io/opentelemetry/contrib/aws/resource/EcsResource.java +++ b/aws-resources/src/main/java/io/opentelemetry/contrib/aws/resource/EcsResource.java @@ -101,17 +101,24 @@ static void fetchMetadata( @Nullable private static String getAccountId(@Nullable String arn) { - if (arn != null) { - return arn.split(":")[4]; - } - - return null; + return getArnPart(arn, 4); } @Nullable private static String getRegion(@Nullable String arn) { - if (arn != null) { - return arn.split(":")[3]; + return getArnPart(arn, 3); + } + + @Nullable + private static String getArnPart(@Nullable String arn, int partIndex) { + if (arn == null) { + return null; + } + + String[] arnParts = arn.split(":"); + + if (partIndex < arnParts.length) { + return arnParts[partIndex]; } return null; From 367c7150199ab76166794d9763b0d25a8693bfda Mon Sep 17 00:00:00 2001 From: Michele Mancioppi Date: Tue, 6 Feb 2024 10:04:46 +0100 Subject: [PATCH 4/7] use Optional to reduce code duplication --- .../contrib/aws/resource/EcsResource.java | 66 +++++++++---------- 1 file changed, 32 insertions(+), 34 deletions(-) diff --git a/aws-resources/src/main/java/io/opentelemetry/contrib/aws/resource/EcsResource.java b/aws-resources/src/main/java/io/opentelemetry/contrib/aws/resource/EcsResource.java index d498766cd..d4974166e 100644 --- a/aws-resources/src/main/java/io/opentelemetry/contrib/aws/resource/EcsResource.java +++ b/aws-resources/src/main/java/io/opentelemetry/contrib/aws/resource/EcsResource.java @@ -99,29 +99,39 @@ static void fetchMetadata( } } - @Nullable - private static String getAccountId(@Nullable String arn) { - return getArnPart(arn, 4); + private static Optional getAccountId(@Nullable String arn) { + return getArnPart(arn, ArnPart.ACCOUNT); } - @Nullable - private static String getRegion(@Nullable String arn) { - return getArnPart(arn, 3); + private static Optional getRegion(@Nullable String arn) { + return getArnPart(arn, ArnPart.REGION); } - @Nullable - private static String getArnPart(@Nullable String arn, int partIndex) { + private static enum ArnPart { + + REGION(3), + ACCOUNT(4); + + final int partIndex; + + private ArnPart(int partIndex) { + this.partIndex = partIndex; + } + + } + + private static Optional getArnPart(@Nullable String arn, ArnPart arnPart) { if (arn == null) { - return null; + return Optional.empty(); } String[] arnParts = arn.split(":"); - if (partIndex < arnParts.length) { - return arnParts[partIndex]; + if (arnPart.partIndex >= arnParts.length) { + return Optional.empty(); } - - return null; + + return Optional.of(arnParts[arnPart.partIndex]); } static void parseResponse( @@ -132,8 +142,11 @@ static void parseResponse( return; } - String accountId = null; - String region = null; + // Either the container ARN or the task ARN, they both contain the + // account id and region tokens we need later for the cloud.account.id + // and cloud.region attributes. + String arn = null; + while (parser.nextToken() != JsonToken.END_OBJECT) { String value = parser.nextTextValue(); switch (parser.getCurrentName()) { @@ -147,12 +160,7 @@ static void parseResponse( attrBuilders.put(ResourceAttributes.CONTAINER_NAME, value); break; case "ContainerARN": - if (accountId == null) { - accountId = getAccountId(value); - } - if (region == null) { - region = getRegion(value); - } + arn = value; attrBuilders.put(ResourceAttributes.AWS_ECS_CONTAINER_ARN, value); logArnBuilder.setContainerArn(value); break; @@ -182,12 +190,7 @@ static void parseResponse( logArnBuilder.setRegion(value); break; case "TaskARN": - if (accountId == null) { - accountId = getAccountId(value); - } - if (region == null) { - region = getRegion(value); - } + arn = value; attrBuilders.put(ResourceAttributes.AWS_ECS_TASK_ARN, value); break; case "LaunchType": @@ -205,13 +208,8 @@ static void parseResponse( } } - if (accountId != null) { - attrBuilders.put(ResourceAttributes.CLOUD_ACCOUNT_ID, accountId); - } - - if (region != null) { - attrBuilders.put(ResourceAttributes.CLOUD_REGION, region); - } + getRegion(arn).ifPresent(region -> attrBuilders.put(ResourceAttributes.CLOUD_REGION, region)); + getAccountId(arn).ifPresent(accountId -> attrBuilders.put(ResourceAttributes.CLOUD_ACCOUNT_ID, accountId)); } private EcsResource() {} From 9dd2654ee48628ddfd7c573a596b2861ac070c5d Mon Sep 17 00:00:00 2001 From: Michele Mancioppi Date: Tue, 6 Feb 2024 12:29:51 +0100 Subject: [PATCH 5/7] fix: compile error I somehow did not catch in local tests --- .../java/io/opentelemetry/contrib/aws/resource/EcsResource.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws-resources/src/main/java/io/opentelemetry/contrib/aws/resource/EcsResource.java b/aws-resources/src/main/java/io/opentelemetry/contrib/aws/resource/EcsResource.java index d4974166e..7aca931b8 100644 --- a/aws-resources/src/main/java/io/opentelemetry/contrib/aws/resource/EcsResource.java +++ b/aws-resources/src/main/java/io/opentelemetry/contrib/aws/resource/EcsResource.java @@ -241,7 +241,7 @@ void setLogStreamName(@Nullable String logStreamName) { } void setContainerArn(@Nullable String containerArn) { - account = getAccountId(containerArn); + account = getAccountId(containerArn).orElse(null); } Optional getLogGroupArn() { From e99c608e7876c92c5bdc689f0554f05fef746d9e Mon Sep 17 00:00:00 2001 From: Michele Mancioppi Date: Tue, 6 Feb 2024 12:32:02 +0100 Subject: [PATCH 6/7] fix: lint --- .../io/opentelemetry/contrib/aws/resource/EcsResource.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/aws-resources/src/main/java/io/opentelemetry/contrib/aws/resource/EcsResource.java b/aws-resources/src/main/java/io/opentelemetry/contrib/aws/resource/EcsResource.java index 7aca931b8..824435c71 100644 --- a/aws-resources/src/main/java/io/opentelemetry/contrib/aws/resource/EcsResource.java +++ b/aws-resources/src/main/java/io/opentelemetry/contrib/aws/resource/EcsResource.java @@ -108,7 +108,6 @@ private static Optional getRegion(@Nullable String arn) { } private static enum ArnPart { - REGION(3), ACCOUNT(4); @@ -117,7 +116,6 @@ private static enum ArnPart { private ArnPart(int partIndex) { this.partIndex = partIndex; } - } private static Optional getArnPart(@Nullable String arn, ArnPart arnPart) { @@ -130,7 +128,7 @@ private static Optional getArnPart(@Nullable String arn, ArnPart arnPart if (arnPart.partIndex >= arnParts.length) { return Optional.empty(); } - + return Optional.of(arnParts[arnPart.partIndex]); } @@ -209,7 +207,8 @@ static void parseResponse( } getRegion(arn).ifPresent(region -> attrBuilders.put(ResourceAttributes.CLOUD_REGION, region)); - getAccountId(arn).ifPresent(accountId -> attrBuilders.put(ResourceAttributes.CLOUD_ACCOUNT_ID, accountId)); + getAccountId(arn) + .ifPresent(accountId -> attrBuilders.put(ResourceAttributes.CLOUD_ACCOUNT_ID, accountId)); } private EcsResource() {} From c37d759a46bdfad7027975d6abae272c4a2a7d10 Mon Sep 17 00:00:00 2001 From: Michele Mancioppi Date: Fri, 9 Feb 2024 15:31:51 +0100 Subject: [PATCH 7/7] add cloud.resource_id --- CHANGELOG.md | 5 +++++ .../io/opentelemetry/contrib/aws/resource/EcsResource.java | 1 + .../opentelemetry/contrib/aws/resource/EcsResourceTest.java | 3 +++ 3 files changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c0b59878e..97f5447fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ## Unreleased +### Resource providers + +- Add support `cloud.account.id`, `cloud.availability_zone`, `cloud.region` and `cloud.resource_id` + ([#1171](https://github.com/open-telemetry/opentelemetry-java-contrib/pull/1171)) + ## Version 1.32.0 (2023-11-27) ### Disk buffering diff --git a/aws-resources/src/main/java/io/opentelemetry/contrib/aws/resource/EcsResource.java b/aws-resources/src/main/java/io/opentelemetry/contrib/aws/resource/EcsResource.java index 824435c71..f61221d04 100644 --- a/aws-resources/src/main/java/io/opentelemetry/contrib/aws/resource/EcsResource.java +++ b/aws-resources/src/main/java/io/opentelemetry/contrib/aws/resource/EcsResource.java @@ -160,6 +160,7 @@ static void parseResponse( case "ContainerARN": arn = value; attrBuilders.put(ResourceAttributes.AWS_ECS_CONTAINER_ARN, value); + attrBuilders.put(ResourceAttributes.CLOUD_RESOURCE_ID, value); logArnBuilder.setContainerArn(value); break; case "Image": diff --git a/aws-resources/src/test/java/io/opentelemetry/contrib/aws/resource/EcsResourceTest.java b/aws-resources/src/test/java/io/opentelemetry/contrib/aws/resource/EcsResourceTest.java index b0b9ac463..d56eeb065 100644 --- a/aws-resources/src/test/java/io/opentelemetry/contrib/aws/resource/EcsResourceTest.java +++ b/aws-resources/src/test/java/io/opentelemetry/contrib/aws/resource/EcsResourceTest.java @@ -90,6 +90,9 @@ void testCreateAttributesV4() throws IOException { entry(ResourceAttributes.CLOUD_PLATFORM, "aws_ecs"), entry(ResourceAttributes.CLOUD_ACCOUNT_ID, "111122223333"), entry(ResourceAttributes.CLOUD_REGION, "us-west-2"), + entry( + ResourceAttributes.CLOUD_RESOURCE_ID, + "arn:aws:ecs:us-west-2:111122223333:container/0206b271-b33f-47ab-86c6-a0ba208a70a9"), entry(ResourceAttributes.CLOUD_AVAILABILITY_ZONE, "us-west-2d"), entry(ResourceAttributes.CONTAINER_NAME, "ecs-curltest-26-curl-cca48e8dcadd97805600"), entry(