Skip to content

Commit 9b25540

Browse files
committed
add httpapi case
1 parent f0ad3eb commit 9b25540

File tree

7 files changed

+270
-93
lines changed

7 files changed

+270
-93
lines changed

datadog_lambda/tracing.py

Lines changed: 64 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
from ddtrace.propagation.http import HTTPPropagator
3636
from datadog_lambda import __version__ as datadog_lambda_version
3737
from datadog_lambda.trigger import (
38+
_EventSource,
3839
parse_event_source,
3940
get_first_record,
4041
EventTypes,
@@ -169,16 +170,21 @@ def extract_context_from_lambda_context(lambda_context):
169170
return trace_id, parent_id, sampling_priority
170171

171172

172-
def extract_context_from_http_event_or_context(event, lambda_context):
173+
def extract_context_from_http_event_or_context(event, lambda_context, event_source: _EventSource):
173174
"""
174175
Extract Datadog trace context from the `headers` key in from the Lambda
175176
`event` object.
176177
177178
Falls back to lambda context if no trace data is found in the `headers`
178179
"""
179-
if is_non_cached_authorizer_invocation(event):
180+
injected_authorizer_data = get_injected_authorizer_data(event, event_source)
181+
if injected_authorizer_data:
180182
try:
181-
return extract_context_from_authorizer_event(event)
183+
# fail fast on any KeyError here
184+
trace_id = injected_authorizer_data[TraceHeader.TRACE_ID]
185+
parent_id = injected_authorizer_data[TraceHeader.PARENT_ID]
186+
sampling_priority = injected_authorizer_data[TraceHeader.SAMPLING_PRIORITY]
187+
return trace_id, parent_id, sampling_priority
182188
except Exception as e:
183189
logger.debug(
184190
"extract_context_from_authorizer_event returned with error. \
@@ -328,28 +334,31 @@ def extract_context_custom_extractor(extractor, event, lambda_context):
328334
return None, None, None
329335

330336

331-
def extract_context_from_authorizer_event(event):
332-
# fail fast if any KeyError until '_datadog'
333-
dd_data = json.loads(event["requestContext"]["authorizer"]["_datadog"])
334-
trace_id = dd_data.get(TraceHeader.TRACE_ID)
335-
parent_id = dd_data.get(TraceHeader.PARENT_ID)
336-
sampling_priority = dd_data.get(TraceHeader.SAMPLING_PRIORITY)
337-
return trace_id, parent_id, sampling_priority
338-
339-
340-
def is_non_cached_authorizer_invocation(event) -> bool:
337+
def get_injected_authorizer_data(event, event_source: _EventSource) -> dict:
341338
try:
339+
dd_data_raw = None
342340
# has '_datadog' header and not cached because integrationLatency > 0
343-
injected_authorizer_headers = event.get("requestContext", {}).get(
344-
"authorizer", {}
345-
)
346-
return (
347-
injected_authorizer_headers.get("_datadog")
348-
and int(injected_authorizer_headers.get("integrationLatency", 0)) > 0
349-
) # non-cached
341+
authorizer_headers = event.get("requestContext", {}).get("authorizer", {})
342+
343+
# [API_GATEWAY V1]if integrationLatency == 0, it's cached and no trace for the authorizer func
344+
if event_source.equals(
345+
EventTypes.API_GATEWAY, subtype=EventSubtypes.API_GATEWAY
346+
) and int(authorizer_headers.get("integrationLatency", 0)) == 0:
347+
return None
348+
349+
if event_source.equals(EventTypes.API_GATEWAY, subtype=EventSubtypes.HTTP_API):
350+
dd_data_raw = authorizer_headers.get("lambda", {}).get("_datadog")
351+
else:
352+
dd_data_raw = authorizer_headers.get("_datadog")
353+
354+
if dd_data_raw:
355+
dd_data = json.loads(dd_data_raw)
356+
return dd_data
357+
return None
358+
350359
except Exception as e:
351360
logger.debug("Failed to check if invocated by an authorizer. error %s", e)
352-
return False
361+
return None
353362

354363

355364
def extract_dd_trace_context(event, lambda_context, extractor=None):
@@ -374,7 +383,7 @@ def extract_dd_trace_context(event, lambda_context, extractor=None):
374383
trace_id,
375384
parent_id,
376385
sampling_priority,
377-
) = extract_context_from_http_event_or_context(event, lambda_context)
386+
) = extract_context_from_http_event_or_context(event, lambda_context, event_source)
378387
elif event_source.equals(EventTypes.SNS) or event_source.equals(EventTypes.SQS):
379388
(
380389
trace_id,
@@ -414,7 +423,7 @@ def extract_dd_trace_context(event, lambda_context, extractor=None):
414423
if dd_trace_context:
415424
trace_context_source = TraceContextSource.XRAY
416425
logger.debug("extracted dd trace context %s", dd_trace_context)
417-
return dd_trace_context, trace_context_source
426+
return dd_trace_context, trace_context_source, event_source
418427

419428

420429
def get_dd_trace_context():
@@ -537,8 +546,9 @@ def set_dd_trace_py_root(trace_context_source, merge_xray_traces):
537546
)
538547

