Skip to content

Commit 1015d8d

Browse files
authored
Properly parent lazily loaded module imports. (#446)
* Properly parent lazily loaded module imports. * Test parenting for lazy loaded package. * Clear lists instead of updating the reference.
1 parent 6bccf3b commit 1015d8d

File tree

2 files changed

+54
-7
lines changed

2 files changed

+54
-7
lines changed

datadog_lambda/cold_start.py

+10-5
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
_cold_start = True
99
_proactive_initialization = False
1010
_lambda_container_initialized = False
11+
_tracer = None
1112

1213

1314
def set_cold_start(init_timestamp_ns):
@@ -18,6 +19,7 @@ def set_cold_start(init_timestamp_ns):
1819
global _cold_start
1920
global _lambda_container_initialized
2021
global _proactive_initialization
22+
global _tracer
2123
if not _lambda_container_initialized:
2224
now = time.time_ns()
2325
if (now - init_timestamp_ns) // 1_000_000_000 > 10:
@@ -29,6 +31,7 @@ def set_cold_start(init_timestamp_ns):
2931
_cold_start = False
3032
_proactive_initialization = False
3133
_lambda_container_initialized = True
34+
from ddtrace import tracer as _tracer
3235

3336

3437
def is_cold_start():
@@ -62,6 +65,9 @@ def __init__(self, module_name, full_file_path, start_time_ns, end_time_ns=None)
6265
self.start_time_ns = start_time_ns
6366
self.end_time_ns = end_time_ns
6467
self.children = []
68+
self.context = None
69+
if _lambda_container_initialized:
70+
self.context = _tracer.context_provider.active()
6571

6672

6773
root_nodes: List[ImportNode] = []
@@ -70,10 +76,8 @@ def __init__(self, module_name, full_file_path, start_time_ns, end_time_ns=None)
7076

7177

7278
def reset_node_stacks():
73-
global root_nodes
74-
root_nodes = []
75-
global import_stack
76-
import_stack = []
79+
root_nodes.clear()
80+
import_stack.clear()
7781

7882

7983
def push_node(module_name, file_path):
@@ -183,7 +187,8 @@ def trace(self, root_nodes: List[ImportNode] = root_nodes):
183187
cold_start_span = self.create_cold_start_span(cold_start_span_start_time_ns)
184188
while root_nodes:
185189
root_node = root_nodes.pop()
186-
self.trace_tree(root_node, cold_start_span)
190+
parent = root_node.context or cold_start_span
191+
self.trace_tree(root_node, parent)
187192
self.finish_span(cold_start_span, cold_start_span_end_time_ns)
188193

189194
def trace_tree(self, import_node: ImportNode, parent_span):

tests/test_cold_start.py

+44-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1+
import os
12
import time
23
import unittest
3-
import datadog_lambda.cold_start as cold_start
4+
45
from sys import modules, meta_path
5-
import os
66
from unittest.mock import MagicMock
77

8+
import datadog_lambda.cold_start as cold_start
9+
import datadog_lambda.wrapper as wrapper
10+
811

912
class TestColdStartTracingSetup(unittest.TestCase):
1013
def test_proactive_init(self):
@@ -234,3 +237,42 @@ def test_trace_ignore_libs(self):
234237
self.cold_start_tracer.trace(nodes)
235238
self.mock_activate.assert_called_once_with(self.mock_trace_ctx)
236239
self.assertEqual(self.output_spans, ["node_0", "unittest_cold_start"])
240+
241+
242+
def test_lazy_loaded_package_imports(monkeypatch):
243+
244+
spans = []
245+
246+
def finish(span):
247+
spans.append(span)
248+
249+
monkeypatch.setattr(wrapper.tracer, "_on_span_finish", finish)
250+
monkeypatch.setattr(wrapper, "is_new_sandbox", lambda: True)
251+
monkeypatch.setattr("datadog_lambda.wrapper.dd_tracing_enabled", True)
252+
monkeypatch.setenv(
253+
"DD_COLD_START_TRACE_SKIP_LIB", "ddtrace.contrib.logging,datadog_lambda.wrapper"
254+
)
255+
monkeypatch.setenv("DD_MIN_COLD_START_DURATION", "0")
256+
257+
@wrapper.datadog_lambda_wrapper
258+
def handler(event, context):
259+
import tabnanny
260+
261+
lambda_context = MagicMock()
262+
lambda_context.invoked_function_arn = (
263+
"arn:aws:lambda:us-west-1:123457598159:function:python-layer-test:1"
264+
)
265+
266+
handler.cold_start_tracing = True
267+
handler({}, lambda_context)
268+
269+
function_span = import_span = None
270+
for span in spans:
271+
if span.resource == "tabnanny":
272+
import_span = span
273+
elif span.name == "aws.lambda":
274+
function_span = span
275+
276+
assert function_span is not None
277+
assert import_span is not None
278+
assert import_span.parent_id == function_span.span_id

0 commit comments

Comments
 (0)