@@ -59,6 +59,9 @@ pub const KEY_DATADOG_STATS_COMPUTED: &str = "_dd.stats_computed";
5959
6060// const SPAN_TYPE_GENERIC_DB: &str = "db";
6161
62+ // AWS Lambda OTEL instrumentation scope name
63+ const AWS_LAMBDA_INSTRUMENTATION_SCOPE : & str = "opentelemetry.instrumentation.aws_lambda" ;
64+
6265lazy_static ! {
6366 // TODO: add mappings
6467 static ref DB_SYSTEM_MAP : HashMap <String , String > = HashMap :: new( ) ;
@@ -247,7 +250,7 @@ fn get_otel_operation_name_v1(
247250}
248251
249252#[ allow( clippy:: too_many_lines) ]
250- fn get_otel_operation_name_v2 ( otel_span : & OtelSpan ) -> String {
253+ fn get_otel_operation_name_v2 ( otel_span : & OtelSpan , lib : & OtelInstrumentationScope ) -> String {
251254 let operation_name =
252255 get_otel_attribute_value_as_string ( & otel_span. attributes , "operation.name" , false ) ;
253256 if !operation_name. is_empty ( ) {
@@ -257,6 +260,12 @@ fn get_otel_operation_name_v2(otel_span: &OtelSpan) -> String {
257260 let is_client = otel_span. kind ( ) == SpanKind :: Client ;
258261 let is_server = otel_span. kind ( ) == SpanKind :: Server ;
259262
263+ // AWS Lambda: Check if this is the root Lambda invocation span
264+ // Only applies to Server spans from the AWS Lambda OTEL instrumentation
265+ if is_server && lib. name == AWS_LAMBDA_INSTRUMENTATION_SCOPE {
266+ return "aws.lambda" . to_string ( ) ;
267+ }
268+
260269 // HTTP
261270 let method =
262271 get_otel_attribute_value_as_string ( & otel_span. attributes , "http.request.method" , false ) ;
@@ -890,7 +899,7 @@ pub fn otel_span_to_dd_span(
890899 }
891900
892901 if otel_operation_and_resource_v2_enabled ( config. clone ( ) ) {
893- dd_span. name = get_otel_operation_name_v2 ( otel_span) ;
902+ dd_span. name = get_otel_operation_name_v2 ( otel_span, lib ) ;
894903 } else {
895904 dd_span. name = get_otel_operation_name_v1 (
896905 otel_span,
@@ -1321,6 +1330,52 @@ mod tests {
13211330 get_otel_operation_name_v1( & otel_span, & lib, false , & HashMap :: new( ) , true ) ,
13221331 "opentelemetry_instrumentation_aws_lambda.server"
13231332 ) ;
1324- assert_eq ! ( get_otel_operation_name_v2( & otel_span) , "server.request" ) ;
1333+ // With a non-matching lib name, should return server.request
1334+ assert_eq ! ( get_otel_operation_name_v2( & otel_span, & lib) , "server.request" ) ;
1335+ }
1336+
1337+ #[ test]
1338+ fn test_otel_operation_name_aws_lambda ( ) {
1339+ // Test that AWS Lambda OTEL instrumentation gets aws.lambda operation name
1340+ let otel_span = OtelSpan {
1341+ name : "handler.handler" . to_string ( ) ,
1342+ kind : SpanKind :: Server as i32 ,
1343+ ..Default :: default ( )
1344+ } ;
1345+ let aws_lambda_lib = OtelInstrumentationScope {
1346+ name : "opentelemetry.instrumentation.aws_lambda" . to_string ( ) ,
1347+ version : "0.42b0" . to_string ( ) ,
1348+ attributes : [ ] . to_vec ( ) ,
1349+ dropped_attributes_count : 0 ,
1350+ } ;
1351+
1352+ // AWS Lambda Server span should return aws.lambda
1353+ assert_eq ! (
1354+ get_otel_operation_name_v2( & otel_span, & aws_lambda_lib) ,
1355+ "aws.lambda"
1356+ ) ;
1357+
1358+ // Non-server span from AWS Lambda instrumentation should NOT return aws.lambda
1359+ let internal_span = OtelSpan {
1360+ name : "my-function" . to_string ( ) ,
1361+ kind : SpanKind :: Internal as i32 ,
1362+ ..Default :: default ( )
1363+ } ;
1364+ assert_eq ! (
1365+ get_otel_operation_name_v2( & internal_span, & aws_lambda_lib) ,
1366+ "SPAN_KIND_INTERNAL"
1367+ ) ;
1368+
1369+ // Server span from different instrumentation should return server.request
1370+ let other_lib = OtelInstrumentationScope {
1371+ name : "handler" . to_string ( ) ,
1372+ version : String :: new ( ) ,
1373+ attributes : [ ] . to_vec ( ) ,
1374+ dropped_attributes_count : 0 ,
1375+ } ;
1376+ assert_eq ! (
1377+ get_otel_operation_name_v2( & otel_span, & other_lib) ,
1378+ "server.request"
1379+ ) ;
13251380 }
13261381}
0 commit comments