Skip to content

Commit 8fc0cd0

Browse files
committed
chore: update processing of eventbridge DSM
1 parent 218a15b commit 8fc0cd0

2 files changed

Lines changed: 76 additions & 27 deletions

File tree

datadog_lambda/tracing.py

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -99,15 +99,16 @@ def _dsm_set_eventbridge_checkpoint(context_json, detail_type):
9999
return
100100

101101
try:
102-
from ddtrace.internal.datastreams import data_streams_processor
103-
from ddtrace.internal.datastreams.processor import PROPAGATION_KEY_BASE_64
102+
from ddtrace.data_streams import PROPAGATION_KEY_BASE_64
103+
from ddtrace.data_streams import ddtrace as ddtrace_data_streams
104104

105-
processor = data_streams_processor()
105+
processor = getattr(ddtrace_data_streams.tracer, "data_streams_processor", None)
106106
if not processor:
107107
return
108108

109-
carrier_get = lambda k: context_json and context_json.get(k) # noqa: E731
110-
processor.decode_pathway_b64(carrier_get(PROPAGATION_KEY_BASE_64))
109+
processor.decode_pathway_b64(
110+
context_json.get(PROPAGATION_KEY_BASE_64) if context_json else None
111+
)
111112

112113
tags = ["direction:in", "topic:" + detail_type, "type:eventbridge"]
113114
if config.dsm_exchange_name:
@@ -289,9 +290,13 @@ def extract_context_from_sqs_or_sns_event_or_context(
289290

290291
# EventBridge => SQS
291292
try:
292-
context = _extract_context_from_eventbridge_sqs_event(event)
293-
if _is_context_complete(context):
294-
return context
293+
context, is_eventbridge_sqs = _extract_context_from_eventbridge_sqs_event(
294+
event
295+
)
296+
if is_eventbridge_sqs:
297+
if _is_context_complete(context):
298+
return context
299+
return extract_context_from_lambda_context(lambda_context)
295300
except Exception:
296301
logger.debug("Failed extracting context as EventBridge to SQS.")
297302

@@ -390,24 +395,35 @@ def _extract_context_from_eventbridge_sqs_event(event):
390395
This is only possible if first record in `Records` contains a
391396
`body` field which contains the EventBridge `detail` as a JSON string.
392397
"""
393-
records = event.get("Records")
398+
records = event.get("Records") or []
399+
if not records:
400+
return None, False
401+
394402
first_record = records[0]
395403
body_str = first_record.get("body")
396404
body = json.loads(body_str)
397405
detail = body.get("detail")
398-
# If `detail` is missing this is not an EventBridge -> SQS event; raising
399-
# here lets the caller fall back to the regular SQS extraction path before
400-
# any DSM checkpoint is set, avoiding double counting for plain SQS events.
406+
if not isinstance(detail, dict):
407+
return None, False
408+
401409
dd_context = detail.get("_datadog")
402410

403411
# The event has been confirmed as EventBridge -> SQS. Set a consume
404412
# checkpoint for every record in the batch. The message is consumed from
405413
# the SQS queue, so it follows SQS conventions (type:sqs, topic:queue ARN).
406414
if config.data_streams_enabled:
415+
_dsm_set_checkpoint(dd_context, "sqs", first_record.get("eventSourceARN", ""))
407416
for record in records:
417+
if record is first_record:
418+
continue
408419
try:
409420
record_body = json.loads(record.get("body"))
410-
record_context = (record_body.get("detail") or {}).get("_datadog")
421+
record_detail = record_body.get("detail")
422+
record_context = (
423+
record_detail.get("_datadog")
424+
if isinstance(record_detail, dict)
425+
else None
426+
)
411427
_dsm_set_checkpoint(
412428
record_context, "sqs", record.get("eventSourceARN", "")
413429
)
@@ -418,13 +434,13 @@ def _extract_context_from_eventbridge_sqs_event(event):
418434

419435
if is_step_function_event(dd_context):
420436
try:
421-
return extract_context_from_step_functions(dd_context, None)
437+
return extract_context_from_step_functions(dd_context, None), True
422438
except Exception:
423439
logger.debug(
424440
"Failed to extract Step Functions context from EventBridge to SQS event."
425441
)
426442

427-
return propagator.extract(dd_context)
443+
return propagator.extract(dd_context), True
428444

429445

430446
def extract_context_from_eventbridge_event(event, lambda_context):

tests/test_tracing.py

Lines changed: 45 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3651,19 +3651,23 @@ def test_kinesis_data_streams_disabled(self):
36513651
# EVENTBRIDGE -> SQS TESTS
36523652

36533653
@staticmethod
3654-
def _eventbridge_sqs_record(queue_arn, pathway_ctx):
3655-
body = {
3656-
"detail-type": "MyDetailType",
3657-
"source": "my.event.source",
3658-
"detail": {
3659-
"_datadog": {
3654+
def _eventbridge_sqs_record(queue_arn, pathway_ctx, include_trace_headers=True):
3655+
dd_context = {"dd-pathway-ctx-base64": pathway_ctx}
3656+
if include_trace_headers:
3657+
dd_context.update(
3658+
{
36603659
# Complete trace context so the extractor returns early and
36613660
# does not fall through to the regular SQS path.
36623661
"x-datadog-trace-id": "12345",
36633662
"x-datadog-parent-id": "67890",
36643663
"x-datadog-sampling-priority": "1",
3665-
"dd-pathway-ctx-base64": pathway_ctx,
36663664
}
3665+
)
3666+
body = {
3667+
"detail-type": "MyDetailType",
3668+
"source": "my.event.source",
3669+
"detail": {
3670+
"_datadog": dd_context
36673671
},
36683672
}
36693673
return {
@@ -3710,6 +3714,35 @@ def test_eventbridge_sqs_checkpoints_all_records(self):
37103714
self.assertEqual((second_args[0], second_args[1]), ("sqs", arn2))
37113715
self.assertEqual(second_args[2]("dd-pathway-ctx-base64"), "ctx-2")
37123716

3717+
@patch(
3718+
"datadog_lambda.tracing.extract_context_from_lambda_context",
3719+
return_value=Context(trace_id=111, span_id=222, sampling_priority=1),
3720+
)
3721+
def test_eventbridge_sqs_incomplete_context_uses_single_checkpoint(
3722+
self, mock_extract_context
3723+
):
3724+
queue_arn = "arn:aws:sqs:us-east-1:123456789012:eb-queue"
3725+
event = {
3726+
"Records": [
3727+
self._eventbridge_sqs_record(
3728+
queue_arn, "ctx-only", include_trace_headers=False
3729+
)
3730+
]
3731+
}
3732+
3733+
context = extract_context_from_sqs_or_sns_event_or_context(
3734+
event, self.lambda_context, parse_event_source(event)
3735+
)
3736+
3737+
self.assertEqual(context.trace_id, 111)
3738+
self.assertEqual(context.span_id, 222)
3739+
mock_extract_context.assert_called_once_with(self.lambda_context)
3740+
self.assertEqual(self.mock_checkpoint.call_count, 1)
3741+
args, _ = self.mock_checkpoint.call_args
3742+
self.assertEqual(args[0], "sqs")
3743+
self.assertEqual(args[1], queue_arn)
3744+
self.assertEqual(args[2]("dd-pathway-ctx-base64"), "ctx-only")
3745+
37133746
@patch("datadog_lambda.config.Config.data_streams_enabled", False)
37143747
def test_eventbridge_sqs_data_streams_disabled(self):
37153748
queue_arn = "arn:aws:sqs:us-east-1:123456789012:eb-queue"
@@ -3726,12 +3759,12 @@ class TestEventBridgeDSMLogic(unittest.TestCase):
37263759
def setUp(self):
37273760
self.lambda_context = get_mock_context()
37283761
self.mock_processor = Mock()
3729-
processor_patcher = patch(
3730-
"ddtrace.internal.datastreams.data_streams_processor",
3731-
return_value=self.mock_processor,
3762+
tracer_patcher = patch(
3763+
"ddtrace.data_streams.ddtrace.tracer",
3764+
new=SimpleNamespace(data_streams_processor=self.mock_processor),
37323765
)
3733-
processor_patcher.start()
3734-
self.addCleanup(processor_patcher.stop)
3766+
tracer_patcher.start()
3767+
self.addCleanup(tracer_patcher.stop)
37353768
config_patcher = patch(
37363769
"datadog_lambda.config.Config.data_streams_enabled", True
37373770
)

0 commit comments

Comments
 (0)