Skip to content

Commit 3be0c9b

Browse files
committed
[Lambda] Create dummy segment when trace header is incomplete
This change makes it so that when the trace header is incomplete no new subsegments will be added. Customers can call the X-Ray SDK APIs without any output in this case.
1 parent a6a3e86 commit 3be0c9b

File tree

2 files changed

+24
-17
lines changed

2 files changed

+24
-17
lines changed

aws_xray_sdk/core/lambda_launcher.py

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import threading
44

55
from aws_xray_sdk import global_sdk_config
6+
from .models.dummy_entities import DummySegment
67
from .models.facade_segment import FacadeSegment
78
from .models.trace_header import TraceHeader
89
from .context import Context
@@ -44,7 +45,7 @@ class LambdaContext(Context):
4445
"""
4546
Lambda service will generate a segment for each function invocation which
4647
cannot be mutated. The context doesn't keep any manually created segment
47-
but instead every time ``get_trace_entity()`` gets called it refresh the facade
48+
but instead every time ``get_trace_entity()`` gets called it refresh the
4849
segment based on environment variables set by Lambda worker.
4950
"""
5051
def __init__(self):
@@ -65,12 +66,12 @@ def end_segment(self, end_time=None):
6566

6667
def put_subsegment(self, subsegment):
6768
"""
68-
Refresh the facade segment every time this function is invoked to prevent
69+
Refresh the segment every time this function is invoked to prevent
6970
a new subsegment from being attached to a leaked segment/subsegment.
7071
"""
7172
current_entity = self.get_trace_entity()
7273

73-
if not self._is_subsegment(current_entity) and current_entity.initializing:
74+
if not self._is_subsegment(current_entity) and (getattr(current_entity, 'initializing', None) or isinstance(current_entity, DummySegment)):
7475
if global_sdk_config.sdk_enabled():
7576
log.warning("Subsegment %s discarded due to Lambda worker still initializing" % subsegment.name)
7677
return
@@ -99,9 +100,9 @@ def get_trace_entity(self):
99100

100101
def _refresh_context(self):
101102
"""
102-
Get current facade segment. To prevent resource leaking in Lambda worker,
103+
Get current segment. To prevent resource leaking in Lambda worker,
103104
every time there is segment present, we compare its trace id to current
104-
environment variables. If it is different we create a new facade segment
105+
environment variables. If it is different we create a new segment
105106
and clean up subsegments stored.
106107
"""
107108
header_str = os.getenv(LAMBDA_TRACE_HEADER_KEY)
@@ -136,8 +137,8 @@ def handle_context_missing(self):
136137

137138
def _initialize_context(self, trace_header):
138139
"""
139-
Create a facade segment based on environment variables
140-
set by AWS Lambda and initialize storage for subsegments.
140+
Create a segment based on environment variables set by
141+
AWS Lambda and initialize storage for subsegments.
141142
"""
142143
sampled = None
143144
if not global_sdk_config.sdk_enabled():
@@ -148,12 +149,17 @@ def _initialize_context(self, trace_header):
148149
elif trace_header.sampled == 1:
149150
sampled = True
150151

151-
segment = FacadeSegment(
152-
name='facade',
153-
traceid=trace_header.root,
154-
entityid=trace_header.parent,
155-
sampled=sampled,
156-
)
152+
segment = None
153+
if not trace_header.root or not trace_header.parent or trace_header.sampled is None:
154+
segment = DummySegment()
155+
log.debug("Creating NoOp/Dummy parent segment")
156+
else:
157+
segment = FacadeSegment(
158+
name='facade',
159+
traceid=trace_header.root,
160+
entityid=trace_header.parent,
161+
sampled=sampled,
162+
)
157163
segment.save_origin_trace_header(trace_header)
158164
setattr(self._local, 'segment', segment)
159165
setattr(self._local, 'entities', [])

tests/test_lambda_context.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,18 +67,19 @@ def test_disable():
6767

6868

6969
def test_non_initialized():
70-
# Context that hasn't been initialized by lambda container should not add subsegments to the facade segment.
70+
# Context that hasn't been initialized by lambda container should not add subsegments to the dummy segment.
7171
temp_header_var = os.environ[lambda_launcher.LAMBDA_TRACE_HEADER_KEY]
7272
del os.environ[lambda_launcher.LAMBDA_TRACE_HEADER_KEY]
7373

7474
temp_context = lambda_launcher.LambdaContext()
75-
facade_segment = temp_context.get_trace_entity()
76-
subsegment = Subsegment("TestSubsegment", "local", facade_segment)
75+
dummy_segment = temp_context.get_trace_entity()
76+
subsegment = Subsegment("TestSubsegment", "local", dummy_segment)
7777
temp_context.put_subsegment(subsegment)
7878

79-
assert temp_context.get_trace_entity() == facade_segment
79+
assert temp_context.get_trace_entity() == dummy_segment
8080

8181
# "Lambda" container added metadata now. Should see subsegment now.
82+
# The following put_segment call will overwrite the dummy segment in the context with an intialized facade segment that accepts a subsegment.
8283
os.environ[lambda_launcher.LAMBDA_TRACE_HEADER_KEY] = temp_header_var
8384
temp_context.put_subsegment(subsegment)
8485

0 commit comments

Comments
 (0)