5252import static software .amazon .opentelemetry .javaagent .providers .AwsAttributeKeys .AWS_GUARDRAIL_ARN ;
5353import static software .amazon .opentelemetry .javaagent .providers .AwsAttributeKeys .AWS_GUARDRAIL_ID ;
5454import static software .amazon .opentelemetry .javaagent .providers .AwsAttributeKeys .AWS_KNOWLEDGE_BASE_ID ;
55+ import static software .amazon .opentelemetry .javaagent .providers .AwsAttributeKeys .AWS_LAMBDA_ARN ;
56+ import static software .amazon .opentelemetry .javaagent .providers .AwsAttributeKeys .AWS_LAMBDA_NAME ;
5557import static software .amazon .opentelemetry .javaagent .providers .AwsAttributeKeys .AWS_LAMBDA_RESOURCE_ID ;
5658import static software .amazon .opentelemetry .javaagent .providers .AwsAttributeKeys .AWS_LOCAL_OPERATION ;
5759import static software .amazon .opentelemetry .javaagent .providers .AwsAttributeKeys .AWS_LOCAL_SERVICE ;
5860import static software .amazon .opentelemetry .javaagent .providers .AwsAttributeKeys .AWS_QUEUE_NAME ;
5961import static software .amazon .opentelemetry .javaagent .providers .AwsAttributeKeys .AWS_QUEUE_URL ;
6062import static software .amazon .opentelemetry .javaagent .providers .AwsAttributeKeys .AWS_REMOTE_DB_USER ;
63+ import static software .amazon .opentelemetry .javaagent .providers .AwsAttributeKeys .AWS_REMOTE_ENVIRONMENT ;
6164import static software .amazon .opentelemetry .javaagent .providers .AwsAttributeKeys .AWS_REMOTE_OPERATION ;
6265import static software .amazon .opentelemetry .javaagent .providers .AwsAttributeKeys .AWS_REMOTE_RESOURCE_IDENTIFIER ;
6366import static software .amazon .opentelemetry .javaagent .providers .AwsAttributeKeys .AWS_REMOTE_RESOURCE_TYPE ;
@@ -518,6 +521,40 @@ private static void setRemoteResourceTypeAndIdentifier(SpanData span, Attributes
518521 Optional .ofNullable (escapeDelimiters (span .getAttributes ().get (AWS_SECRET_ARN ))));
519522 cloudformationPrimaryIdentifier =
520523 Optional .ofNullable (escapeDelimiters (span .getAttributes ().get (AWS_SECRET_ARN )));
524+ } else if (isKeyPresent (span , AWS_LAMBDA_NAME )) {
525+ // Handling downstream Lambda as a service vs. an AWS resource:
526+ // - If the method call is "Invoke", we treat downstream Lambda as a service.
527+ // - Otherwise, we treat it as an AWS resource.
528+ //
529+ // This addresses a Lambda topology issue in Application Signals.
530+ // More context in PR:
531+ // https://github.com/aws-observability/aws-otel-python-instrumentation/pull/319
532+ //
533+ // NOTE: The environment variables LAMBDA_APPLICATION_SIGNALS_REMOTE_SERVICE and
534+ // LAMBDA_APPLICATION_SIGNALS_REMOTE_ENVIRONMENT were introduced as part of this fix.
535+ // They are optional and allow users to override the default values if needed.
536+ if ("Invoke" .equals (getRemoteOperation (span , RPC_METHOD ))) {
537+ String remoteService =
538+ Optional .ofNullable (System .getenv ("LAMBDA_APPLICATION_SIGNALS_REMOTE_SERVICE" ))
539+ .filter (s -> !s .isEmpty ())
540+ .orElse (span .getAttributes ().get (AWS_LAMBDA_NAME ));
541+ if (remoteService != null ) {
542+ builder .put (AWS_REMOTE_SERVICE , remoteService );
543+ }
544+
545+ String remoteEnvironment =
546+ Optional .ofNullable (System .getenv ("LAMBDA_APPLICATION_SIGNALS_REMOTE_ENVIRONMENT" ))
547+ .filter (s -> !s .isEmpty ())
548+ .orElse ("default" );
549+ builder .put (AWS_REMOTE_ENVIRONMENT , "lambda:" + remoteEnvironment );
550+ } else {
551+ remoteResourceType = Optional .of (NORMALIZED_LAMBDA_SERVICE_NAME + "::Function" );
552+ remoteResourceIdentifier =
553+ getLambdaFunctionNameFromArn (
554+ Optional .ofNullable (escapeDelimiters (span .getAttributes ().get (AWS_LAMBDA_NAME ))));
555+ cloudformationPrimaryIdentifier =
556+ Optional .ofNullable (escapeDelimiters (span .getAttributes ().get (AWS_LAMBDA_ARN )));
557+ }
521558 } else if (isKeyPresent (span , AWS_LAMBDA_RESOURCE_ID )) {
522559 remoteResourceType = Optional .of (NORMALIZED_LAMBDA_SERVICE_NAME + "::EventSourceMapping" );
523560 remoteResourceIdentifier =
@@ -539,6 +576,14 @@ private static void setRemoteResourceTypeAndIdentifier(SpanData span, Attributes
539576 }
540577 }
541578
579+ private static Optional <String > getLambdaFunctionNameFromArn (Optional <String > stringArn ) {
580+ if (stringArn .isPresent () && stringArn .get ().startsWith ("arn:aws:lambda:" )) {
581+ Arn resourceArn = Arn .fromString (stringArn .get ());
582+ return Optional .of (resourceArn .getResource ().toString ().split (":" )[1 ]);
583+ }
584+ return stringArn ;
585+ }
586+
542587 private static Optional <String > getSecretsManagerResourceNameFromArn (Optional <String > stringArn ) {
543588 Arn resourceArn = Arn .fromString (stringArn .get ());
544589 return Optional .of (resourceArn .getResource ().toString ().split (":" )[1 ]);
0 commit comments