datadog_lambda 5.92.0__py3-none-any.whl → 5.94.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- datadog_lambda/__init__.py +2 -6
- datadog_lambda/cold_start.py +6 -2
- datadog_lambda/extension.py +2 -4
- datadog_lambda/handler.py +18 -3
- datadog_lambda/metric.py +14 -18
- datadog_lambda/patch.py +3 -3
- datadog_lambda/tag_object.py +4 -4
- datadog_lambda/tags.py +21 -48
- datadog_lambda/tracing.py +201 -182
- datadog_lambda/trigger.py +39 -35
- datadog_lambda/version.py +1 -0
- datadog_lambda/wrapper.py +6 -5
- datadog_lambda/xray.py +45 -34
- datadog_lambda-5.94.0.dist-info/LICENSE-3rdparty.csv +13 -0
- {datadog_lambda-5.92.0.dist-info → datadog_lambda-5.94.0.dist-info}/METADATA +3 -3
- datadog_lambda-5.94.0.dist-info/RECORD +27 -0
- datadog_lambda-5.92.0.dist-info/LICENSE-3rdparty.csv +0 -3
- datadog_lambda-5.92.0.dist-info/RECORD +0 -26
- {datadog_lambda-5.92.0.dist-info → datadog_lambda-5.94.0.dist-info}/LICENSE +0 -0
- {datadog_lambda-5.92.0.dist-info → datadog_lambda-5.94.0.dist-info}/NOTICE +0 -0
- {datadog_lambda-5.92.0.dist-info → datadog_lambda-5.94.0.dist-info}/WHEEL +0 -0
datadog_lambda/tracing.py
CHANGED
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
import hashlib
|
|
6
6
|
import logging
|
|
7
7
|
import os
|
|
8
|
-
import json
|
|
9
8
|
import base64
|
|
9
|
+
import traceback
|
|
10
|
+
import ujson as json
|
|
10
11
|
from datetime import datetime, timezone
|
|
11
12
|
from typing import Optional, Dict
|
|
12
13
|
|
|
@@ -66,6 +67,8 @@ if dd_tracing_enabled:
|
|
|
66
67
|
|
|
67
68
|
telemetry_writer.enable()
|
|
68
69
|
|
|
70
|
+
is_lambda_context = os.environ.get(XrayDaemon.FUNCTION_NAME_HEADER_NAME) != ""
|
|
71
|
+
|
|
69
72
|
propagator = HTTPPropagator()
|
|
70
73
|
|
|
71
74
|
DD_TRACE_JAVA_TRACE_ID_PADDING = "00000000"
|
|
@@ -93,7 +96,7 @@ def _convert_xray_sampling(xray_sampled):
|
|
|
93
96
|
|
|
94
97
|
|
|
95
98
|
def _get_xray_trace_context():
|
|
96
|
-
if not is_lambda_context
|
|
99
|
+
if not is_lambda_context:
|
|
97
100
|
return None
|
|
98
101
|
|
|
99
102
|
xray_trace_entity = parse_xray_header(
|
|
@@ -109,11 +112,7 @@ def _get_xray_trace_context():
|
|
|
109
112
|
logger.debug(
|
|
110
113
|
"Converted trace context %s from X-Ray segment %s",
|
|
111
114
|
trace_context,
|
|
112
|
-
|
|
113
|
-
xray_trace_entity["trace_id"],
|
|
114
|
-
xray_trace_entity["parent_id"],
|
|
115
|
-
xray_trace_entity["sampled"],
|
|
116
|
-
),
|
|
115
|
+
xray_trace_entity,
|
|
117
116
|
)
|
|
118
117
|
return trace_context
|
|
119
118
|
|
|
@@ -124,7 +123,9 @@ def _get_dd_trace_py_context():
|
|
|
124
123
|
return None
|
|
125
124
|
|
|
126
125
|
logger.debug(
|
|
127
|
-
"found dd trace context:
|
|
126
|
+
"found dd trace context: trace_id=%s span_id=%s",
|
|
127
|
+
span.context.trace_id,
|
|
128
|
+
span.context.span_id,
|
|
128
129
|
)
|
|
129
130
|
return span.context
|
|
130
131
|
|
|
@@ -235,37 +236,31 @@ def extract_context_from_sqs_or_sns_event_or_context(event, lambda_context):
|
|
|
235
236
|
|
|
236
237
|
# logic to deal with SNS => SQS event
|
|
237
238
|
if "body" in first_record:
|
|
238
|
-
body_str = first_record.get("body"
|
|
239
|
+
body_str = first_record.get("body")
|
|
239
240
|
try:
|
|
240
241
|
body = json.loads(body_str)
|
|
241
242
|
if body.get("Type", "") == "Notification" and "TopicArn" in body:
|
|
242
243
|
logger.debug("Found SNS message inside SQS event")
|
|
243
244
|
first_record = get_first_record(create_sns_event(body))
|
|
244
245
|
except Exception:
|
|
245
|
-
first_record = event.get("Records")[0]
|
|
246
246
|
pass
|
|
247
247
|
|
|
248
|
-
msg_attributes = first_record.get(
|
|
249
|
-
|
|
250
|
-
first_record.get("Sns"
|
|
251
|
-
|
|
252
|
-
dd_payload = msg_attributes.get("_datadog"
|
|
248
|
+
msg_attributes = first_record.get("messageAttributes")
|
|
249
|
+
if msg_attributes is None:
|
|
250
|
+
sns_record = first_record.get("Sns") or {}
|
|
251
|
+
msg_attributes = sns_record.get("MessageAttributes") or {}
|
|
252
|
+
dd_payload = msg_attributes.get("_datadog")
|
|
253
253
|
if dd_payload:
|
|
254
254
|
# SQS uses dataType and binaryValue/stringValue
|
|
255
255
|
# SNS uses Type and Value
|
|
256
256
|
dd_json_data = None
|
|
257
|
-
dd_json_data_type = dd_payload.get("Type"
|
|
257
|
+
dd_json_data_type = dd_payload.get("Type") or dd_payload.get("dataType")
|
|
258
258
|
if dd_json_data_type == "Binary":
|
|
259
|
-
dd_json_data = dd_payload.get(
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
)
|
|
263
|
-
dd_json_data = base64.b64decode(dd_json_data)
|
|
259
|
+
dd_json_data = dd_payload.get("binaryValue") or dd_payload.get("Value")
|
|
260
|
+
if dd_json_data:
|
|
261
|
+
dd_json_data = base64.b64decode(dd_json_data)
|
|
264
262
|
elif dd_json_data_type == "String":
|
|
265
|
-
dd_json_data = dd_payload.get(
|
|
266
|
-
"stringValue",
|
|
267
|
-
dd_payload.get("Value", r"{}"),
|
|
268
|
-
)
|
|
263
|
+
dd_json_data = dd_payload.get("stringValue") or dd_payload.get("Value")
|
|
269
264
|
else:
|
|
270
265
|
logger.debug(
|
|
271
266
|
"Datadog Lambda Python only supports extracting trace"
|
|
@@ -278,23 +273,25 @@ def extract_context_from_sqs_or_sns_event_or_context(event, lambda_context):
|
|
|
278
273
|
else:
|
|
279
274
|
# Handle case where trace context is injected into attributes.AWSTraceHeader
|
|
280
275
|
# example: Root=1-654321ab-000000001234567890abcdef;Parent=0123456789abcdef;Sampled=1
|
|
281
|
-
|
|
282
|
-
if
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
276
|
+
attrs = first_record.get("attributes")
|
|
277
|
+
if attrs:
|
|
278
|
+
x_ray_header = attrs.get("AWSTraceHeader")
|
|
279
|
+
if x_ray_header:
|
|
280
|
+
x_ray_context = parse_xray_header(x_ray_header)
|
|
281
|
+
trace_id_parts = x_ray_context.get("trace_id", "").split("-")
|
|
282
|
+
if len(trace_id_parts) > 2 and trace_id_parts[2].startswith(
|
|
283
|
+
DD_TRACE_JAVA_TRACE_ID_PADDING
|
|
284
|
+
):
|
|
285
|
+
# If it starts with eight 0's padding,
|
|
286
|
+
# then this AWSTraceHeader contains Datadog injected trace context
|
|
287
|
+
logger.debug(
|
|
288
|
+
"Found dd-trace injected trace context from AWSTraceHeader"
|
|
289
|
+
)
|
|
290
|
+
return Context(
|
|
291
|
+
trace_id=int(trace_id_parts[2][8:], 16),
|
|
292
|
+
span_id=int(x_ray_context["parent_id"], 16),
|
|
293
|
+
sampling_priority=float(x_ray_context["sampled"]),
|
|
294
|
+
)
|
|
298
295
|
return extract_context_from_lambda_context(lambda_context)
|
|
299
296
|
except Exception as e:
|
|
300
297
|
logger.debug("The trace extractor returned with error %s", e)
|
|
@@ -339,21 +336,22 @@ def extract_context_from_kinesis_event(event, lambda_context):
|
|
|
339
336
|
"""
|
|
340
337
|
try:
|
|
341
338
|
record = get_first_record(event)
|
|
342
|
-
|
|
339
|
+
kinesis = record.get("kinesis")
|
|
340
|
+
if not kinesis:
|
|
341
|
+
return extract_context_from_lambda_context(lambda_context)
|
|
342
|
+
data = kinesis.get("data")
|
|
343
343
|
if data:
|
|
344
344
|
b64_bytes = data.encode("ascii")
|
|
345
345
|
str_bytes = base64.b64decode(b64_bytes)
|
|
346
346
|
data_str = str_bytes.decode("ascii")
|
|
347
347
|
data_obj = json.loads(data_str)
|
|
348
348
|
dd_ctx = data_obj.get("_datadog")
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
return extract_context_from_lambda_context(lambda_context)
|
|
352
|
-
|
|
353
|
-
return propagator.extract(dd_ctx)
|
|
349
|
+
if dd_ctx:
|
|
350
|
+
return propagator.extract(dd_ctx)
|
|
354
351
|
except Exception as e:
|
|
355
352
|
logger.debug("The trace extractor returned with error %s", e)
|
|
356
|
-
|
|
353
|
+
|
|
354
|
+
return extract_context_from_lambda_context(lambda_context)
|
|
357
355
|
|
|
358
356
|
|
|
359
357
|
def _deterministic_md5_hash(s: str) -> int:
|
|
@@ -380,7 +378,7 @@ def extract_context_from_step_functions(event, lambda_context):
|
|
|
380
378
|
state_entered_time = event.get("State").get("EnteredTime")
|
|
381
379
|
trace_id = _deterministic_md5_hash(execution_id)
|
|
382
380
|
parent_id = _deterministic_md5_hash(
|
|
383
|
-
execution_id
|
|
381
|
+
f"{execution_id}#{state_name}#{state_entered_time}"
|
|
384
382
|
)
|
|
385
383
|
sampling_priority = SamplingPriority.AUTO_KEEP
|
|
386
384
|
return Context(
|
|
@@ -396,11 +394,7 @@ def extract_context_custom_extractor(extractor, event, lambda_context):
|
|
|
396
394
|
Extract Datadog trace context using a custom trace extractor function
|
|
397
395
|
"""
|
|
398
396
|
try:
|
|
399
|
-
(
|
|
400
|
-
trace_id,
|
|
401
|
-
parent_id,
|
|
402
|
-
sampling_priority,
|
|
403
|
-
) = extractor(event, lambda_context)
|
|
397
|
+
trace_id, parent_id, sampling_priority = extractor(event, lambda_context)
|
|
404
398
|
return Context(
|
|
405
399
|
trace_id=int(trace_id),
|
|
406
400
|
span_id=int(parent_id),
|
|
@@ -426,15 +420,20 @@ def is_authorizer_response(response) -> bool:
|
|
|
426
420
|
|
|
427
421
|
def get_injected_authorizer_data(event, is_http_api) -> dict:
|
|
428
422
|
try:
|
|
429
|
-
|
|
423
|
+
req_ctx = event.get("requestContext")
|
|
424
|
+
if not req_ctx:
|
|
425
|
+
return None
|
|
426
|
+
authorizer_headers = req_ctx.get("authorizer")
|
|
430
427
|
if not authorizer_headers:
|
|
431
428
|
return None
|
|
432
429
|
|
|
433
|
-
|
|
434
|
-
authorizer_headers.get("lambda"
|
|
435
|
-
if
|
|
436
|
-
|
|
437
|
-
|
|
430
|
+
if is_http_api:
|
|
431
|
+
lambda_hdr = authorizer_headers.get("lambda")
|
|
432
|
+
if not lambda_hdr:
|
|
433
|
+
return None
|
|
434
|
+
dd_data_raw = lambda_hdr.get("_datadog")
|
|
435
|
+
else:
|
|
436
|
+
dd_data_raw = authorizer_headers.get("_datadog")
|
|
438
437
|
|
|
439
438
|
if not dd_data_raw:
|
|
440
439
|
return None
|
|
@@ -448,16 +447,19 @@ def get_injected_authorizer_data(event, is_http_api) -> dict:
|
|
|
448
447
|
# that case, we use the injected Authorizing_Request_Id to tell if it's cached. But token
|
|
449
448
|
# authorizers don't pass on the requestId. The Authorizing_Request_Id can't work for all
|
|
450
449
|
# cases neither. As a result, we combine both methods as shown below.
|
|
451
|
-
if authorizer_headers.get("integrationLatency", 0) > 0
|
|
452
|
-
"requestContext", {}
|
|
453
|
-
).get("requestId") == injected_data.get(Headers.Authorizing_Request_Id):
|
|
450
|
+
if authorizer_headers.get("integrationLatency", 0) > 0:
|
|
454
451
|
return injected_data
|
|
455
|
-
|
|
452
|
+
req_ctx = event.get("requestContext")
|
|
453
|
+
if not req_ctx:
|
|
456
454
|
return None
|
|
455
|
+
if req_ctx.get("requestId") == injected_data.get(
|
|
456
|
+
Headers.Authorizing_Request_Id
|
|
457
|
+
):
|
|
458
|
+
return injected_data
|
|
459
|
+
return None
|
|
457
460
|
|
|
458
461
|
except Exception as e:
|
|
459
462
|
logger.debug("Failed to check if invocated by an authorizer. error %s", e)
|
|
460
|
-
return None
|
|
461
463
|
|
|
462
464
|
|
|
463
465
|
def extract_dd_trace_context(
|
|
@@ -529,8 +531,8 @@ def get_dd_trace_context_obj():
|
|
|
529
531
|
xray_context = _get_xray_trace_context() # xray (sub)segment
|
|
530
532
|
except Exception as e:
|
|
531
533
|
logger.debug(
|
|
532
|
-
"get_dd_trace_context couldn't read from segment from x-ray, with error %s"
|
|
533
|
-
|
|
534
|
+
"get_dd_trace_context couldn't read from segment from x-ray, with error %s",
|
|
535
|
+
e,
|
|
534
536
|
)
|
|
535
537
|
if not xray_context:
|
|
536
538
|
return None
|
|
@@ -569,7 +571,7 @@ def set_correlation_ids():
|
|
|
569
571
|
|
|
570
572
|
TODO: Remove me when Datadog tracer is natively supported in Lambda.
|
|
571
573
|
"""
|
|
572
|
-
if not is_lambda_context
|
|
574
|
+
if not is_lambda_context:
|
|
573
575
|
logger.debug("set_correlation_ids is only supported in LambdaContext")
|
|
574
576
|
return
|
|
575
577
|
if dd_tracing_enabled:
|
|
@@ -613,14 +615,6 @@ def inject_correlation_ids():
|
|
|
613
615
|
logger.debug("logs injection configured")
|
|
614
616
|
|
|
615
617
|
|
|
616
|
-
def is_lambda_context():
|
|
617
|
-
"""
|
|
618
|
-
Return True if the X-Ray context is `LambdaContext`, rather than the
|
|
619
|
-
regular `Context` (e.g., when testing lambda functions locally).
|
|
620
|
-
"""
|
|
621
|
-
return os.environ.get(XrayDaemon.FUNCTION_NAME_HEADER_NAME, "") != ""
|
|
622
|
-
|
|
623
|
-
|
|
624
618
|
def set_dd_trace_py_root(trace_context_source, merge_xray_traces):
|
|
625
619
|
if trace_context_source == TraceContextSource.EVENT or merge_xray_traces:
|
|
626
620
|
context = Context(
|
|
@@ -635,8 +629,9 @@ def set_dd_trace_py_root(trace_context_source, merge_xray_traces):
|
|
|
635
629
|
|
|
636
630
|
tracer.context_provider.activate(context)
|
|
637
631
|
logger.debug(
|
|
638
|
-
"Set dd trace root context to:
|
|
639
|
-
|
|
632
|
+
"Set dd trace root context to: trace_id=%s span_id=%s",
|
|
633
|
+
context.trace_id,
|
|
634
|
+
context.span_id,
|
|
640
635
|
)
|
|
641
636
|
|
|
642
637
|
|
|
@@ -697,9 +692,7 @@ def create_inferred_span(
|
|
|
697
692
|
event_source.to_string(),
|
|
698
693
|
e,
|
|
699
694
|
)
|
|
700
|
-
return None
|
|
701
695
|
logger.debug("Unable to infer a span: unknown event type")
|
|
702
|
-
return None
|
|
703
696
|
|
|
704
697
|
|
|
705
698
|
def create_service_mapping(val):
|
|
@@ -721,20 +714,22 @@ def determine_service_name(service_mapping, specific_key, generic_key, default_v
|
|
|
721
714
|
return service_name
|
|
722
715
|
|
|
723
716
|
|
|
724
|
-
service_mapping = {}
|
|
725
717
|
# Initialization code
|
|
726
718
|
service_mapping_str = os.getenv("DD_SERVICE_MAPPING", "")
|
|
727
719
|
service_mapping = create_service_mapping(service_mapping_str)
|
|
728
720
|
|
|
721
|
+
_dd_origin = {"_dd.origin": "lambda"}
|
|
722
|
+
|
|
729
723
|
|
|
730
724
|
def create_inferred_span_from_lambda_function_url_event(event, context):
|
|
731
725
|
request_context = event.get("requestContext")
|
|
732
726
|
api_id = request_context.get("apiId")
|
|
733
727
|
domain = request_context.get("domainName")
|
|
734
728
|
service_name = determine_service_name(service_mapping, api_id, "lambda_url", domain)
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
729
|
+
http = request_context.get("http")
|
|
730
|
+
method = http.get("method") if http else None
|
|
731
|
+
path = http.get("path") if http else None
|
|
732
|
+
resource = f"{method} {path}"
|
|
738
733
|
tags = {
|
|
739
734
|
"operation_name": "aws.lambda.url",
|
|
740
735
|
"http.url": domain + path,
|
|
@@ -744,25 +739,23 @@ def create_inferred_span_from_lambda_function_url_event(event, context):
|
|
|
744
739
|
"request_id": context.aws_request_id,
|
|
745
740
|
}
|
|
746
741
|
request_time_epoch = request_context.get("timeEpoch")
|
|
747
|
-
|
|
748
|
-
"service": service_name,
|
|
749
|
-
"resource": resource,
|
|
750
|
-
"span_type": "http",
|
|
751
|
-
}
|
|
752
|
-
tracer.set_tags(
|
|
753
|
-
{"_dd.origin": "lambda"}
|
|
754
|
-
) # function urls don't count as lambda_inferred,
|
|
742
|
+
tracer.set_tags(_dd_origin) # function urls don't count as lambda_inferred,
|
|
755
743
|
# because they're in the same service as the inferring lambda function
|
|
756
|
-
span = tracer.trace(
|
|
744
|
+
span = tracer.trace(
|
|
745
|
+
"aws.lambda.url", service=service_name, resource=resource, span_type="http"
|
|
746
|
+
)
|
|
757
747
|
InferredSpanInfo.set_tags(tags, tag_source="self", synchronicity="sync")
|
|
758
748
|
if span:
|
|
759
749
|
span.set_tags(tags)
|
|
760
|
-
|
|
750
|
+
span.start_ns = int(request_time_epoch) * 1e6
|
|
761
751
|
return span
|
|
762
752
|
|
|
763
753
|
|
|
764
754
|
def is_api_gateway_invocation_async(event):
|
|
765
|
-
|
|
755
|
+
hdrs = event.get("headers")
|
|
756
|
+
if not hdrs:
|
|
757
|
+
return False
|
|
758
|
+
return hdrs.get("X-Amz-Invocation-Type") == "Event"
|
|
766
759
|
|
|
767
760
|
|
|
768
761
|
def insert_upstream_authorizer_span(
|
|
@@ -862,7 +855,7 @@ def create_inferred_span_from_api_gateway_websocket_event(
|
|
|
862
855
|
"resource": endpoint,
|
|
863
856
|
"span_type": "web",
|
|
864
857
|
}
|
|
865
|
-
tracer.set_tags(
|
|
858
|
+
tracer.set_tags(_dd_origin)
|
|
866
859
|
upstream_authorizer_span = None
|
|
867
860
|
finish_time_ns = None
|
|
868
861
|
if decode_authorizer_context:
|
|
@@ -893,7 +886,8 @@ def create_inferred_span_from_api_gateway_event(
|
|
|
893
886
|
)
|
|
894
887
|
method = event.get("httpMethod")
|
|
895
888
|
path = event.get("path")
|
|
896
|
-
|
|
889
|
+
resource_path = _get_resource_path(event, request_context)
|
|
890
|
+
resource = f"{method} {resource_path}"
|
|
897
891
|
tags = {
|
|
898
892
|
"operation_name": "aws.apigateway.rest",
|
|
899
893
|
"http.url": domain + path,
|
|
@@ -915,7 +909,7 @@ def create_inferred_span_from_api_gateway_event(
|
|
|
915
909
|
"resource": resource,
|
|
916
910
|
"span_type": "http",
|
|
917
911
|
}
|
|
918
|
-
tracer.set_tags(
|
|
912
|
+
tracer.set_tags(_dd_origin)
|
|
919
913
|
upstream_authorizer_span = None
|
|
920
914
|
finish_time_ns = None
|
|
921
915
|
if decode_authorizer_context:
|
|
@@ -936,6 +930,16 @@ def create_inferred_span_from_api_gateway_event(
|
|
|
936
930
|
return span
|
|
937
931
|
|
|
938
932
|
|
|
933
|
+
def _get_resource_path(event, request_context):
|
|
934
|
+
route_key = request_context.get("routeKey") or ""
|
|
935
|
+
if "{" in route_key:
|
|
936
|
+
try:
|
|
937
|
+
return route_key.split(" ")[1]
|
|
938
|
+
except Exception as e:
|
|
939
|
+
logger.debug("Error parsing routeKey: %s", e)
|
|
940
|
+
return event.get("rawPath") or request_context.get("resourcePath") or route_key
|
|
941
|
+
|
|
942
|
+
|
|
939
943
|
def create_inferred_span_from_http_api_event(
|
|
940
944
|
event, context, decode_authorizer_context: bool = True
|
|
941
945
|
):
|
|
@@ -945,17 +949,19 @@ def create_inferred_span_from_http_api_event(
|
|
|
945
949
|
service_name = determine_service_name(
|
|
946
950
|
service_mapping, api_id, "lambda_api_gateway", domain
|
|
947
951
|
)
|
|
948
|
-
|
|
952
|
+
http = request_context.get("http") or {}
|
|
953
|
+
method = http.get("method")
|
|
949
954
|
path = event.get("rawPath")
|
|
950
|
-
|
|
955
|
+
resource_path = _get_resource_path(event, request_context)
|
|
956
|
+
resource = f"{method} {resource_path}"
|
|
951
957
|
tags = {
|
|
952
958
|
"operation_name": "aws.httpapi",
|
|
953
959
|
"endpoint": path,
|
|
954
960
|
"http.url": domain + path,
|
|
955
|
-
"http.method":
|
|
956
|
-
"http.protocol":
|
|
957
|
-
"http.source_ip":
|
|
958
|
-
"http.user_agent":
|
|
961
|
+
"http.method": http.get("method"),
|
|
962
|
+
"http.protocol": http.get("protocol"),
|
|
963
|
+
"http.source_ip": http.get("sourceIp"),
|
|
964
|
+
"http.user_agent": http.get("userAgent"),
|
|
959
965
|
"resource_names": resource,
|
|
960
966
|
"request_id": context.aws_request_id,
|
|
961
967
|
"apiid": api_id,
|
|
@@ -967,12 +973,7 @@ def create_inferred_span_from_http_api_event(
|
|
|
967
973
|
InferredSpanInfo.set_tags(tags, tag_source="self", synchronicity="async")
|
|
968
974
|
else:
|
|
969
975
|
InferredSpanInfo.set_tags(tags, tag_source="self", synchronicity="sync")
|
|
970
|
-
|
|
971
|
-
"service": service_name,
|
|
972
|
-
"resource": resource,
|
|
973
|
-
"span_type": "http",
|
|
974
|
-
}
|
|
975
|
-
tracer.set_tags({"_dd.origin": "lambda"})
|
|
976
|
+
tracer.set_tags(_dd_origin)
|
|
976
977
|
inferred_span_start_ns = request_time_epoch_ms * 1e6
|
|
977
978
|
if decode_authorizer_context:
|
|
978
979
|
injected_authorizer_data = get_injected_authorizer_data(event, True)
|
|
@@ -980,7 +981,9 @@ def create_inferred_span_from_http_api_event(
|
|
|
980
981
|
inferred_span_start_ns = injected_authorizer_data.get(
|
|
981
982
|
Headers.Parent_Span_Finish_Time
|
|
982
983
|
)
|
|
983
|
-
span = tracer.trace(
|
|
984
|
+
span = tracer.trace(
|
|
985
|
+
"aws.httpapi", service=service_name, resource=resource, span_type="http"
|
|
986
|
+
)
|
|
984
987
|
if span:
|
|
985
988
|
span.set_tags(tags)
|
|
986
989
|
span.start_ns = int(inferred_span_start_ns)
|
|
@@ -996,21 +999,17 @@ def create_inferred_span_from_sqs_event(event, context):
|
|
|
996
999
|
service_name = determine_service_name(
|
|
997
1000
|
service_mapping, queue_name, "lambda_sqs", "sqs"
|
|
998
1001
|
)
|
|
1002
|
+
attrs = event_record.get("attributes") or {}
|
|
999
1003
|
tags = {
|
|
1000
1004
|
"operation_name": "aws.sqs",
|
|
1001
1005
|
"resource_names": queue_name,
|
|
1002
1006
|
"queuename": queue_name,
|
|
1003
1007
|
"event_source_arn": event_source_arn,
|
|
1004
1008
|
"receipt_handle": event_record.get("receiptHandle"),
|
|
1005
|
-
"sender_id":
|
|
1009
|
+
"sender_id": attrs.get("SenderId"),
|
|
1006
1010
|
}
|
|
1007
1011
|
InferredSpanInfo.set_tags(tags, tag_source="self", synchronicity="async")
|
|
1008
|
-
request_time_epoch =
|
|
1009
|
-
args = {
|
|
1010
|
-
"service": service_name,
|
|
1011
|
-
"resource": queue_name,
|
|
1012
|
-
"span_type": "web",
|
|
1013
|
-
}
|
|
1012
|
+
request_time_epoch = attrs.get("SentTimestamp")
|
|
1014
1013
|
start_time = int(request_time_epoch) / 1000
|
|
1015
1014
|
|
|
1016
1015
|
upstream_span = None
|
|
@@ -1039,15 +1038,17 @@ def create_inferred_span_from_sqs_event(event, context):
|
|
|
1039
1038
|
|
|
1040
1039
|
except Exception as e:
|
|
1041
1040
|
logger.debug(
|
|
1042
|
-
"Unable to create upstream span from SQS message, with error %s"
|
|
1041
|
+
"Unable to create upstream span from SQS message, with error %s", e
|
|
1043
1042
|
)
|
|
1044
1043
|
pass
|
|
1045
1044
|
|
|
1046
1045
|
# trace context needs to be set again as it is reset
|
|
1047
1046
|
# when sns_span.finish executes
|
|
1048
1047
|
tracer.context_provider.activate(trace_ctx)
|
|
1049
|
-
tracer.set_tags(
|
|
1050
|
-
span = tracer.trace(
|
|
1048
|
+
tracer.set_tags(_dd_origin)
|
|
1049
|
+
span = tracer.trace(
|
|
1050
|
+
"aws.sqs", service=service_name, resource=queue_name, span_type="web"
|
|
1051
|
+
)
|
|
1051
1052
|
if span:
|
|
1052
1053
|
span.set_tags(tags)
|
|
1053
1054
|
span.start = start_time
|
|
@@ -1059,8 +1060,8 @@ def create_inferred_span_from_sqs_event(event, context):
|
|
|
1059
1060
|
|
|
1060
1061
|
def create_inferred_span_from_sns_event(event, context):
|
|
1061
1062
|
event_record = get_first_record(event)
|
|
1062
|
-
sns_message = event_record.get("Sns")
|
|
1063
|
-
topic_arn =
|
|
1063
|
+
sns_message = event_record.get("Sns") or {}
|
|
1064
|
+
topic_arn = sns_message.get("TopicArn")
|
|
1064
1065
|
topic_name = topic_arn.split(":")[-1]
|
|
1065
1066
|
service_name = determine_service_name(
|
|
1066
1067
|
service_mapping, topic_name, "lambda_sns", "sns"
|
|
@@ -1075,21 +1076,19 @@ def create_inferred_span_from_sns_event(event, context):
|
|
|
1075
1076
|
}
|
|
1076
1077
|
|
|
1077
1078
|
# Subject not available in SNS => SQS scenario
|
|
1078
|
-
|
|
1079
|
-
|
|
1079
|
+
subject = sns_message.get("Subject")
|
|
1080
|
+
if subject:
|
|
1081
|
+
tags["subject"] = subject
|
|
1080
1082
|
|
|
1081
1083
|
InferredSpanInfo.set_tags(tags, tag_source="self", synchronicity="async")
|
|
1082
1084
|
sns_dt_format = "%Y-%m-%dT%H:%M:%S.%fZ"
|
|
1083
|
-
timestamp =
|
|
1085
|
+
timestamp = sns_message.get("Timestamp")
|
|
1084
1086
|
dt = datetime.strptime(timestamp, sns_dt_format)
|
|
1085
1087
|
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
"
|
|
1089
|
-
|
|
1090
|
-
}
|
|
1091
|
-
tracer.set_tags({"_dd.origin": "lambda"})
|
|
1092
|
-
span = tracer.trace("aws.sns", **args)
|
|
1088
|
+
tracer.set_tags(_dd_origin)
|
|
1089
|
+
span = tracer.trace(
|
|
1090
|
+
"aws.sns", service=service_name, resource=topic_name, span_type="web"
|
|
1091
|
+
)
|
|
1093
1092
|
if span:
|
|
1094
1093
|
span.set_tags(tags)
|
|
1095
1094
|
span.start = dt.replace(tzinfo=timezone.utc).timestamp()
|
|
@@ -1105,6 +1104,7 @@ def create_inferred_span_from_kinesis_event(event, context):
|
|
|
1105
1104
|
service_name = determine_service_name(
|
|
1106
1105
|
service_mapping, stream_name, "lambda_kinesis", "kinesis"
|
|
1107
1106
|
)
|
|
1107
|
+
kinesis = event_record.get("kinesis") or {}
|
|
1108
1108
|
tags = {
|
|
1109
1109
|
"operation_name": "aws.kinesis",
|
|
1110
1110
|
"resource_names": stream_name,
|
|
@@ -1114,20 +1114,15 @@ def create_inferred_span_from_kinesis_event(event, context):
|
|
|
1114
1114
|
"event_id": event_id,
|
|
1115
1115
|
"event_name": event_record.get("eventName"),
|
|
1116
1116
|
"event_version": event_record.get("eventVersion"),
|
|
1117
|
-
"partition_key":
|
|
1117
|
+
"partition_key": kinesis.get("partitionKey"),
|
|
1118
1118
|
}
|
|
1119
1119
|
InferredSpanInfo.set_tags(tags, tag_source="self", synchronicity="async")
|
|
1120
|
-
request_time_epoch =
|
|
1121
|
-
"approximateArrivalTimestamp"
|
|
1122
|
-
)
|
|
1120
|
+
request_time_epoch = kinesis.get("approximateArrivalTimestamp")
|
|
1123
1121
|
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
"
|
|
1127
|
-
|
|
1128
|
-
}
|
|
1129
|
-
tracer.set_tags({"_dd.origin": "lambda"})
|
|
1130
|
-
span = tracer.trace("aws.kinesis", **args)
|
|
1122
|
+
tracer.set_tags(_dd_origin)
|
|
1123
|
+
span = tracer.trace(
|
|
1124
|
+
"aws.kinesis", service=service_name, resource=stream_name, span_type="web"
|
|
1125
|
+
)
|
|
1131
1126
|
if span:
|
|
1132
1127
|
span.set_tags(tags)
|
|
1133
1128
|
span.start = request_time_epoch
|
|
@@ -1141,7 +1136,7 @@ def create_inferred_span_from_dynamodb_event(event, context):
|
|
|
1141
1136
|
service_name = determine_service_name(
|
|
1142
1137
|
service_mapping, table_name, "lambda_dynamodb", "dynamodb"
|
|
1143
1138
|
)
|
|
1144
|
-
dynamodb_message = event_record.get("dynamodb")
|
|
1139
|
+
dynamodb_message = event_record.get("dynamodb") or {}
|
|
1145
1140
|
tags = {
|
|
1146
1141
|
"operation_name": "aws.dynamodb",
|
|
1147
1142
|
"resource_names": table_name,
|
|
@@ -1154,16 +1149,11 @@ def create_inferred_span_from_dynamodb_event(event, context):
|
|
|
1154
1149
|
"size_bytes": str(dynamodb_message.get("SizeBytes")),
|
|
1155
1150
|
}
|
|
1156
1151
|
InferredSpanInfo.set_tags(tags, synchronicity="async", tag_source="self")
|
|
1157
|
-
request_time_epoch =
|
|
1158
|
-
|
|
1152
|
+
request_time_epoch = dynamodb_message.get("ApproximateCreationDateTime")
|
|
1153
|
+
tracer.set_tags(_dd_origin)
|
|
1154
|
+
span = tracer.trace(
|
|
1155
|
+
"aws.dynamodb", service=service_name, resource=table_name, span_type="web"
|
|
1159
1156
|
)
|
|
1160
|
-
args = {
|
|
1161
|
-
"service": service_name,
|
|
1162
|
-
"resource": table_name,
|
|
1163
|
-
"span_type": "web",
|
|
1164
|
-
}
|
|
1165
|
-
tracer.set_tags({"_dd.origin": "lambda"})
|
|
1166
|
-
span = tracer.trace("aws.dynamodb", **args)
|
|
1167
1157
|
if span:
|
|
1168
1158
|
span.set_tags(tags)
|
|
1169
1159
|
|
|
@@ -1173,7 +1163,10 @@ def create_inferred_span_from_dynamodb_event(event, context):
|
|
|
1173
1163
|
|
|
1174
1164
|
def create_inferred_span_from_s3_event(event, context):
|
|
1175
1165
|
event_record = get_first_record(event)
|
|
1176
|
-
|
|
1166
|
+
s3 = event_record.get("s3") or {}
|
|
1167
|
+
bucket = s3.get("bucket") or {}
|
|
1168
|
+
obj = s3.get("object") or {}
|
|
1169
|
+
bucket_name = bucket.get("name")
|
|
1177
1170
|
service_name = determine_service_name(
|
|
1178
1171
|
service_mapping, bucket_name, "lambda_s3", "s3"
|
|
1179
1172
|
)
|
|
@@ -1182,23 +1175,20 @@ def create_inferred_span_from_s3_event(event, context):
|
|
|
1182
1175
|
"resource_names": bucket_name,
|
|
1183
1176
|
"event_name": event_record.get("eventName"),
|
|
1184
1177
|
"bucketname": bucket_name,
|
|
1185
|
-
"bucket_arn":
|
|
1186
|
-
"object_key":
|
|
1187
|
-
"object_size": str(
|
|
1188
|
-
"object_etag":
|
|
1178
|
+
"bucket_arn": bucket.get("arn"),
|
|
1179
|
+
"object_key": obj.get("key"),
|
|
1180
|
+
"object_size": str(obj.get("size")),
|
|
1181
|
+
"object_etag": obj.get("eTag"),
|
|
1189
1182
|
}
|
|
1190
1183
|
InferredSpanInfo.set_tags(tags, synchronicity="async", tag_source="self")
|
|
1191
1184
|
dt_format = "%Y-%m-%dT%H:%M:%S.%fZ"
|
|
1192
1185
|
timestamp = event_record.get("eventTime")
|
|
1193
1186
|
dt = datetime.strptime(timestamp, dt_format)
|
|
1194
1187
|
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
"
|
|
1198
|
-
|
|
1199
|
-
}
|
|
1200
|
-
tracer.set_tags({"_dd.origin": "lambda"})
|
|
1201
|
-
span = tracer.trace("aws.s3", **args)
|
|
1188
|
+
tracer.set_tags(_dd_origin)
|
|
1189
|
+
span = tracer.trace(
|
|
1190
|
+
"aws.s3", service=service_name, resource=bucket_name, span_type="web"
|
|
1191
|
+
)
|
|
1202
1192
|
if span:
|
|
1203
1193
|
span.set_tags(tags)
|
|
1204
1194
|
span.start = dt.replace(tzinfo=timezone.utc).timestamp()
|
|
@@ -1224,13 +1214,10 @@ def create_inferred_span_from_eventbridge_event(event, context):
|
|
|
1224
1214
|
timestamp = event.get("time")
|
|
1225
1215
|
dt = datetime.strptime(timestamp, dt_format)
|
|
1226
1216
|
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
"
|
|
1230
|
-
|
|
1231
|
-
}
|
|
1232
|
-
tracer.set_tags({"_dd.origin": "lambda"})
|
|
1233
|
-
span = tracer.trace("aws.eventbridge", **args)
|
|
1217
|
+
tracer.set_tags(_dd_origin)
|
|
1218
|
+
span = tracer.trace(
|
|
1219
|
+
"aws.eventbridge", service=service_name, resource=source, span_type="web"
|
|
1220
|
+
)
|
|
1234
1221
|
if span:
|
|
1235
1222
|
span.set_tags(tags)
|
|
1236
1223
|
span.start = dt.replace(tzinfo=timezone.utc).timestamp()
|
|
@@ -1247,7 +1234,7 @@ def create_function_execution_span(
|
|
|
1247
1234
|
trigger_tags,
|
|
1248
1235
|
parent_span=None,
|
|
1249
1236
|
):
|
|
1250
|
-
tags =
|
|
1237
|
+
tags = None
|
|
1251
1238
|
if context:
|
|
1252
1239
|
function_arn = (context.invoked_function_arn or "").lower()
|
|
1253
1240
|
tk = function_arn.split(":")
|
|
@@ -1266,18 +1253,19 @@ def create_function_execution_span(
|
|
|
1266
1253
|
"dd_trace": ddtrace_version,
|
|
1267
1254
|
"span.name": "aws.lambda",
|
|
1268
1255
|
}
|
|
1256
|
+
tags = tags or {}
|
|
1269
1257
|
if is_proactive_init:
|
|
1270
1258
|
tags["proactive_initialization"] = str(is_proactive_init).lower()
|
|
1271
1259
|
if trace_context_source == TraceContextSource.XRAY and merge_xray_traces:
|
|
1272
1260
|
tags["_dd.parent_source"] = trace_context_source
|
|
1273
1261
|
tags.update(trigger_tags)
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
"
|
|
1277
|
-
"
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1262
|
+
tracer.set_tags(_dd_origin)
|
|
1263
|
+
span = tracer.trace(
|
|
1264
|
+
"aws.lambda",
|
|
1265
|
+
service="aws.lambda",
|
|
1266
|
+
resource=function_name,
|
|
1267
|
+
span_type="serverless",
|
|
1268
|
+
)
|
|
1281
1269
|
if span:
|
|
1282
1270
|
span.set_tags(tags)
|
|
1283
1271
|
if parent_span:
|
|
@@ -1333,3 +1321,34 @@ class InferredSpanInfo(object):
|
|
|
1333
1321
|
e,
|
|
1334
1322
|
)
|
|
1335
1323
|
return False
|
|
1324
|
+
|
|
1325
|
+
|
|
1326
|
+
def emit_telemetry_on_exception_outside_of_handler(
|
|
1327
|
+
exception, resource_name, handler_load_start_time_ns
|
|
1328
|
+
):
|
|
1329
|
+
"""
|
|
1330
|
+
Emit an enhanced error metric and create a span for exceptions occurring outside the handler
|
|
1331
|
+
"""
|
|
1332
|
+
submit_errors_metric(None)
|
|
1333
|
+
if dd_tracing_enabled:
|
|
1334
|
+
span = tracer.trace(
|
|
1335
|
+
"aws.lambda",
|
|
1336
|
+
service="aws.lambda",
|
|
1337
|
+
resource=resource_name,
|
|
1338
|
+
span_type="serverless",
|
|
1339
|
+
)
|
|
1340
|
+
span.start_ns = handler_load_start_time_ns
|
|
1341
|
+
|
|
1342
|
+
tags = {
|
|
1343
|
+
"error.status": 500,
|
|
1344
|
+
"error.type": type(exception).__name__,
|
|
1345
|
+
"error.message": exception,
|
|
1346
|
+
"error.stack": traceback.format_exc(),
|
|
1347
|
+
"resource_names": resource_name,
|
|
1348
|
+
"resource.name": resource_name,
|
|
1349
|
+
"operation_name": "aws.lambda",
|
|
1350
|
+
"status": "error",
|
|
1351
|
+
}
|
|
1352
|
+
span.set_tags(tags)
|
|
1353
|
+
span.error = 1
|
|
1354
|
+
span.finish()
|