diff --git a/datadog_lambda/asm.py b/datadog_lambda/asm.py index 11df2471..31f750d8 100644 --- a/datadog_lambda/asm.py +++ b/datadog_lambda/asm.py @@ -195,6 +195,17 @@ def asm_start_response( ), ) + if isinstance(response, dict) and "statusCode" in response: + body = response.get("body") + else: + body = response + + core.dispatch( + # The matching listener is registered in ddtrace.appsec._handlers + "aws_lambda.parse_body", + (body,), + ) + def get_asm_blocked_response( event_source: _EventSource, diff --git a/datadog_lambda/wrapper.py b/datadog_lambda/wrapper.py index 917a4fa0..49455625 100644 --- a/datadog_lambda/wrapper.py +++ b/datadog_lambda/wrapper.py @@ -318,6 +318,9 @@ def _after(self, event, context): if status_code: self.inferred_span.set_tag("http.status_code", status_code) + if self.trigger_tags and (route := self.trigger_tags.get("http.route")): + self.inferred_span.set_tag("http.route", route) + if config.service: self.inferred_span.set_tag("peer.service", config.service) diff --git a/tests/integration/snapshots/logs/async-metrics_python310.log b/tests/integration/snapshots/logs/async-metrics_python310.log index bda234df..6de63bec 100644 --- a/tests/integration/snapshots/logs/async-metrics_python310.log +++ b/tests/integration/snapshots/logs/async-metrics_python310.log @@ -66,6 +66,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "_inferred_span.synchronicity": "sync", "_inferred_span.tag_source": "self", "http.status_code": "200", + "http.route": "/", "peer.service": "integration-tests-python", "_dd.peer.service.source": "peer.service", "_dd.base_service": "integration-tests-python", @@ -613,6 +614,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "_inferred_span.synchronicity": "sync", "_inferred_span.tag_source": "self", "http.status_code": "200", + "http.route": "/httpapi/get", "peer.service": "integration-tests-python", "_dd.peer.service.source": "peer.service", "_dd.base_service": "integration-tests-python", diff --git a/tests/integration/snapshots/logs/async-metrics_python311.log b/tests/integration/snapshots/logs/async-metrics_python311.log index fd318de3..18bd119d 100644 --- a/tests/integration/snapshots/logs/async-metrics_python311.log +++ b/tests/integration/snapshots/logs/async-metrics_python311.log @@ -66,6 +66,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "_inferred_span.synchronicity": "sync", "_inferred_span.tag_source": "self", "http.status_code": "200", + "http.route": "/", "peer.service": "integration-tests-python", "_dd.peer.service.source": "peer.service", "_dd.base_service": "integration-tests-python", @@ -613,6 +614,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "_inferred_span.synchronicity": "sync", "_inferred_span.tag_source": "self", "http.status_code": "200", + "http.route": "/httpapi/get", "peer.service": "integration-tests-python", "_dd.peer.service.source": "peer.service", "_dd.base_service": "integration-tests-python", diff --git a/tests/integration/snapshots/logs/async-metrics_python312.log b/tests/integration/snapshots/logs/async-metrics_python312.log index b51b6a2d..e884a96d 100644 --- a/tests/integration/snapshots/logs/async-metrics_python312.log +++ b/tests/integration/snapshots/logs/async-metrics_python312.log @@ -66,6 +66,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "_inferred_span.synchronicity": "sync", "_inferred_span.tag_source": "self", "http.status_code": "200", + "http.route": "/", "peer.service": "integration-tests-python", "_dd.peer.service.source": "peer.service", "_dd.base_service": "integration-tests-python", @@ -613,6 +614,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "_inferred_span.synchronicity": "sync", "_inferred_span.tag_source": "self", "http.status_code": "200", + "http.route": "/httpapi/get", "peer.service": "integration-tests-python", "_dd.peer.service.source": "peer.service", "_dd.base_service": "integration-tests-python", diff --git a/tests/integration/snapshots/logs/async-metrics_python313.log b/tests/integration/snapshots/logs/async-metrics_python313.log index 89b73e92..3393f684 100644 --- a/tests/integration/snapshots/logs/async-metrics_python313.log +++ b/tests/integration/snapshots/logs/async-metrics_python313.log @@ -66,6 +66,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "_inferred_span.synchronicity": "sync", "_inferred_span.tag_source": "self", "http.status_code": "200", + "http.route": "/", "peer.service": "integration-tests-python", "_dd.peer.service.source": "peer.service", "_dd.base_service": "integration-tests-python", @@ -613,6 +614,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "_inferred_span.synchronicity": "sync", "_inferred_span.tag_source": "self", "http.status_code": "200", + "http.route": "/httpapi/get", "peer.service": "integration-tests-python", "_dd.peer.service.source": "peer.service", "_dd.base_service": "integration-tests-python", diff --git a/tests/integration/snapshots/logs/async-metrics_python38.log b/tests/integration/snapshots/logs/async-metrics_python38.log index ff5e5a60..0495ba45 100644 --- a/tests/integration/snapshots/logs/async-metrics_python38.log +++ b/tests/integration/snapshots/logs/async-metrics_python38.log @@ -66,6 +66,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "_inferred_span.synchronicity": "sync", "_inferred_span.tag_source": "self", "http.status_code": "200", + "http.route": "/", "peer.service": "integration-tests-python", "_dd.peer.service.source": "peer.service", "_dd.base_service": "integration-tests-python", @@ -613,6 +614,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "_inferred_span.synchronicity": "sync", "_inferred_span.tag_source": "self", "http.status_code": "200", + "http.route": "/httpapi/get", "peer.service": "integration-tests-python", "_dd.peer.service.source": "peer.service", "_dd.base_service": "integration-tests-python", diff --git a/tests/integration/snapshots/logs/async-metrics_python39.log b/tests/integration/snapshots/logs/async-metrics_python39.log index 5e3d46b6..d2aad04c 100644 --- a/tests/integration/snapshots/logs/async-metrics_python39.log +++ b/tests/integration/snapshots/logs/async-metrics_python39.log @@ -66,6 +66,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "_inferred_span.synchronicity": "sync", "_inferred_span.tag_source": "self", "http.status_code": "200", + "http.route": "/", "peer.service": "integration-tests-python", "_dd.peer.service.source": "peer.service", "_dd.base_service": "integration-tests-python", @@ -613,6 +614,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "_inferred_span.synchronicity": "sync", "_inferred_span.tag_source": "self", "http.status_code": "200", + "http.route": "/httpapi/get", "peer.service": "integration-tests-python", "_dd.peer.service.source": "peer.service", "_dd.base_service": "integration-tests-python", diff --git a/tests/integration/snapshots/logs/sync-metrics_python310.log b/tests/integration/snapshots/logs/sync-metrics_python310.log index 21569831..aa96b02d 100644 --- a/tests/integration/snapshots/logs/sync-metrics_python310.log +++ b/tests/integration/snapshots/logs/sync-metrics_python310.log @@ -46,6 +46,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "_inferred_span.synchronicity": "sync", "_inferred_span.tag_source": "self", "http.status_code": "200", + "http.route": "/", "peer.service": "integration-tests-python", "_dd.peer.service.source": "peer.service", "_dd.base_service": "integration-tests-python", @@ -653,6 +654,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "_inferred_span.synchronicity": "sync", "_inferred_span.tag_source": "self", "http.status_code": "200", + "http.route": "/httpapi/get", "peer.service": "integration-tests-python", "_dd.peer.service.source": "peer.service", "_dd.base_service": "integration-tests-python", diff --git a/tests/integration/snapshots/logs/sync-metrics_python311.log b/tests/integration/snapshots/logs/sync-metrics_python311.log index 5fcd504d..91ae2e51 100644 --- a/tests/integration/snapshots/logs/sync-metrics_python311.log +++ b/tests/integration/snapshots/logs/sync-metrics_python311.log @@ -46,6 +46,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "_inferred_span.synchronicity": "sync", "_inferred_span.tag_source": "self", "http.status_code": "200", + "http.route": "/", "peer.service": "integration-tests-python", "_dd.peer.service.source": "peer.service", "_dd.base_service": "integration-tests-python", @@ -653,6 +654,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "_inferred_span.synchronicity": "sync", "_inferred_span.tag_source": "self", "http.status_code": "200", + "http.route": "/httpapi/get", "peer.service": "integration-tests-python", "_dd.peer.service.source": "peer.service", "_dd.base_service": "integration-tests-python", diff --git a/tests/integration/snapshots/logs/sync-metrics_python312.log b/tests/integration/snapshots/logs/sync-metrics_python312.log index 9a05404c..81e897ca 100644 --- a/tests/integration/snapshots/logs/sync-metrics_python312.log +++ b/tests/integration/snapshots/logs/sync-metrics_python312.log @@ -46,6 +46,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "_inferred_span.synchronicity": "sync", "_inferred_span.tag_source": "self", "http.status_code": "200", + "http.route": "/", "peer.service": "integration-tests-python", "_dd.peer.service.source": "peer.service", "_dd.base_service": "integration-tests-python", @@ -653,6 +654,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "_inferred_span.synchronicity": "sync", "_inferred_span.tag_source": "self", "http.status_code": "200", + "http.route": "/httpapi/get", "peer.service": "integration-tests-python", "_dd.peer.service.source": "peer.service", "_dd.base_service": "integration-tests-python", diff --git a/tests/integration/snapshots/logs/sync-metrics_python313.log b/tests/integration/snapshots/logs/sync-metrics_python313.log index 5d17bed5..77376e6b 100644 --- a/tests/integration/snapshots/logs/sync-metrics_python313.log +++ b/tests/integration/snapshots/logs/sync-metrics_python313.log @@ -46,6 +46,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "_inferred_span.synchronicity": "sync", "_inferred_span.tag_source": "self", "http.status_code": "200", + "http.route": "/", "peer.service": "integration-tests-python", "_dd.peer.service.source": "peer.service", "_dd.base_service": "integration-tests-python", @@ -653,6 +654,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "_inferred_span.synchronicity": "sync", "_inferred_span.tag_source": "self", "http.status_code": "200", + "http.route": "/httpapi/get", "peer.service": "integration-tests-python", "_dd.peer.service.source": "peer.service", "_dd.base_service": "integration-tests-python", diff --git a/tests/integration/snapshots/logs/sync-metrics_python38.log b/tests/integration/snapshots/logs/sync-metrics_python38.log index 37ed391e..ddc8d751 100644 --- a/tests/integration/snapshots/logs/sync-metrics_python38.log +++ b/tests/integration/snapshots/logs/sync-metrics_python38.log @@ -46,6 +46,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "_inferred_span.synchronicity": "sync", "_inferred_span.tag_source": "self", "http.status_code": "200", + "http.route": "/", "peer.service": "integration-tests-python", "_dd.peer.service.source": "peer.service", "_dd.base_service": "integration-tests-python", @@ -653,6 +654,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "_inferred_span.synchronicity": "sync", "_inferred_span.tag_source": "self", "http.status_code": "200", + "http.route": "/httpapi/get", "peer.service": "integration-tests-python", "_dd.peer.service.source": "peer.service", "_dd.base_service": "integration-tests-python", diff --git a/tests/integration/snapshots/logs/sync-metrics_python39.log b/tests/integration/snapshots/logs/sync-metrics_python39.log index f147744b..128319f6 100644 --- a/tests/integration/snapshots/logs/sync-metrics_python39.log +++ b/tests/integration/snapshots/logs/sync-metrics_python39.log @@ -46,6 +46,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "_inferred_span.synchronicity": "sync", "_inferred_span.tag_source": "self", "http.status_code": "200", + "http.route": "/", "peer.service": "integration-tests-python", "_dd.peer.service.source": "peer.service", "_dd.base_service": "integration-tests-python", @@ -653,6 +654,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "_inferred_span.synchronicity": "sync", "_inferred_span.tag_source": "self", "http.status_code": "200", + "http.route": "/httpapi/get", "peer.service": "integration-tests-python", "_dd.peer.service.source": "peer.service", "_dd.base_service": "integration-tests-python", diff --git a/tests/test_asm.py b/tests/test_asm.py index e3a5e027..7a5e6c56 100644 --- a/tests/test_asm.py +++ b/tests/test_asm.py @@ -106,6 +106,7 @@ }, "200", {"Content-Type": "text/html"}, + None, True, ), ( @@ -123,6 +124,7 @@ "Content-Type": "text/plain", "X-Error": "Not Found", }, + None, True, ), ( @@ -140,6 +142,7 @@ "Location": "/user/123", "Content-Type": "application/json", }, + None, True, ), ( @@ -158,6 +161,7 @@ "Content-Type": "application/json", "X-Custom-Header": "test-value", }, + '{"message": "success"}', True, ), ( @@ -169,6 +173,7 @@ }, "200", {"Content-Type": "application/json"}, + None, True, ), ( @@ -180,6 +185,7 @@ }, "200", {"Content-Type": "text/plain"}, + None, True, ), ( @@ -188,6 +194,7 @@ {"statusCode": 200}, "200", {}, + None, False, # Should not dispatch for non-HTTP events ), ( @@ -196,6 +203,7 @@ "Hello, World!", "200", {"content-type": "application/json"}, + "Hello, World!", True, ), ( @@ -204,6 +212,7 @@ {"message": "Hello, World!"}, "200", {"content-type": "application/json"}, + {"message": "Hello, World!"}, True, ), ] @@ -326,7 +335,7 @@ def test_asm_start_request_parametrized( @pytest.mark.parametrize( - "name,event_file,response,status_code,expected_headers,should_dispatch", + "name,event_file,response,status_code,expected_headers,expected_body,should_dispatch", ASM_START_RESPONSE_TEST_CASES, ) @patch("datadog_lambda.asm.core") @@ -337,6 +346,7 @@ def test_asm_start_response_parametrized( response, status_code, expected_headers, + expected_body, should_dispatch, ): """Test ASM start response for various HTTP event types using parametrization""" @@ -355,18 +365,17 @@ def test_asm_start_response_parametrized( asm_start_response(mock_span, status_code, event_source, response) if should_dispatch: - # Verify core.dispatch was called - mock_core.dispatch.assert_called_once() - call_args = mock_core.dispatch.call_args - assert call_args[0][0] == "aws_lambda.start_response" - - # Extract the dispatched arguments - dispatch_args = call_args[0][1] - span, response_status_code, response_headers = dispatch_args - - assert span == mock_span - assert response_status_code == status_code - assert response_headers == expected_headers + assert mock_core.dispatch.call_count == 2 + + assert mock_core.dispatch.call_args_list[0].args == ( + "aws_lambda.start_response", + (mock_span, status_code, expected_headers), + ) + + assert mock_core.dispatch.call_args_list[1].args == ( + "aws_lambda.parse_body", + (expected_body,), + ) else: # Verify core.dispatch was not called for non-HTTP events mock_core.dispatch.assert_not_called()