@@ -107,6 +107,7 @@ def custom_event_context_extractor(lambda_event):
107
107
OTEL_INSTRUMENTATION_AWS_LAMBDA_FLUSH_TIMEOUT = (
108
108
"OTEL_INSTRUMENTATION_AWS_LAMBDA_FLUSH_TIMEOUT"
109
109
)
110
+ OTEL_LAMBDA_USE_AWS_CONTEXT_PROPAGATION = "OTEL_LAMBDA_USE_AWS_CONTEXT_PROPAGATION"
110
111
111
112
112
113
def _default_event_context_extractor (lambda_event : Any ) -> Context :
@@ -140,7 +141,9 @@ def _default_event_context_extractor(lambda_event: Any) -> Context:
140
141
141
142
142
143
def _determine_parent_context (
143
- lambda_event : Any , event_context_extractor : Callable [[Any ], Context ]
144
+ lambda_event : Any ,
145
+ use_aws_context_propagation : bool ,
146
+ event_context_extractor : Callable [[Any ], Context ],
144
147
) -> Context :
145
148
"""Determine the parent context for the current Lambda invocation.
146
149
@@ -158,15 +161,26 @@ def _determine_parent_context(
158
161
A Context with configuration found in the carrier.
159
162
"""
160
163
parent_context = None
161
-
162
- if event_context_extractor :
164
+ if use_aws_context_propagation :
165
+ parent_context = _get_x_ray_context ()
166
+ elif event_context_extractor :
163
167
parent_context = event_context_extractor (lambda_event )
164
168
else :
165
169
parent_context = _default_event_context_extractor (lambda_event )
166
170
167
171
return parent_context
168
172
169
173
174
+ def _get_x_ray_context () -> Optional [Context ]:
175
+ """Determine teh context propagated through the lambda runtime"""
176
+ xray_env_var = os .environ .get (_X_AMZN_TRACE_ID )
177
+ if xray_env_var :
178
+ env_context = AwsXRayPropagator ().extract ({TRACE_HEADER_KEY : xray_env_var })
179
+ return env_context
180
+
181
+ return None
182
+
183
+
170
184
def _determine_links () -> Optional [Sequence [Link ]]:
171
185
"""Determine if a Link should be added to the Span based on the
172
186
environment variable `_X_AMZN_TRACE_ID`.
@@ -180,31 +194,23 @@ def _determine_links() -> Optional[Sequence[Link]]:
180
194
"""
181
195
links = None
182
196
183
- xray_env_var = os . environ . get ( _X_AMZN_TRACE_ID )
197
+ x_ray_context = _get_x_ray_context ( )
184
198
185
- if xray_env_var :
186
- env_context = AwsXRayPropagator ().extract (
187
- {TRACE_HEADER_KEY : xray_env_var }
188
- )
189
-
190
- span_context = get_current_span (env_context ).get_span_context ()
199
+ if x_ray_context :
200
+ span_context = get_current_span (x_ray_context ).get_span_context ()
191
201
if span_context .is_valid :
192
202
links = [Link (span_context , {"source" : "x-ray-env" })]
193
203
194
204
return links
195
205
196
206
197
- def _set_api_gateway_v1_proxy_attributes (
198
- lambda_event : Any , span : Span
199
- ) -> Span :
207
+ def _set_api_gateway_v1_proxy_attributes (lambda_event : Any , span : Span ) -> Span :
200
208
"""Sets HTTP attributes for REST APIs and v1 HTTP APIs
201
209
202
210
More info:
203
211
https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format
204
212
"""
205
- span .set_attribute (
206
- SpanAttributes .HTTP_METHOD , lambda_event .get ("httpMethod" )
207
- )
213
+ span .set_attribute (SpanAttributes .HTTP_METHOD , lambda_event .get ("httpMethod" ))
208
214
209
215
if lambda_event .get ("headers" ):
210
216
if "User-Agent" in lambda_event ["headers" ]:
@@ -231,16 +237,12 @@ def _set_api_gateway_v1_proxy_attributes(
231
237
f"{ lambda_event ['resource' ]} ?{ urlencode (lambda_event ['queryStringParameters' ])} " ,
232
238
)
233
239
else :
234
- span .set_attribute (
235
- SpanAttributes .HTTP_TARGET , lambda_event ["resource" ]
236
- )
240
+ span .set_attribute (SpanAttributes .HTTP_TARGET , lambda_event ["resource" ])
237
241
238
242
return span
239
243
240
244
241
- def _set_api_gateway_v2_proxy_attributes (
242
- lambda_event : Any , span : Span
243
- ) -> Span :
245
+ def _set_api_gateway_v2_proxy_attributes (lambda_event : Any , span : Span ) -> Span :
244
246
"""Sets HTTP attributes for v2 HTTP APIs
245
247
246
248
More info:
@@ -289,21 +291,26 @@ def _instrument(
289
291
event_context_extractor : Callable [[Any ], Context ],
290
292
tracer_provider : TracerProvider = None ,
291
293
meter_provider : MeterProvider = None ,
294
+ use_aws_context_propagation : bool = False ,
292
295
):
293
296
def _instrumented_lambda_handler_call ( # noqa pylint: disable=too-many-branches
294
297
call_wrapped , instance , args , kwargs
295
298
):
296
- orig_handler_name = "." .join (
297
- [wrapped_module_name , wrapped_function_name ]
298
- )
299
+ orig_handler_name = "." .join ([wrapped_module_name , wrapped_function_name ])
299
300
300
301
lambda_event = args [0 ]
301
302
303
+ # We are not fully complying with the specification here to be backwards
304
+ # compatible with the old version of the specification.
305
+ # the ``use_aws_context_propagation`` flag allow us
306
+ # to opt-in into the previous behavior
302
307
parent_context = _determine_parent_context (
303
- lambda_event , event_context_extractor
308
+ lambda_event , use_aws_context_propagation , event_context_extractor
304
309
)
305
310
306
- links = _determine_links ()
311
+ links = None
312
+ if not use_aws_context_propagation :
313
+ links = _determine_links ()
307
314
308
315
span_kind = None
309
316
try :
@@ -354,9 +361,7 @@ def _instrumented_lambda_handler_call( # noqa pylint: disable=too-many-branches
354
361
# If the request came from an API Gateway, extract http attributes from the event
355
362
# https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/instrumentation/aws-lambda.md#api-gateway
356
363
# https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/http.md#http-server-semantic-conventions
357
- if isinstance (lambda_event , dict ) and lambda_event .get (
358
- "requestContext"
359
- ):
364
+ if isinstance (lambda_event , dict ) and lambda_event .get ("requestContext" ):
360
365
span .set_attribute (SpanAttributes .FAAS_TRIGGER , "http" )
361
366
362
367
if lambda_event .get ("version" ) == "2.0" :
@@ -424,6 +429,13 @@ def _instrument(self, **kwargs):
424
429
Event as input and extracts an OTel Context from it. By default,
425
430
the context is extracted from the HTTP headers of an API Gateway
426
431
request.
432
+ ``use_aws_context_propagation``: whether to use the AWS context propagation
433
+ to populate the parent context. When set to true, the spans
434
+ from the lambda runtime will not be added as span link to the
435
+ span of the lambda invocation.
436
+ Defaults to False.
437
+ """
438
+ _instrument (** kwargs )
427
439
"""
428
440
lambda_handler = os.environ.get(ORIG_HANDLER, os.environ.get(_HANDLER))
429
441
# pylint: disable=attribute-defined-outside-init
@@ -454,6 +466,11 @@ def _instrument(self, **kwargs):
454
466
),
455
467
tracer_provider=kwargs.get("tracer_provider"),
456
468
meter_provider=kwargs.get("meter_provider"),
469
+ use_aws_context_propagation=kwargs.get(
470
+ "use_aws_context_propagation",
471
+ os.environ.get(OTEL_LAMBDA_USE_AWS_CONTEXT_PROPAGATION, "False").lower()
472
+ in ("true", "1", "t"),
473
+ ),
457
474
)
458
475
459
476
def _uninstrument (self , ** kwargs ):
0 commit comments