|
40 | 40 | import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_QUEUE_NAME; |
41 | 41 | import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_QUEUE_URL; |
42 | 42 | import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_REMOTE_OPERATION; |
| 43 | +import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_REMOTE_RESOURCE_IDENTIFIER; |
| 44 | +import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_REMOTE_RESOURCE_TYPE; |
43 | 45 | import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_REMOTE_SERVICE; |
44 | | -import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_REMOTE_TARGET; |
45 | 46 | import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_SPAN_KIND; |
46 | 47 | import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_STREAM_NAME; |
47 | 48 | import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_TABLE_NAME; |
@@ -88,6 +89,12 @@ final class AwsMetricAttributeGenerator implements MetricAttributeGenerator { |
88 | 89 | private static final Logger logger = |
89 | 90 | Logger.getLogger(AwsMetricAttributeGenerator.class.getName()); |
90 | 91 |
|
| 92 | + // Normalized remote service names for supported AWS services |
| 93 | + private static final String NORMALIZED_DYNAMO_DB_SERVICE_NAME = "AWS::DynamoDB"; |
| 94 | + private static final String NORMALIZED_KINESIS_SERVICE_NAME = "AWS::Kinesis"; |
| 95 | + private static final String NORMALIZED_S3_SERVICE_NAME = "AWS::S3"; |
| 96 | + private static final String NORMALIZED_SQS_SERVICE_NAME = "AWS::SQS"; |
| 97 | + |
91 | 98 | // Special DEPENDENCY attribute value if GRAPHQL_OPERATION_TYPE attribute key is present. |
92 | 99 | private static final String GRAPHQL = "graphql"; |
93 | 100 |
|
@@ -129,51 +136,13 @@ private Attributes generateDependencyMetricAttributes(SpanData span, Resource re |
129 | 136 | setService(resource, span, builder); |
130 | 137 | setEgressOperation(span, builder); |
131 | 138 | setRemoteServiceAndOperation(span, builder); |
132 | | - setRemoteTarget(span, builder); |
| 139 | + setRemoteResourceTypeAndIdentifier(span, builder); |
133 | 140 | setSpanKindForDependency(span, builder); |
134 | 141 | setHttpStatus(span, builder); |
135 | 142 |
|
136 | 143 | return builder.build(); |
137 | 144 | } |
138 | 145 |
|
139 | | - private static void setRemoteTarget(SpanData span, AttributesBuilder builder) { |
140 | | - Optional<String> remoteTarget = getRemoteTarget(span); |
141 | | - remoteTarget.ifPresent(s -> builder.put(AWS_REMOTE_TARGET, s)); |
142 | | - } |
143 | | - |
144 | | - /** |
145 | | - * RemoteTarget attribute {@link AwsAttributeKeys#AWS_REMOTE_TARGET} is used to store the resource |
146 | | - * name of the remote invokes, such as S3 bucket name, mysql table name, etc. TODO: currently only |
147 | | - * support AWS resource name, will be extended to support the general remote targets, such as |
148 | | - * ActiveMQ name, etc. |
149 | | - */ |
150 | | - private static Optional<String> getRemoteTarget(SpanData span) { |
151 | | - if (isKeyPresent(span, AWS_BUCKET_NAME)) { |
152 | | - return Optional.ofNullable("::s3:::" + span.getAttributes().get(AWS_BUCKET_NAME)); |
153 | | - } |
154 | | - |
155 | | - if (isKeyPresent(span, AWS_QUEUE_URL)) { |
156 | | - String arn = SqsUrlParser.getSqsRemoteTarget(span.getAttributes().get(AWS_QUEUE_URL)); |
157 | | - |
158 | | - if (arn != null) { |
159 | | - return Optional.ofNullable(arn); |
160 | | - } |
161 | | - } |
162 | | - |
163 | | - if (isKeyPresent(span, AWS_QUEUE_NAME)) { |
164 | | - return Optional.ofNullable("::sqs:::" + span.getAttributes().get(AWS_QUEUE_NAME)); |
165 | | - } |
166 | | - |
167 | | - if (isKeyPresent(span, AWS_STREAM_NAME)) { |
168 | | - return Optional.ofNullable("::kinesis:::stream/" + span.getAttributes().get(AWS_STREAM_NAME)); |
169 | | - } |
170 | | - |
171 | | - if (isKeyPresent(span, AWS_TABLE_NAME)) { |
172 | | - return Optional.ofNullable("::dynamodb:::table/" + span.getAttributes().get(AWS_TABLE_NAME)); |
173 | | - } |
174 | | - return Optional.empty(); |
175 | | - } |
176 | | - |
177 | 146 | /** Service is always derived from {@link ResourceAttributes#SERVICE_NAME} */ |
178 | 147 | private static void setService(Resource resource, SpanData span, AttributesBuilder builder) { |
179 | 148 | String service = resource.getAttribute(SERVICE_NAME); |
@@ -213,14 +182,6 @@ private static void setEgressOperation(SpanData span, AttributesBuilder builder) |
213 | 182 | builder.put(AWS_LOCAL_OPERATION, operation); |
214 | 183 | } |
215 | 184 |
|
216 | | - // add `AWS.SDK.` as prefix to indicate the metrics resulted from current span is from AWS SDK |
217 | | - private static String normalizeServiceName(SpanData span, String serviceName) { |
218 | | - if (AwsSpanProcessingUtil.isAwsSDKSpan(span)) { |
219 | | - return "AWS.SDK." + serviceName; |
220 | | - } |
221 | | - return serviceName; |
222 | | - } |
223 | | - |
224 | 185 | /** |
225 | 186 | * Remote attributes (only for Client and Producer spans) are generated based on low-cardinality |
226 | 187 | * span attributes, in priority order. |
@@ -265,7 +226,7 @@ private static void setRemoteServiceAndOperation(SpanData span, AttributesBuilde |
265 | 226 | remoteService = getRemoteService(span, AWS_REMOTE_SERVICE); |
266 | 227 | remoteOperation = getRemoteOperation(span, AWS_REMOTE_OPERATION); |
267 | 228 | } else if (isKeyPresent(span, RPC_SERVICE) || isKeyPresent(span, RPC_METHOD)) { |
268 | | - remoteService = normalizeServiceName(span, getRemoteService(span, RPC_SERVICE)); |
| 229 | + remoteService = normalizeRemoteServiceName(span, getRemoteService(span, RPC_SERVICE)); |
269 | 230 | remoteOperation = getRemoteOperation(span, RPC_METHOD); |
270 | 231 | } else if (isKeyPresent(span, DB_SYSTEM) |
271 | 232 | || isKeyPresent(span, DB_OPERATION) |
@@ -365,6 +326,72 @@ private static String generateRemoteService(SpanData span) { |
365 | 326 | return remoteService; |
366 | 327 | } |
367 | 328 |
|
| 329 | + /** |
| 330 | + * If the span is an AWS SDK span, normalize the name to align with <a |
| 331 | + * href="https://docs.aws.amazon.com/cloudcontrolapi/latest/userguide/supported-resources.html">AWS |
| 332 | + * Cloud Control resource format</a> as much as possible, with special attention to services we |
| 333 | + * can detect remote resource information for. Long term, we would like to normalize service name |
| 334 | + * in the upstream. |
| 335 | + */ |
| 336 | + private static String normalizeRemoteServiceName(SpanData span, String serviceName) { |
| 337 | + if (AwsSpanProcessingUtil.isAwsSDKSpan(span)) { |
| 338 | + switch (serviceName) { |
| 339 | + case "AmazonDynamoDBv2": // AWS SDK v1 |
| 340 | + case "DynamoDb": // AWS SDK v2 |
| 341 | + return NORMALIZED_DYNAMO_DB_SERVICE_NAME; |
| 342 | + case "AmazonKinesis": // AWS SDK v1 |
| 343 | + case "Kinesis": // AWS SDK v2 |
| 344 | + return NORMALIZED_KINESIS_SERVICE_NAME; |
| 345 | + case "Amazon S3": // AWS SDK v1 |
| 346 | + case "S3": // AWS SDK v2 |
| 347 | + return NORMALIZED_S3_SERVICE_NAME; |
| 348 | + case "AmazonSQS": // AWS SDK v1 |
| 349 | + case "Sqs": // AWS SDK v2 |
| 350 | + return NORMALIZED_SQS_SERVICE_NAME; |
| 351 | + default: |
| 352 | + return "AWS::" + serviceName; |
| 353 | + } |
| 354 | + } |
| 355 | + return serviceName; |
| 356 | + } |
| 357 | + |
| 358 | + /** |
| 359 | + * Remote resource attributes {@link AwsAttributeKeys#AWS_REMOTE_RESOURCE_TYPE} and {@link |
| 360 | + * AwsAttributeKeys#AWS_REMOTE_RESOURCE_IDENTIFIER} are used to store information about the |
| 361 | + * resource associated with the remote invocation, such as S3 bucket name, etc. We should only |
| 362 | + * ever set both type and identifier or neither. |
| 363 | + * |
| 364 | + * <p>AWS resources type and identifier adhere to <a |
| 365 | + * href="https://docs.aws.amazon.com/cloudcontrolapi/latest/userguide/supported-resources.html">AWS |
| 366 | + * Cloud Control resource format</a>. |
| 367 | + */ |
| 368 | + private static void setRemoteResourceTypeAndIdentifier(SpanData span, AttributesBuilder builder) { |
| 369 | + Optional<String> remoteResourceType = Optional.empty(); |
| 370 | + Optional<String> remoteResourceIdentifier = Optional.empty(); |
| 371 | + |
| 372 | + if (isKeyPresent(span, AWS_TABLE_NAME)) { |
| 373 | + remoteResourceType = Optional.of(NORMALIZED_DYNAMO_DB_SERVICE_NAME + "::Table"); |
| 374 | + remoteResourceIdentifier = Optional.ofNullable(span.getAttributes().get(AWS_TABLE_NAME)); |
| 375 | + } else if (isKeyPresent(span, AWS_STREAM_NAME)) { |
| 376 | + remoteResourceType = Optional.of(NORMALIZED_KINESIS_SERVICE_NAME + "::Stream"); |
| 377 | + remoteResourceIdentifier = Optional.ofNullable(span.getAttributes().get(AWS_STREAM_NAME)); |
| 378 | + } else if (isKeyPresent(span, AWS_BUCKET_NAME)) { |
| 379 | + remoteResourceType = Optional.of(NORMALIZED_S3_SERVICE_NAME + "::Bucket"); |
| 380 | + remoteResourceIdentifier = Optional.ofNullable(span.getAttributes().get(AWS_BUCKET_NAME)); |
| 381 | + } else if (isKeyPresent(span, AWS_QUEUE_NAME)) { |
| 382 | + remoteResourceType = Optional.of(NORMALIZED_SQS_SERVICE_NAME + "::Queue"); |
| 383 | + remoteResourceIdentifier = Optional.ofNullable(span.getAttributes().get(AWS_QUEUE_NAME)); |
| 384 | + } else if (isKeyPresent(span, AWS_QUEUE_URL)) { |
| 385 | + remoteResourceType = Optional.of(NORMALIZED_SQS_SERVICE_NAME + "::Queue"); |
| 386 | + remoteResourceIdentifier = SqsUrlParser.getQueueName(span.getAttributes().get(AWS_QUEUE_URL)); |
| 387 | + } |
| 388 | + |
| 389 | + if (remoteResourceType.isPresent() && remoteResourceIdentifier.isPresent()) { |
| 390 | + builder.put(AWS_REMOTE_RESOURCE_TYPE, remoteResourceType.get()); |
| 391 | + builder.put(AWS_REMOTE_RESOURCE_IDENTIFIER, remoteResourceIdentifier.get()); |
| 392 | + } |
| 393 | + } |
| 394 | + |
368 | 395 | /** Span kind is needed for differentiating metrics in the EMF exporter */ |
369 | 396 | private static void setSpanKindForService(SpanData span, AttributesBuilder builder) { |
370 | 397 | String spanKind = span.getKind().name(); |
|
0 commit comments