539548

540-
def create_inferred_span(event, context):
541-
event_source = parse_event_source(event)
549+
def create_inferred_span(event, context, event_source: _EventSource = None):
550+
if event_source is None:
551+
event_source = parse_event_source(event)
542552
try:
543553
if event_source.equals(
544554
EventTypes.API_GATEWAY, subtype=EventSubtypes.API_GATEWAY
@@ -649,20 +659,6 @@ def insert_upstream_authorizer_span(
649659
return upstream_authorizer_span
650660

651661

652-
def get_start_and_finish_time_for_authorizer_span(request_context):
653-
parsed_upstream_context = json.loads(
654-
request_context.get("authorizer", {}).get("_datadog")
655-
)
656-
start_time_s = (
657-
int(parsed_upstream_context.get(OtherConsts.parentSpanFinishTimeHeader)) / 1000
658-
)
659-
finish_time_s = (
660-
int(request_context.get("requestTimeEpoch"))
661-
+ int(request_context.get("authorizer", {}).get("integrationLatency", 0))
662-
) / 1000
663-
return start_time_s, finish_time_s
664-
665-
666662
def create_inferred_span_from_api_gateway_websocket_event(event, context):
667663
trace_ctx = tracer.current_trace_context()
668664
request_context = event.get("requestContext")
@@ -681,7 +677,7 @@ def create_inferred_span_from_api_gateway_websocket_event(event, context):
681677
"event_type": request_context.get("eventType"),
682678
"message_direction": request_context.get("messageDirection"),
683679
}
684-
request_time_epoch = request_context.get("requestTimeEpoch")
680+
request_time_epoch_s = int(request_context.get("requestTimeEpoch")) / 1000
685681
if is_api_gateway_invocation_async(event):
686682
InferredSpanInfo.set_tags(tags, tag_source="self", synchronicity="async")
687683
else:
@@ -693,14 +689,18 @@ def create_inferred_span_from_api_gateway_websocket_event(event, context):
693689
}
694690
tracer.set_tags({"_dd.origin": "lambda"})
695691
upstream_authorizer_span = None
696-
if is_non_cached_authorizer_invocation(event):
692+
injected_authorizer_data = get_injected_authorizer_data(event, _EventSource(EventTypes.API_GATEWAY, EventSubtypes.WEBSOCKET))
693+
if injected_authorizer_data:
697694
try:
698-
start_time_s, finish_time_s = get_start_and_finish_time_for_authorizer_span(
699-
request_context
700-
)
695+
start_time_s = int(injected_authorizer_data.get(OtherConsts.parentSpanFinishTimeHeader)) / 1000
696+
finish_time_s = request_time_epoch_s + \
697+
(int(request_context.get("authorizer", {}).get("integrationLatency", 0))
698+
) / 1000
701699
upstream_authorizer_span = insert_upstream_authorizer_span(
702700
args, tags, start_time_s, finish_time_s
703701
)
702+
# trace context needs to be set again as it is reset by upstream_authorizer_span.finish
703+
tracer.context_provider.activate(trace_ctx)
704704
except Exception as e:
705705
traceback.print_exc()
706706
logger.debug(
@@ -714,7 +714,7 @@ def create_inferred_span_from_api_gateway_websocket_event(event, context):
714714
span = tracer.trace("aws.apigateway.websocket", **args)
715715
if span:
716716
span.set_tags(tags)
717-
span.start = request_time_epoch / 1000
717+
span.start = finish_time_s if finish_time_s is not None else request_time_epoch_s
718718
if upstream_authorizer_span:
719719
span.parent_id = upstream_authorizer_span.span_id
720720
return span
@@ -738,7 +738,7 @@ def create_inferred_span_from_api_gateway_event(event, context):
738738
"stage": request_context.get("stage"),
739739
"request_id": request_context.get("requestId"),
740740
}
741-
request_time_epoch = int(request_context.get("requestTimeEpoch")) / 1000
741+
request_time_epoch_s = int(request_context.get("requestTimeEpoch")) / 1000
742742
if is_api_gateway_invocation_async(event):
743743
InferredSpanInfo.set_tags(tags, tag_source="self", synchronicity="async")
744744
else:
@@ -751,14 +751,17 @@ def create_inferred_span_from_api_gateway_event(event, context):
751751
tracer.set_tags({"_dd.origin": "lambda"})
752752
upstream_authorizer_span = None
753753
finish_time_s = None
754-
if is_non_cached_authorizer_invocation(event):
754+
injected_authorizer_data = get_injected_authorizer_data(event, _EventSource(EventTypes.API_GATEWAY, EventSubtypes.API_GATEWAY))
755+
if injected_authorizer_data:
755756
try:
756-
start_time_s, finish_time_s = get_start_and_finish_time_for_authorizer_span(
757-
request_context
758-
)
757+
start_time_s = int(injected_authorizer_data.get(OtherConsts.parentSpanFinishTimeHeader)) / 1000
758+
finish_time_s = request_time_epoch_s + \
759+
(int(request_context.get("authorizer", {}).get("integrationLatency", 0))) / 1000
759760
upstream_authorizer_span = insert_upstream_authorizer_span(
760761
args, tags, start_time_s, finish_time_s
761762
)
763+
# trace context needs to be set again as it is reset by upstream_authorizer_span.finish
764+
tracer.context_provider.activate(trace_ctx)
762765
except Exception as e:
763766
traceback.print_exc()
764767
logger.debug(
@@ -767,13 +770,12 @@ def create_inferred_span_from_api_gateway_event(event, context):
767770
event,
768771
e,
769772
)
770-
# trace context needs to be set again as it is reset by upstream_authorizer_span.finish
771-
tracer.context_provider.activate(trace_ctx)
773+
772774
span = tracer.trace("aws.apigateway", **args)
773775
if span:
774776
span.set_tags(tags)
775777
# start time pushed by the inserted authorizer span
776-
span.start = finish_time_s if finish_time_s is not None else request_time_epoch
778+
span.start = finish_time_s if finish_time_s is not None else request_time_epoch_s
777779
if upstream_authorizer_span:
778780
span.parent_id = upstream_authorizer_span.span_id
779781
return span
@@ -800,7 +802,7 @@ def create_inferred_span_from_http_api_event(event, context):
800802
"apiname": request_context.get("apiId"),
801803
"stage": request_context.get("stage"),
802804
}
803-
request_time_epoch = request_context.get("timeEpoch")
805+
request_time_epoch_s = int(request_context.get("timeEpoch")) / 1000
804806
if is_api_gateway_invocation_async(event):
805807
InferredSpanInfo.set_tags(tags, tag_source="self", synchronicity="async")
806808
else:
@@ -812,14 +814,17 @@ def create_inferred_span_from_http_api_event(event, context):
812814
}
813815
tracer.set_tags({"_dd.origin": "lambda"})
814816
upstream_authorizer_span = None
815-
if is_non_cached_authorizer_invocation(event):
817+
finish_time_s = None
818+
injected_authorizer_data = get_injected_authorizer_data(event, _EventSource(EventTypes.API_GATEWAY, EventSubtypes.HTTP_API))
819+
if injected_authorizer_data:
816820
try:
817-
start_time_s, finish_time_s = get_start_and_finish_time_for_authorizer_span(
818-
request_context
819-
)
821+
start_time_s = int(injected_authorizer_data.get(OtherConsts.parentSpanFinishTimeHeader)) / 1000
822+
finish_time_s = start_time_s # we don't have the integrationLatency info for the authorizer
820823
upstream_authorizer_span = insert_upstream_authorizer_span(
821824
args, tags, start_time_s, finish_time_s
822825
)
826+
# trace context needs to be set again as it is reset by upstream_authorizer_span.finish
827+
tracer.context_provider.activate(trace_ctx)
823828
except Exception as e:
824829
traceback.print_exc()
825830
logger.debug(
@@ -828,12 +833,10 @@ def create_inferred_span_from_http_api_event(event, context):
828833
event,
829834
e,
830835
)
831-
# trace context needs to be set again as it is reset by upstream_authorizer_span.finish
832-
tracer.context_provider.activate(trace_ctx)
833836
span = tracer.trace("aws.httpapi", **args)
834837
if span:
835838
span.set_tags(tags)
836-
span.start = request_time_epoch / 1e3
839+
span.start = finish_time_s if finish_time_s is not None else request_time_epoch_s
837840
if upstream_authorizer_span:
838841
span.parent_id = upstream_authorizer_span.span_id
839842
return span

