|
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