Skip to content

Commit 689ed6f

Browse files
Czechhtianchu
andauthored
[POC] Add DD_TRACE_EXTRACTOR env var to customize Trace extraction customization (#112)
* add extractor and tests * lint and other fixes * fix lint * Update tests * Fix format Co-authored-by: Tian Chu <[email protected]>
1 parent 0affcc2 commit 689ed6f

File tree

6 files changed

+101
-4
lines changed

6 files changed

+101
-4
lines changed

datadog_lambda/tracing.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,20 @@ def extract_context_from_sqs_event_or_context(event, lambda_context):
165165
return extract_context_from_lambda_context(lambda_context)
166166

167167

168-
def extract_dd_trace_context(event, lambda_context):
168+
def extract_context_custom_extractor(extractor, event, lambda_context):
169+
"""
170+
Extract Datadog trace context using a custom trace extractor function
171+
"""
172+
try:
173+
(trace_id, parent_id, sampling_priority,) = extractor(event, lambda_context)
174+
return trace_id, parent_id, sampling_priority
175+
except Exception as e:
176+
logger.debug("The trace extractor returned with error %s", e)
177+
178+
return None, None, None
179+
180+
181+
def extract_dd_trace_context(event, lambda_context, extractor=None):
169182
"""
170183
Extract Datadog trace context from the Lambda `event` object.
171184
@@ -175,7 +188,11 @@ def extract_dd_trace_context(event, lambda_context):
175188
global dd_trace_context
176189
trace_context_source = None
177190

178-
if "headers" in event:
191+
if extractor is not None:
192+
(trace_id, parent_id, sampling_priority,) = extract_context_custom_extractor(
193+
extractor, event, lambda_context
194+
)
195+
elif "headers" in event:
179196
(
180197
trace_id,
181198
parent_id,

datadog_lambda/wrapper.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import os
77
import logging
88
import traceback
9+
from importlib import import_module
910

1011
from datadog_lambda.extension import should_use_extension, flush_extension
1112
from datadog_lambda.cold_start import set_cold_start, is_cold_start
@@ -15,6 +16,7 @@
1516
submit_invocations_metric,
1617
submit_errors_metric,
1718
)
19+
from datadog_lambda.module_name import modify_module_name
1820
from datadog_lambda.patch import patch_all
1921
from datadog_lambda.tracing import (
2022
extract_dd_trace_context,
@@ -91,6 +93,16 @@ def __init__(self, func):
9193
os.environ.get("DD_MERGE_XRAY_TRACES", "false").lower() == "true"
9294
)
9395
self.function_name = os.environ.get("AWS_LAMBDA_FUNCTION_NAME", "function")
96+
self.extractor_env = os.environ.get("DD_TRACE_EXTRACTOR", None)
97+
self.trace_extractor = None
98+
99+
if self.extractor_env:
100+
extractor_parts = self.extractor_env.rsplit(".", 1)
101+
if len(extractor_parts) == 2:
102+
(mod_name, extractor_name) = extractor_parts
103+
modified_extractor_name = modify_module_name(mod_name)
104+
extractor_module = import_module(modified_extractor_name)
105+
self.trace_extractor = getattr(extractor_module, extractor_name)
94106

95107
# Inject trace correlation ids to logs
96108
if self.logs_injection:
@@ -125,7 +137,9 @@ def _before(self, event, context):
125137
set_cold_start()
126138
submit_invocations_metric(context)
127139
# Extract Datadog trace context and source from incoming requests
128-
dd_context, trace_context_source = extract_dd_trace_context(event, context)
140+
dd_context, trace_context_source = extract_dd_trace_context(
141+
event, context, extractor=self.trace_extractor
142+
)
129143
# Create a Datadog X-Ray subsegment with the trace context
130144
if dd_context and trace_context_source == TraceContextSource.EVENT:
131145
create_dd_dummy_metadata_subsegment(

tests/integration/serverless-plugin.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ provider:
66
DD_INTEGRATION_TEST: true
77
DD_API_KEY: ${env:DD_API_KEY}
88
WITH_PLUGIN: true
9+
lambdaHashingVersion: 20201221
910

1011
layers:
1112
python27:

tests/integration/serverless.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ provider:
88
environment:
99
DD_INTEGRATION_TEST: true
1010
DD_API_KEY: ${env:DD_API_KEY}
11+
lambdaHashingVersion: 20201221
1112

1213
layers:
1314
python27:

tests/test_tracing.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,70 @@ def test_with_complete_datadog_trace_headers(self):
136136
XraySubsegment.NAMESPACE,
137137
)
138138

139+
def test_with_extractor_function(self):
140+
def extractor_foo(event, context):
141+
foo = event.get("foo", {})
142+
lowercase_foo = {k.lower(): v for k, v in foo.items()}
143+
144+
trace_id = lowercase_foo.get(TraceHeader.TRACE_ID)
145+
parent_id = lowercase_foo.get(TraceHeader.PARENT_ID)
146+
sampling_priority = lowercase_foo.get(TraceHeader.SAMPLING_PRIORITY)
147+
return trace_id, parent_id, sampling_priority
148+
149+
lambda_ctx = get_mock_context()
150+
ctx, ctx_source = extract_dd_trace_context(
151+
{
152+
"foo": {
153+
TraceHeader.TRACE_ID: "123",
154+
TraceHeader.PARENT_ID: "321",
155+
TraceHeader.SAMPLING_PRIORITY: "1",
156+
}
157+
},
158+
lambda_ctx,
159+
extractor=extractor_foo,
160+
)
161+
self.assertEquals(ctx_source, "event")
162+
self.assertDictEqual(
163+
ctx, {"trace-id": "123", "parent-id": "321", "sampling-priority": "1",},
164+
)
165+
self.assertDictEqual(
166+
get_dd_trace_context(),
167+
{
168+
TraceHeader.TRACE_ID: "123",
169+
TraceHeader.PARENT_ID: "65535",
170+
TraceHeader.SAMPLING_PRIORITY: "1",
171+
},
172+
)
173+
174+
def test_graceful_fail_of_extractor_function(self):
175+
def extractor_raiser(event, context):
176+
raise Exception("kreator")
177+
178+
lambda_ctx = get_mock_context()
179+
ctx, ctx_source = extract_dd_trace_context(
180+
{
181+
"foo": {
182+
TraceHeader.TRACE_ID: "123",
183+
TraceHeader.PARENT_ID: "321",
184+
TraceHeader.SAMPLING_PRIORITY: "1",
185+
}
186+
},
187+
lambda_ctx,
188+
extractor=extractor_raiser,
189+
)
190+
self.assertEquals(ctx_source, "xray")
191+
self.assertDictEqual(
192+
ctx, {"trace-id": "4369", "parent-id": "65535", "sampling-priority": "2",},
193+
)
194+
self.assertDictEqual(
195+
get_dd_trace_context(),
196+
{
197+
TraceHeader.TRACE_ID: "4369",
198+
TraceHeader.PARENT_ID: "65535",
199+
TraceHeader.SAMPLING_PRIORITY: "2",
200+
},
201+
)
202+
139203
def test_with_sqs_distributed_datadog_trace_data(self):
140204
lambda_ctx = get_mock_context()
141205
sqs_event = {

tests/test_wrapper.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ def lambda_handler(event, context):
100100
)
101101
self.mock_wrapper_lambda_stats.flush.assert_called()
102102
self.mock_extract_dd_trace_context.assert_called_with(
103-
lambda_event, lambda_context
103+
lambda_event, lambda_context, extractor=None
104104
)
105105
self.mock_set_correlation_ids.assert_called()
106106
self.mock_inject_correlation_ids.assert_called()

0 commit comments

Comments
 (0)