Skip to content

Commit ef9f18c

Browse files
authored
Send errors metrics for 5xx response from API Gateway, Lambda Function URL, or ALB (#229)
1 parent e1d29e5 commit ef9f18c

File tree

4 files changed

+94
-3
lines changed

4 files changed

+94
-3
lines changed

datadog_lambda/tracing.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
from datetime import datetime, timezone
1111
from typing import Optional, Dict
1212

13+
from datadog_lambda.metric import submit_errors_metric
14+
1315
try:
1416
from typing import Literal
1517
except ImportError:
@@ -959,6 +961,13 @@ def create_function_execution_span(
959961
return span
960962

961963

964+
def mark_trace_as_error_for_5xx_responses(context, status_code, span):
965+
if len(status_code) == 3 and status_code.startswith("5"):
966+
submit_errors_metric(context)
967+
if span:
968+
span.error = 1
969+
970+
962971
class InferredSpanInfo(object):
963972
BASE_NAME = "_inferred_span"
964973
SYNCHRONICITY = f"{BASE_NAME}.synchronicity"

datadog_lambda/wrapper.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
from datadog_lambda.extension import should_use_extension, flush_extension
1212
from datadog_lambda.cold_start import set_cold_start, is_cold_start
1313
from datadog_lambda.constants import (
14-
XraySubsegment,
1514
TraceContextSource,
15+
XraySubsegment,
1616
)
1717
from datadog_lambda.metric import (
1818
flush_stats,
@@ -26,6 +26,7 @@
2626
create_dd_dummy_metadata_subsegment,
2727
inject_correlation_ids,
2828
dd_tracing_enabled,
29+
mark_trace_as_error_for_5xx_responses,
2930
set_correlation_ids,
3031
set_dd_trace_py_root,
3132
create_function_execution_span,
@@ -151,7 +152,7 @@ def __call__(self, event, context, **kwargs):
151152

152153
def _before(self, event, context):
153154
try:
154-
155+
self.response = None
155156
set_cold_start()
156157
submit_invocations_metric(context)
157158
self.trigger_tags = extract_trigger_tags(event, context)
@@ -190,6 +191,8 @@ def _after(self, event, context):
190191
status_code = extract_http_status_code_tag(self.trigger_tags, self.response)
191192
if status_code:
192193
self.trigger_tags["http.status_code"] = status_code
194+
mark_trace_as_error_for_5xx_responses(context, status_code, self.span)
195+
193196
# Create a new dummy Datadog subsegment for function trigger tags so we
194197
# can attach them to X-Ray spans when hybrid tracing is used
195198
if self.trigger_tags:

tests/test_tracing.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22
import json
33
import os
44

5-
from unittest.mock import MagicMock, patch, call
5+
from unittest.mock import MagicMock, Mock, patch, call
66

7+
import ddtrace
8+
from ddtrace.constants import ERROR_MSG, ERROR_TYPE
79
from ddtrace.helpers import get_correlation_ids
810
from ddtrace.context import Context
911

@@ -18,6 +20,7 @@
1820
create_dd_dummy_metadata_subsegment,
1921
create_function_execution_span,
2022
get_dd_trace_context,
23+
mark_trace_as_error_for_5xx_responses,
2124
set_correlation_ids,
2225
set_dd_trace_py_root,
2326
_convert_xray_trace_id,
@@ -1191,3 +1194,24 @@ def test_create_inferred_span_from_api_gateway_event_no_apiid(self):
11911194
self.assertEqual(span.span_type, "http")
11921195
self.assertEqual(span.get_tag(InferredSpanInfo.TAG_SOURCE), "self")
11931196
self.assertEqual(span.get_tag(InferredSpanInfo.SYNCHRONICITY), "sync")
1197+
1198+
@patch("datadog_lambda.tracing.submit_errors_metric")
1199+
def test_mark_trace_as_error_for_5xx_responses_getting_400_response_code(
1200+
self, mock_submit_errors_metric
1201+
):
1202+
mark_trace_as_error_for_5xx_responses(
1203+
context="fake_context", status_code="400", span="empty_span"
1204+
)
1205+
mock_submit_errors_metric.assert_not_called()
1206+
1207+
@patch("datadog_lambda.tracing.submit_errors_metric")
1208+
def test_mark_trace_as_error_for_5xx_responses_sends_error_metric_and_set_error_tags(
1209+
self, mock_submit_errors_metric
1210+
):
1211+
mock_span = Mock(ddtrace.span.Span)
1212+
status_code = "500"
1213+
mark_trace_as_error_for_5xx_responses(
1214+
context="fake_context", status_code=status_code, span=mock_span
1215+
)
1216+
mock_submit_errors_metric.assert_called_once()
1217+
self.assertEqual(1, mock_span.error)

tests/test_wrapper.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,61 @@ def lambda_handler(event, context):
275275
]
276276
)
277277

278+
@patch("datadog_lambda.wrapper.extract_trigger_tags")
279+
def test_5xx_sends_errors_metric_and_set_tags(self, mock_extract_trigger_tags):
280+
mock_extract_trigger_tags.return_value = {
281+
"function_trigger.event_source": "api-gateway",
282+
"function_trigger.event_source_arn": "arn:aws:apigateway:us-west-1::/restapis/1234567890/stages/prod",
283+
"http.url": "70ixmpl4fl.execute-api.us-east-2.amazonaws.com",
284+
"http.url_details.path": "/prod/path/to/resource",
285+
"http.method": "GET",
286+
}
287+
288+
@datadog_lambda_wrapper
289+
def lambda_handler(event, context):
290+
return {"statusCode": 500, "body": "fake response body"}
291+
292+
lambda_event = {}
293+
294+
lambda_handler(lambda_event, get_mock_context())
295+
296+
self.mock_write_metric_point_to_stdout.assert_has_calls(
297+
[
298+
call(
299+
"aws.lambda.enhanced.invocations",
300+
1,
301+
tags=[
302+
"region:us-west-1",
303+
"account_id:123457598159",
304+
"functionname:python-layer-test",
305+
"resource:python-layer-test:1",
306+
"cold_start:true",
307+
"memorysize:256",
308+
"runtime:python3.9",
309+
"datadog_lambda:v6.6.6",
310+
"dd_lambda_layer:datadog-python39_X.X.X",
311+
],
312+
timestamp=None,
313+
),
314+
call(
315+
"aws.lambda.enhanced.errors",
316+
1,
317+
tags=[
318+
"region:us-west-1",
319+
"account_id:123457598159",
320+
"functionname:python-layer-test",
321+
"resource:python-layer-test:1",
322+
"cold_start:true",
323+
"memorysize:256",
324+
"runtime:python3.9",
325+
"datadog_lambda:v6.6.6",
326+
"dd_lambda_layer:datadog-python39_X.X.X",
327+
],
328+
timestamp=None,
329+
),
330+
]
331+
)
332+
278333
def test_enhanced_metrics_cold_start_tag(self):
279334
@datadog_lambda_wrapper
280335
def lambda_handler(event, context):

0 commit comments

Comments
 (0)