datadog_lambda/wrapper.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ def _before(self, event, context):
200200
submit_invocations_metric(context)
201201
self.trigger_tags = extract_trigger_tags(event, context)
202202
# Extract Datadog trace context and source from incoming requests
203-
dd_context, trace_context_source = extract_dd_trace_context(
203+
dd_context, trace_context_source, event_source = extract_dd_trace_context(
204204
event, context, extractor=self.trace_extractor
205205
)
206206
# Create a Datadog X-Ray subsegment with the trace context
@@ -212,7 +212,7 @@ def _before(self, event, context):
212212
if dd_tracing_enabled:
213213
set_dd_trace_py_root(trace_context_source, self.merge_xray_traces)
214214
if self.make_inferred_span:
215-
self.inferred_span = create_inferred_span(event, context)
215+
self.inferred_span = create_inferred_span(event, context, event_source)
216216
self.span = create_function_execution_span(
217217
context,
218218
self.function_name,

tests/event_samples/authorizer-request-api-gateway-v1.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@
7070
"stage": "dev",
7171
"domainPrefix": "amddr1rix9",
7272
"requestTimeEpoch": 1663295019935,
73-
"requestId": "e4816b6c-8954-4510-8771-837988d7f485",
73+
"requestId": "1234567",
7474
"identity": {
7575
"cognitoIdentityPoolId": null,
7676
"accountId": null,
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
{
2+
"version": "2.0",
3+
"routeKey": "GET /hello",
4+
"rawPath": "/hello",
5+
"rawQueryString": "",
6+
"headers": {
7+
"accept": "*/*",
8+
"accept-encoding": "gzip, deflate, br",
9+
"authorization": "secretT0k3n",
10+
"authorizationtoken": "secretT0k3n",
11+
"cache-control": "no-cache",
12+
"content-length": "0",
13+
"host": "amddr1rix9.execute-api.sa-east-1.amazonaws.com",
14+
"postman-token": "a9231288-1486-4753-a589-7ad3ac853c78",
15+
"user-agent": "curl/7.64.1",
16+
"x-amzn-trace-id": "Root=1-632a6082-6d32606a1e652ce274e8f055",
17+
"x-forwarded-for": "38.122.226.210",
18+
"x-forwarded-port": "443",
19+
"x-forwarded-proto": "https"
20+
},
21+
"requestContext": {
22+
"accountId": "601427279990",
23+
"apiId": "amddr1rix9",
24+
"authorizer": {
25+
"lambda": {
26+
"_datadog": "{\"x-datadog-trace-id\": \"12056652649662348062\", \"x-datadog-parent-id\": \"12647339963998804799\", \"x-datadog-sampling-priority\": \"1\", \"x-datadog-tags\": \"_dd.p.dm=-0\", \"x-datadog-parent-span-finish-time\": 1663721602440.8286}",
27+
"scope": "this is just a string"
28+
}
29+
},
30+
"domainName": "amddr1rix9.execute-api.sa-east-1.amazonaws.com",
31+
"domainPrefix": "amddr1rix9",
32+
"http": {
33+
"method": "GET",
34+
"path": "/hello",
35+
"protocol": "HTTP/1.1",
36+
"sourceIp": "38.122.226.210",
37+
"userAgent": "curl/7.64.1"
38+
},
39+
"requestId": "1234567",
40+
"routeKey": "GET /hello",
41+
"stage": "$default",
42+
"time": "21/Sep/2022:00:53:22 +0000",
43+
"timeEpoch": 1663295021832
44+
},
45+
"isBase64Encoded": false
46+
}

0 commit comments

Comments
 (0)