Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions datadog_lambda/durable.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,30 @@ def extract_durable_function_tags(event):
"durable_function_execution_name": execution_name,
"durable_function_execution_id": execution_id,
}


VALID_DURABLE_STATUSES = {"SUCCEEDED", "FAILED", "PENDING"}


def extract_durable_function_status_tag(response, event):
"""
Extract durable function execution status from handler response.

Returns a dict with the status tag if:
- The event indicates a durable function invocation (has DurableExecutionArn)
- The response is a dict with a valid Status field

Returns empty dict otherwise.
"""
# Only add status tag for durable function invocations
if not isinstance(event, dict) or "DurableExecutionArn" not in event:
return {}

if not isinstance(response, dict):
return {}

status = response.get("Status")
if status not in VALID_DURABLE_STATUSES:
return {}

return {"durable_function_execution_status": status}
12 changes: 11 additions & 1 deletion datadog_lambda/wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,10 @@
tracer,
propagator,
)
from datadog_lambda.durable import extract_durable_function_tags
from datadog_lambda.durable import (
extract_durable_function_tags,
extract_durable_function_status_tag,
)
from datadog_lambda.trigger import (
extract_trigger_tags,
extract_http_status_code_tag,
Expand Down Expand Up @@ -340,6 +343,13 @@ def _after(self, event, context):
if status_code:
self.span.set_tag("http.status_code", status_code)

# Extract and set durable function execution status
durable_status_tags = extract_durable_function_status_tag(
self.response, event
)
for tag_name, tag_value in durable_status_tags.items():
self.span.set_tag(tag_name, tag_value)

self.span.finish()

if status_code:
Expand Down
63 changes: 63 additions & 0 deletions tests/test_durable.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from datadog_lambda.durable import (
_parse_durable_execution_arn,
extract_durable_function_tags,
extract_durable_function_status_tag,
)


Expand Down Expand Up @@ -89,3 +90,65 @@ def test_returns_empty_dict_when_durable_execution_arn_cannot_be_parsed(self):
def test_returns_empty_dict_when_event_is_empty(self):
result = extract_durable_function_tags({})
self.assertEqual(result, {})


class TestExtractDurableFunctionStatusTag(unittest.TestCase):
def test_succeeded_status(self):
event = {"DurableExecutionArn": "arn:aws:states:us-east-1:123456789012:..."}
response = {"Status": "SUCCEEDED", "Result": "some-result"}
result = extract_durable_function_status_tag(response, event)
self.assertEqual(result, {"durable_function_execution_status": "SUCCEEDED"})

def test_failed_status(self):
event = {"DurableExecutionArn": "arn:aws:states:us-east-1:123456789012:..."}
response = {"Status": "FAILED", "Error": "some-error"}
result = extract_durable_function_status_tag(response, event)
self.assertEqual(result, {"durable_function_execution_status": "FAILED"})

def test_pending_status(self):
event = {"DurableExecutionArn": "arn:aws:states:us-east-1:123456789012:..."}
response = {"Status": "PENDING"}
result = extract_durable_function_status_tag(response, event)
self.assertEqual(result, {"durable_function_execution_status": "PENDING"})

def test_non_durable_event_returns_empty(self):
event = {"key": "value"} # No DurableExecutionArn
response = {"Status": "SUCCEEDED"}
result = extract_durable_function_status_tag(response, event)
self.assertEqual(result, {})

def test_non_dict_response_returns_empty(self):
event = {"DurableExecutionArn": "arn:aws:states:us-east-1:123456789012:..."}
response = "string response"
result = extract_durable_function_status_tag(response, event)
self.assertEqual(result, {})

def test_missing_status_returns_empty(self):
event = {"DurableExecutionArn": "arn:aws:states:us-east-1:123456789012:..."}
response = {"Result": "some-result"} # No Status field
result = extract_durable_function_status_tag(response, event)
self.assertEqual(result, {})

def test_invalid_status_returns_empty(self):
event = {"DurableExecutionArn": "arn:aws:states:us-east-1:123456789012:..."}
response = {"Status": "INVALID"}
result = extract_durable_function_status_tag(response, event)
self.assertEqual(result, {})

def test_non_dict_event_returns_empty(self):
event = "not-a-dict"
response = {"Status": "SUCCEEDED"}
result = extract_durable_function_status_tag(response, event)
self.assertEqual(result, {})

def test_none_event_returns_empty(self):
event = None
response = {"Status": "SUCCEEDED"}
result = extract_durable_function_status_tag(response, event)
self.assertEqual(result, {})

def test_none_response_returns_empty(self):
event = {"DurableExecutionArn": "arn:aws:states:us-east-1:123456789012:..."}
response = None
result = extract_durable_function_status_tag(response, event)
self.assertEqual(result, {})
Loading