39
39
_EventSource ,
40
40
parse_event_source ,
41
41
get_first_record ,
42
+ is_step_function_event ,
42
43
EventTypes ,
43
44
EventSubtypes ,
44
45
)
@@ -271,6 +272,15 @@ def extract_context_from_sqs_or_sns_event_or_context(event, lambda_context):
271
272
272
273
if dd_json_data :
273
274
dd_data = json .loads (dd_json_data )
275
+
276
+ if is_step_function_event (dd_data ):
277
+ try :
278
+ return extract_context_from_step_functions (dd_data , None )
279
+ except Exception :
280
+ logger .debug (
281
+ "Failed to extract Step Functions context from SQS/SNS event."
282
+ )
283
+
274
284
return propagator .extract (dd_data )
275
285
else :
276
286
# Handle case where trace context is injected into attributes.AWSTraceHeader
@@ -313,19 +323,39 @@ def _extract_context_from_eventbridge_sqs_event(event):
313
323
body = json .loads (body_str )
314
324
detail = body .get ("detail" )
315
325
dd_context = detail .get ("_datadog" )
326
+
327
+ if is_step_function_event (dd_context ):
328
+ try :
329
+ return extract_context_from_step_functions (dd_context , None )
330
+ except Exception :
331
+ logger .debug (
332
+ "Failed to extract Step Functions context from EventBridge to SQS event."
333
+ )
334
+
316
335
return propagator .extract (dd_context )
317
336
318
337
319
338
def extract_context_from_eventbridge_event (event , lambda_context ):
320
339
"""
321
340
Extract datadog trace context from an EventBridge message's Details.
322
341
This is only possible if Details is a JSON string.
342
+
343
+ If we find a Step Function context, try to extract the trace context from
344
+ that header.
323
345
"""
324
346
try :
325
347
detail = event .get ("detail" )
326
348
dd_context = detail .get ("_datadog" )
327
349
if not dd_context :
328
350
return extract_context_from_lambda_context (lambda_context )
351
+
352
+ try :
353
+ return extract_context_from_step_functions (dd_context , None )
354
+ except Exception :
355
+ logger .debug (
356
+ "Failed to extract Step Functions context from EventBridge event."
357
+ )
358
+
329
359
return propagator .extract (dd_context )
330
360
except Exception as e :
331
361
logger .debug ("The trace extractor returned with error %s" , e )
@@ -424,7 +454,7 @@ def _generate_sfn_trace_id(execution_id: str, part: str):
424
454
def extract_context_from_step_functions (event , lambda_context ):
425
455
"""
426
456
Only extract datadog trace context when Step Functions Context Object is injected
427
- into lambda's event dict.
457
+ into lambda's event dict. Unwrap "Payload" if it exists to handle Legacy Lambda cases.
428
458
429
459
If '_datadog' header is present, we have two cases:
430
460
1. Root is a Lambda and we use its traceID
@@ -435,25 +465,25 @@ def extract_context_from_step_functions(event, lambda_context):
435
465
object.
436
466
"""
437
467
try :
468
+ event = event .get ("Payload" , event )
469
+ event = event .get ("_datadog" , event )
470
+
438
471
meta = {}
439
- dd_data = event .get ("_datadog" )
440
472
441
- if dd_data and dd_data .get ("serverless-version" ) == "v1" :
442
- if "x-datadog-trace-id" in dd_data : # lambda root
443
- trace_id = int (dd_data .get ("x-datadog-trace-id" ))
444
- high_64_bit_trace_id = _parse_high_64_bits (
445
- dd_data .get ("x-datadog-tags" )
446
- )
473
+ if event .get ("serverless-version" ) == "v1" :
474
+ if "x-datadog-trace-id" in event : # lambda root
475
+ trace_id = int (event .get ("x-datadog-trace-id" ))
476
+ high_64_bit_trace_id = _parse_high_64_bits (event .get ("x-datadog-tags" ))
447
477
if high_64_bit_trace_id :
448
478
meta ["_dd.p.tid" ] = high_64_bit_trace_id
449
479
else : # sfn root
450
- root_execution_id = dd_data .get ("RootExecutionId" )
480
+ root_execution_id = event .get ("RootExecutionId" )
451
481
trace_id = _generate_sfn_trace_id (root_execution_id , LOWER_64_BITS )
452
482
meta ["_dd.p.tid" ] = _generate_sfn_trace_id (
453
483
root_execution_id , HIGHER_64_BITS
454
484
)
455
485
456
- parent_id = _generate_sfn_parent_id (dd_data )
486
+ parent_id = _generate_sfn_parent_id (event )
457
487
else :
458
488
execution_id = event .get ("Execution" ).get ("Id" )
459
489
trace_id = _generate_sfn_trace_id (execution_id , LOWER_64_BITS )
@@ -472,20 +502,6 @@ def extract_context_from_step_functions(event, lambda_context):
472
502
return extract_context_from_lambda_context (lambda_context )
473
503
474
504
475
- def is_legacy_lambda_step_function (event ):
476
- """
477
- Check if the event is a step function that called a legacy lambda
478
- """
479
- if not isinstance (event , dict ) or "Payload" not in event :
480
- return False
481
-
482
- event = event .get ("Payload" )
483
- return isinstance (event , dict ) and (
484
- "_datadog" in event
485
- or ("Execution" in event and "StateMachine" in event and "State" in event )
486
- )
487
-
488
-
489
505
def extract_context_custom_extractor (extractor , event , lambda_context ):
490
506
"""
491
507
Extract Datadog trace context using a custom trace extractor function
@@ -1309,8 +1325,18 @@ def create_inferred_span_from_eventbridge_event(event, context):
1309
1325
synchronicity = "async" ,
1310
1326
tag_source = "self" ,
1311
1327
)
1312
- dt_format = "%Y-%m-%dT%H:%M:%SZ"
1328
+
1313
1329
timestamp = event .get ("time" )
1330
+ dt_format = "%Y-%m-%dT%H:%M:%SZ"
1331
+
1332
+ # Use more granular timestamp from upstream Step Function if possible
1333
+ try :
1334
+ if is_step_function_event (event .get ("detail" )):
1335
+ timestamp = event ["detail" ]["_datadog" ]["State" ]["EnteredTime" ]
1336
+ dt_format = "%Y-%m-%dT%H:%M:%S.%fZ"
1337
+ except (TypeError , KeyError , AttributeError ):
1338
+ logger .debug ("Error parsing timestamp from Step Functions event" )
1339
+
1314
1340
dt = datetime .strptime (timestamp , dt_format )
1315
1341
1316
1342
tracer .set_tags (_dd_origin )
@@ -1320,6 +1346,11 @@ def create_inferred_span_from_eventbridge_event(event, context):
1320
1346
if span :
1321
1347
span .set_tags (tags )
1322
1348
span .start = dt .replace (tzinfo = timezone .utc ).timestamp ()
1349
+
1350
+ # Since inferred span will later parent Lambda, preserve Lambda's current parent
1351
+ if dd_trace_context .span_id :
1352
+ span .parent_id = dd_trace_context .span_id
1353
+
1323
1354
return span
1324
1355
1325
1356
0 commit comments