4242import static io .opentelemetry .semconv .SemanticAttributes .SERVER_SOCKET_PORT ;
4343import static software .amazon .opentelemetry .javaagent .providers .AwsAttributeKeys .AWS_AGENT_ID ;
4444import static software .amazon .opentelemetry .javaagent .providers .AwsAttributeKeys .AWS_BUCKET_NAME ;
45+ import static software .amazon .opentelemetry .javaagent .providers .AwsAttributeKeys .AWS_CLOUDFORMATION_PRIMARY_IDENTIFIER ;
4546import static software .amazon .opentelemetry .javaagent .providers .AwsAttributeKeys .AWS_DATA_SOURCE_ID ;
4647import static software .amazon .opentelemetry .javaagent .providers .AwsAttributeKeys .AWS_GUARDRAIL_ID ;
4748import static software .amazon .opentelemetry .javaagent .providers .AwsAttributeKeys .AWS_KNOWLEDGE_BASE_ID ;
49+ import static software .amazon .opentelemetry .javaagent .providers .AwsAttributeKeys .AWS_LAMBDA_NAME ;
50+ import static software .amazon .opentelemetry .javaagent .providers .AwsAttributeKeys .AWS_LAMBDA_RESOURCE_ID ;
4851import static software .amazon .opentelemetry .javaagent .providers .AwsAttributeKeys .AWS_LOCAL_OPERATION ;
4952import static software .amazon .opentelemetry .javaagent .providers .AwsAttributeKeys .AWS_LOCAL_SERVICE ;
5053import static software .amazon .opentelemetry .javaagent .providers .AwsAttributeKeys .AWS_QUEUE_NAME ;
5457import static software .amazon .opentelemetry .javaagent .providers .AwsAttributeKeys .AWS_REMOTE_RESOURCE_IDENTIFIER ;
5558import static software .amazon .opentelemetry .javaagent .providers .AwsAttributeKeys .AWS_REMOTE_RESOURCE_TYPE ;
5659import static software .amazon .opentelemetry .javaagent .providers .AwsAttributeKeys .AWS_REMOTE_SERVICE ;
60+ import static software .amazon .opentelemetry .javaagent .providers .AwsAttributeKeys .AWS_SECRET_ARN ;
61+ import static software .amazon .opentelemetry .javaagent .providers .AwsAttributeKeys .AWS_SNS_TOPIC_ARN ;
5762import static software .amazon .opentelemetry .javaagent .providers .AwsAttributeKeys .AWS_SPAN_KIND ;
63+ import static software .amazon .opentelemetry .javaagent .providers .AwsAttributeKeys .AWS_STATE_MACHINE_ARN ;
64+ import static software .amazon .opentelemetry .javaagent .providers .AwsAttributeKeys .AWS_STEP_FUNCTIONS_ACTIVITY_ARN ;
5865import static software .amazon .opentelemetry .javaagent .providers .AwsAttributeKeys .AWS_STREAM_NAME ;
5966import static software .amazon .opentelemetry .javaagent .providers .AwsAttributeKeys .AWS_TABLE_NAME ;
6067import static software .amazon .opentelemetry .javaagent .providers .AwsSpanProcessingUtil .GEN_AI_REQUEST_MODEL ;
6774import static software .amazon .opentelemetry .javaagent .providers .AwsSpanProcessingUtil .isDBSpan ;
6875import static software .amazon .opentelemetry .javaagent .providers .AwsSpanProcessingUtil .isKeyPresent ;
6976
77+ import com .amazonaws .arn .Arn ;
7078import io .opentelemetry .api .common .AttributeKey ;
7179import io .opentelemetry .api .common .Attributes ;
7280import io .opentelemetry .api .common .AttributesBuilder ;
@@ -111,6 +119,10 @@ final class AwsMetricAttributeGenerator implements MetricAttributeGenerator {
111119 private static final String NORMALIZED_SQS_SERVICE_NAME = "AWS::SQS" ;
112120 private static final String NORMALIZED_BEDROCK_SERVICE_NAME = "AWS::Bedrock" ;
113121 private static final String NORMALIZED_BEDROCK_RUNTIME_SERVICE_NAME = "AWS::BedrockRuntime" ;
122+ private static final String NORMALIZED_STEPFUNCTIONS_SERVICE_NAME = "AWS::StepFunctions" ;
123+ private static final String NORMALIZED_SNS_SERVICE_NAME = "AWS::SNS" ;
124+ private static final String NORMALIZED_SECRETSMANAGER_SERVICE_NAME = "AWS::SecretsManager" ;
125+ private static final String NORMALIZED_LAMBDA_SERVICE_NAME = "AWS::Lambda" ;
114126
115127 // Special DEPENDENCY attribute value if GRAPHQL_OPERATION_TYPE attribute key is present.
116128 private static final String GRAPHQL = "graphql" ;
@@ -371,6 +383,18 @@ private static String normalizeRemoteServiceName(SpanData span, String serviceNa
371383 case "AmazonBedrockRuntime" : // AWS SDK v1
372384 case "BedrockRuntime" : // AWS SDK v2
373385 return NORMALIZED_BEDROCK_RUNTIME_SERVICE_NAME ;
386+ case "AWSStepFunctions" : // AWS SDK v1
387+ case "Sfn" : // AWS SDK v2
388+ return NORMALIZED_STEPFUNCTIONS_SERVICE_NAME ;
389+ case "AmazonSNS" :
390+ case "Sns" :
391+ return NORMALIZED_SNS_SERVICE_NAME ;
392+ case "AWSSecretsManager" : // AWS SDK v1
393+ case "SecretsManager" : // AWS SDK v2
394+ return NORMALIZED_SECRETSMANAGER_SERVICE_NAME ;
395+ case "AWSLambda" : // AWS SDK v1
396+ case "Lambda" : // AWS SDK v2
397+ return NORMALIZED_LAMBDA_SERVICE_NAME ;
374398 default :
375399 return "AWS::" + serviceName ;
376400 }
@@ -392,6 +416,7 @@ private static String normalizeRemoteServiceName(SpanData span, String serviceNa
392416 private static void setRemoteResourceTypeAndIdentifier (SpanData span , AttributesBuilder builder ) {
393417 Optional <String > remoteResourceType = Optional .empty ();
394418 Optional <String > remoteResourceIdentifier = Optional .empty ();
419+ Optional <String > cloudformationPrimaryIdentifier = Optional .empty ();
395420
396421 if (isAwsSDKSpan (span )) {
397422 if (isKeyPresent (span , AWS_TABLE_NAME )) {
@@ -434,18 +459,88 @@ private static void setRemoteResourceTypeAndIdentifier(SpanData span, Attributes
434459 remoteResourceType = Optional .of (NORMALIZED_BEDROCK_SERVICE_NAME + "::Model" );
435460 remoteResourceIdentifier =
436461 Optional .ofNullable (escapeDelimiters (span .getAttributes ().get (GEN_AI_REQUEST_MODEL )));
462+ } else if (isKeyPresent (span , AWS_STATE_MACHINE_ARN )) {
463+ remoteResourceType = Optional .of (NORMALIZED_STEPFUNCTIONS_SERVICE_NAME + "::StateMachine" );
464+ remoteResourceIdentifier =
465+ getSfnResourceNameFromArn (
466+ Optional .ofNullable (
467+ escapeDelimiters (span .getAttributes ().get (AWS_STATE_MACHINE_ARN ))));
468+ cloudformationPrimaryIdentifier =
469+ Optional .ofNullable (escapeDelimiters (span .getAttributes ().get (AWS_STATE_MACHINE_ARN )));
470+ } else if (isKeyPresent (span , AWS_STEP_FUNCTIONS_ACTIVITY_ARN )) {
471+ remoteResourceType = Optional .of (NORMALIZED_STEPFUNCTIONS_SERVICE_NAME + "::Activity" );
472+ remoteResourceIdentifier =
473+ getSfnResourceNameFromArn (
474+ Optional .ofNullable (
475+ escapeDelimiters (span .getAttributes ().get (AWS_STEP_FUNCTIONS_ACTIVITY_ARN ))));
476+ cloudformationPrimaryIdentifier =
477+ Optional .ofNullable (
478+ escapeDelimiters (span .getAttributes ().get (AWS_STEP_FUNCTIONS_ACTIVITY_ARN )));
479+ } else if (isKeyPresent (span , AWS_SNS_TOPIC_ARN )) {
480+ remoteResourceType = Optional .of (NORMALIZED_SNS_SERVICE_NAME + "::Topic" );
481+ remoteResourceIdentifier =
482+ getSnsResourceNameFromArn (
483+ Optional .ofNullable (escapeDelimiters (span .getAttributes ().get (AWS_SNS_TOPIC_ARN ))));
484+ cloudformationPrimaryIdentifier =
485+ Optional .ofNullable (escapeDelimiters (span .getAttributes ().get (AWS_SNS_TOPIC_ARN )));
486+ } else if (isKeyPresent (span , AWS_SECRET_ARN )) {
487+ remoteResourceType = Optional .of (NORMALIZED_SECRETSMANAGER_SERVICE_NAME + "::Secret" );
488+ remoteResourceIdentifier =
489+ getSecretsManagerResourceNameFromArn (
490+ Optional .ofNullable (escapeDelimiters (span .getAttributes ().get (AWS_SECRET_ARN ))));
491+ cloudformationPrimaryIdentifier =
492+ Optional .ofNullable (escapeDelimiters (span .getAttributes ().get (AWS_SECRET_ARN )));
493+ } else if (isKeyPresent (span , AWS_LAMBDA_NAME )) {
494+ remoteResourceType = Optional .of (NORMALIZED_LAMBDA_SERVICE_NAME + "::Function" );
495+ remoteResourceIdentifier =
496+ getLambdaResourceNameFromAribitraryName (
497+ Optional .ofNullable (escapeDelimiters (span .getAttributes ().get (AWS_LAMBDA_NAME ))));
498+ } else if (isKeyPresent (span , AWS_LAMBDA_RESOURCE_ID )) {
499+ remoteResourceType = Optional .of (NORMALIZED_LAMBDA_SERVICE_NAME + "::EventSourceMapping" );
500+ remoteResourceIdentifier =
501+ Optional .ofNullable (escapeDelimiters (span .getAttributes ().get (AWS_LAMBDA_RESOURCE_ID )));
437502 }
438503 } else if (isDBSpan (span )) {
439504 remoteResourceType = Optional .of (DB_CONNECTION_RESOURCE_TYPE );
440505 remoteResourceIdentifier = getDbConnection (span );
441506 }
442507
508+ if (cloudformationPrimaryIdentifier .isEmpty ()) {
509+ cloudformationPrimaryIdentifier = remoteResourceIdentifier ;
510+ }
511+
443512 if (remoteResourceType .isPresent () && remoteResourceIdentifier .isPresent ()) {
444513 builder .put (AWS_REMOTE_RESOURCE_TYPE , remoteResourceType .get ());
445514 builder .put (AWS_REMOTE_RESOURCE_IDENTIFIER , remoteResourceIdentifier .get ());
515+ builder .put (AWS_CLOUDFORMATION_PRIMARY_IDENTIFIER , cloudformationPrimaryIdentifier .get ());
446516 }
447517 }
448518
519+ // NOTE: "name" in this case can be either the lambda name or lambda arn
520+ private static Optional <String > getLambdaResourceNameFromAribitraryName (
521+ Optional <String > arbitraryName ) {
522+ if (arbitraryName != null && arbitraryName .get ().startsWith ("arn:aws:lambda:" )) {
523+ Arn resourceArn = Arn .fromString (arbitraryName .get ());
524+ return Optional .of (resourceArn .getResource ().toString ().split (":" )[1 ]);
525+ }
526+ return arbitraryName ;
527+ }
528+
529+ private static Optional <String > getSecretsManagerResourceNameFromArn (Optional <String > stringArn ) {
530+ Arn resourceArn = Arn .fromString (stringArn .get ());
531+ return Optional .of (resourceArn .getResource ().toString ().split (":" )[1 ]);
532+ }
533+
534+ private static Optional <String > getSfnResourceNameFromArn (Optional <String > stringArn ) {
535+ Arn resourceArn = Arn .fromString (stringArn .get ());
536+ return Optional .of (resourceArn .getResource ().toString ().split (":" )[1 ]);
537+ }
538+
539+ private static Optional <String > getSnsResourceNameFromArn (Optional <String > stringArn ) {
540+ Arn resourceArn = Arn .fromString (stringArn .get ());
541+ return Optional .of (resourceArn .getResource ().toString ());
542+ }
543+
449544 /**
450545 * RemoteResourceIdentifier is populated with rule <code>
451546 * ^[{db.name}|]?{address}[|{port}]?
0 commit comments