datadog_lambda 6.110.0__py3-none-any.whl → 7.112.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 +7 -0
- datadog_lambda/api.py +7 -8
- datadog_lambda/asm.py +184 -0
- datadog_lambda/cold_start.py +3 -9
- datadog_lambda/config.py +146 -0
- datadog_lambda/metric.py +5 -12
- datadog_lambda/patch.py +4 -8
- datadog_lambda/span_pointers.py +2 -7
- datadog_lambda/tag_object.py +3 -4
- datadog_lambda/tracing.py +65 -33
- datadog_lambda/trigger.py +2 -2
- datadog_lambda/version.py +1 -1
- datadog_lambda/wrapper.py +56 -125
- {datadog_lambda-6.110.0.dist-info → datadog_lambda-7.112.0.dist-info}/METADATA +2 -2
- datadog_lambda-7.112.0.dist-info/RECORD +30 -0
- datadog_lambda/fips.py +0 -19
- datadog_lambda-6.110.0.dist-info/RECORD +0 -29
- {datadog_lambda-6.110.0.dist-info → datadog_lambda-7.112.0.dist-info}/LICENSE +0 -0
- {datadog_lambda-6.110.0.dist-info → datadog_lambda-7.112.0.dist-info}/LICENSE-3rdparty.csv +0 -0
- {datadog_lambda-6.110.0.dist-info → datadog_lambda-7.112.0.dist-info}/NOTICE +0 -0
- {datadog_lambda-6.110.0.dist-info → datadog_lambda-7.112.0.dist-info}/WHEEL +0 -0
datadog_lambda/tracing.py
CHANGED
|
@@ -32,6 +32,8 @@ from ddtrace import patch
|
|
|
32
32
|
from ddtrace import __version__ as ddtrace_version
|
|
33
33
|
from ddtrace.propagation.http import HTTPPropagator
|
|
34
34
|
from ddtrace.trace import Context, Span, tracer
|
|
35
|
+
|
|
36
|
+
from datadog_lambda.config import config
|
|
35
37
|
from datadog_lambda import __version__ as datadog_lambda_version
|
|
36
38
|
from datadog_lambda.trigger import (
|
|
37
39
|
_EventSource,
|
|
@@ -42,10 +44,7 @@ from datadog_lambda.trigger import (
|
|
|
42
44
|
EventSubtypes,
|
|
43
45
|
)
|
|
44
46
|
|
|
45
|
-
|
|
46
|
-
os.environ.get("DD_TRACE_OTEL_ENABLED", "false").lower() == "true"
|
|
47
|
-
)
|
|
48
|
-
if dd_trace_otel_enabled:
|
|
47
|
+
if config.otel_enabled:
|
|
49
48
|
from opentelemetry.trace import set_tracer_provider
|
|
50
49
|
from ddtrace.opentelemetry import TracerProvider
|
|
51
50
|
|
|
@@ -55,18 +54,11 @@ if dd_trace_otel_enabled:
|
|
|
55
54
|
logger = logging.getLogger(__name__)
|
|
56
55
|
|
|
57
56
|
dd_trace_context = None
|
|
58
|
-
|
|
59
|
-
if dd_tracing_enabled:
|
|
57
|
+
if config.telemetry_enabled:
|
|
60
58
|
# Enable the telemetry client if the user has opted in
|
|
61
|
-
|
|
62
|
-
os.environ.get("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "false").lower()
|
|
63
|
-
== "true"
|
|
64
|
-
):
|
|
65
|
-
from ddtrace.internal.telemetry import telemetry_writer
|
|
59
|
+
from ddtrace.internal.telemetry import telemetry_writer
|
|
66
60
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
is_lambda_context = os.environ.get(XrayDaemon.FUNCTION_NAME_HEADER_NAME) != ""
|
|
61
|
+
telemetry_writer.enable()
|
|
70
62
|
|
|
71
63
|
propagator = HTTPPropagator()
|
|
72
64
|
|
|
@@ -75,6 +67,24 @@ HIGHER_64_BITS = "HIGHER_64_BITS"
|
|
|
75
67
|
LOWER_64_BITS = "LOWER_64_BITS"
|
|
76
68
|
|
|
77
69
|
|
|
70
|
+
def _dsm_set_checkpoint(context_json, event_type, arn):
|
|
71
|
+
if not config.data_streams_enabled:
|
|
72
|
+
return
|
|
73
|
+
|
|
74
|
+
if not arn:
|
|
75
|
+
return
|
|
76
|
+
|
|
77
|
+
try:
|
|
78
|
+
from ddtrace.data_streams import set_consume_checkpoint
|
|
79
|
+
|
|
80
|
+
carrier_get = lambda k: context_json and context_json.get(k) # noqa: E731
|
|
81
|
+
set_consume_checkpoint(event_type, arn, carrier_get, manual_checkpoint=False)
|
|
82
|
+
except Exception as e:
|
|
83
|
+
logger.debug(
|
|
84
|
+
f"DSM:Failed to set consume checkpoint for {event_type} {arn}: {e}"
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
|
|
78
88
|
def _convert_xray_trace_id(xray_trace_id):
|
|
79
89
|
"""
|
|
80
90
|
Convert X-Ray trace id (hex)'s last 63 bits to a Datadog trace id (int).
|
|
@@ -97,7 +107,7 @@ def _convert_xray_sampling(xray_sampled):
|
|
|
97
107
|
|
|
98
108
|
|
|
99
109
|
def _get_xray_trace_context():
|
|
100
|
-
if not is_lambda_context:
|
|
110
|
+
if not config.is_lambda_context:
|
|
101
111
|
return None
|
|
102
112
|
|
|
103
113
|
xray_trace_entity = parse_xray_header(
|
|
@@ -210,7 +220,9 @@ def create_sns_event(message):
|
|
|
210
220
|
}
|
|
211
221
|
|
|
212
222
|
|
|
213
|
-
def extract_context_from_sqs_or_sns_event_or_context(
|
|
223
|
+
def extract_context_from_sqs_or_sns_event_or_context(
|
|
224
|
+
event, lambda_context, event_source
|
|
225
|
+
):
|
|
214
226
|
"""
|
|
215
227
|
Extract Datadog trace context from an SQS event.
|
|
216
228
|
|
|
@@ -222,7 +234,10 @@ def extract_context_from_sqs_or_sns_event_or_context(event, lambda_context):
|
|
|
222
234
|
Lambda Context.
|
|
223
235
|
|
|
224
236
|
Falls back to lambda context if no trace data is found in the SQS message attributes.
|
|
237
|
+
Set a DSM checkpoint if DSM is enabled and the method for context propagation is supported.
|
|
225
238
|
"""
|
|
239
|
+
source_arn = ""
|
|
240
|
+
event_type = "sqs" if event_source.equals(EventTypes.SQS) else "sns"
|
|
226
241
|
|
|
227
242
|
# EventBridge => SQS
|
|
228
243
|
try:
|
|
@@ -234,6 +249,7 @@ def extract_context_from_sqs_or_sns_event_or_context(event, lambda_context):
|
|
|
234
249
|
|
|
235
250
|
try:
|
|
236
251
|
first_record = event.get("Records")[0]
|
|
252
|
+
source_arn = first_record.get("eventSourceARN", "")
|
|
237
253
|
|
|
238
254
|
# logic to deal with SNS => SQS event
|
|
239
255
|
if "body" in first_record:
|
|
@@ -249,6 +265,9 @@ def extract_context_from_sqs_or_sns_event_or_context(event, lambda_context):
|
|
|
249
265
|
msg_attributes = first_record.get("messageAttributes")
|
|
250
266
|
if msg_attributes is None:
|
|
251
267
|
sns_record = first_record.get("Sns") or {}
|
|
268
|
+
# SNS->SQS event would extract SNS arn without this check
|
|
269
|
+
if event_source.equals(EventTypes.SNS):
|
|
270
|
+
source_arn = sns_record.get("TopicArn", "")
|
|
252
271
|
msg_attributes = sns_record.get("MessageAttributes") or {}
|
|
253
272
|
dd_payload = msg_attributes.get("_datadog")
|
|
254
273
|
if dd_payload:
|
|
@@ -280,8 +299,9 @@ def extract_context_from_sqs_or_sns_event_or_context(event, lambda_context):
|
|
|
280
299
|
logger.debug(
|
|
281
300
|
"Failed to extract Step Functions context from SQS/SNS event."
|
|
282
301
|
)
|
|
283
|
-
|
|
284
|
-
|
|
302
|
+
context = propagator.extract(dd_data)
|
|
303
|
+
_dsm_set_checkpoint(dd_data, event_type, source_arn)
|
|
304
|
+
return context
|
|
285
305
|
else:
|
|
286
306
|
# Handle case where trace context is injected into attributes.AWSTraceHeader
|
|
287
307
|
# example: Root=1-654321ab-000000001234567890abcdef;Parent=0123456789abcdef;Sampled=1
|
|
@@ -304,9 +324,13 @@ def extract_context_from_sqs_or_sns_event_or_context(event, lambda_context):
|
|
|
304
324
|
span_id=int(x_ray_context["parent_id"], 16),
|
|
305
325
|
sampling_priority=float(x_ray_context["sampled"]),
|
|
306
326
|
)
|
|
327
|
+
# Still want to set a DSM checkpoint even if DSM context not propagated
|
|
328
|
+
_dsm_set_checkpoint(None, event_type, source_arn)
|
|
307
329
|
return extract_context_from_lambda_context(lambda_context)
|
|
308
330
|
except Exception as e:
|
|
309
331
|
logger.debug("The trace extractor returned with error %s", e)
|
|
332
|
+
# Still want to set a DSM checkpoint even if DSM context not propagated
|
|
333
|
+
_dsm_set_checkpoint(None, event_type, source_arn)
|
|
310
334
|
return extract_context_from_lambda_context(lambda_context)
|
|
311
335
|
|
|
312
336
|
|
|
@@ -365,9 +389,12 @@ def extract_context_from_eventbridge_event(event, lambda_context):
|
|
|
365
389
|
def extract_context_from_kinesis_event(event, lambda_context):
|
|
366
390
|
"""
|
|
367
391
|
Extract datadog trace context from a Kinesis Stream's base64 encoded data string
|
|
392
|
+
Set a DSM checkpoint if DSM is enabled and the method for context propagation is supported.
|
|
368
393
|
"""
|
|
394
|
+
source_arn = ""
|
|
369
395
|
try:
|
|
370
396
|
record = get_first_record(event)
|
|
397
|
+
source_arn = record.get("eventSourceARN", "")
|
|
371
398
|
kinesis = record.get("kinesis")
|
|
372
399
|
if not kinesis:
|
|
373
400
|
return extract_context_from_lambda_context(lambda_context)
|
|
@@ -381,10 +408,13 @@ def extract_context_from_kinesis_event(event, lambda_context):
|
|
|
381
408
|
data_obj = json.loads(data_str)
|
|
382
409
|
dd_ctx = data_obj.get("_datadog")
|
|
383
410
|
if dd_ctx:
|
|
384
|
-
|
|
411
|
+
context = propagator.extract(dd_ctx)
|
|
412
|
+
_dsm_set_checkpoint(dd_ctx, "kinesis", source_arn)
|
|
413
|
+
return context
|
|
385
414
|
except Exception as e:
|
|
386
415
|
logger.debug("The trace extractor returned with error %s", e)
|
|
387
|
-
|
|
416
|
+
# Still want to set a DSM checkpoint even if DSM context not propagated
|
|
417
|
+
_dsm_set_checkpoint(None, "kinesis", source_arn)
|
|
388
418
|
return extract_context_from_lambda_context(lambda_context)
|
|
389
419
|
|
|
390
420
|
|
|
@@ -602,7 +632,7 @@ def extract_dd_trace_context(
|
|
|
602
632
|
)
|
|
603
633
|
elif event_source.equals(EventTypes.SNS) or event_source.equals(EventTypes.SQS):
|
|
604
634
|
context = extract_context_from_sqs_or_sns_event_or_context(
|
|
605
|
-
event, lambda_context
|
|
635
|
+
event, lambda_context, event_source
|
|
606
636
|
)
|
|
607
637
|
elif event_source.equals(EventTypes.EVENTBRIDGE):
|
|
608
638
|
context = extract_context_from_eventbridge_event(event, lambda_context)
|
|
@@ -639,13 +669,11 @@ def get_dd_trace_context_obj():
|
|
|
639
669
|
automatically, but this function can be used to manually inject the trace
|
|
640
670
|
context to an outgoing request.
|
|
641
671
|
"""
|
|
642
|
-
if
|
|
672
|
+
if config.trace_enabled:
|
|
643
673
|
dd_trace_py_context = _get_dd_trace_py_context()
|
|
644
674
|
if _is_context_complete(dd_trace_py_context):
|
|
645
675
|
return dd_trace_py_context
|
|
646
676
|
|
|
647
|
-
global dd_trace_context
|
|
648
|
-
|
|
649
677
|
try:
|
|
650
678
|
xray_context = _get_xray_trace_context() # xray (sub)segment
|
|
651
679
|
except Exception as e:
|
|
@@ -690,10 +718,10 @@ def set_correlation_ids():
|
|
|
690
718
|
|
|
691
719
|
TODO: Remove me when Datadog tracer is natively supported in Lambda.
|
|
692
720
|
"""
|
|
693
|
-
if not is_lambda_context:
|
|
721
|
+
if not config.is_lambda_context:
|
|
694
722
|
logger.debug("set_correlation_ids is only supported in LambdaContext")
|
|
695
723
|
return
|
|
696
|
-
if
|
|
724
|
+
if config.trace_enabled:
|
|
697
725
|
logger.debug("using ddtrace implementation for spans")
|
|
698
726
|
return
|
|
699
727
|
|
|
@@ -850,13 +878,14 @@ def create_inferred_span_from_lambda_function_url_event(event, context):
|
|
|
850
878
|
http = request_context.get("http")
|
|
851
879
|
method = http.get("method") if http else None
|
|
852
880
|
path = http.get("path") if http else None
|
|
881
|
+
http_url = f"https://{domain}{path}"
|
|
853
882
|
resource = f"{method} {path}"
|
|
854
883
|
tags = {
|
|
855
884
|
"operation_name": "aws.lambda.url",
|
|
856
|
-
"http.url":
|
|
885
|
+
"http.url": http_url,
|
|
857
886
|
"endpoint": path,
|
|
858
887
|
"http.method": method,
|
|
859
|
-
"resource_names":
|
|
888
|
+
"resource_names": resource,
|
|
860
889
|
"request_id": context.aws_request_id,
|
|
861
890
|
}
|
|
862
891
|
request_time_epoch = request_context.get("timeEpoch")
|
|
@@ -868,7 +897,7 @@ def create_inferred_span_from_lambda_function_url_event(event, context):
|
|
|
868
897
|
InferredSpanInfo.set_tags(tags, tag_source="self", synchronicity="sync")
|
|
869
898
|
if span:
|
|
870
899
|
span.set_tags(tags)
|
|
871
|
-
span.start_ns = int(request_time_epoch
|
|
900
|
+
span.start_ns = int(request_time_epoch * 1e6)
|
|
872
901
|
return span
|
|
873
902
|
|
|
874
903
|
|
|
@@ -948,6 +977,7 @@ def create_inferred_span_from_api_gateway_websocket_event(
|
|
|
948
977
|
request_context = event.get("requestContext")
|
|
949
978
|
domain = request_context.get("domainName")
|
|
950
979
|
endpoint = request_context.get("routeKey")
|
|
980
|
+
http_url = f"https://{domain}{endpoint}"
|
|
951
981
|
api_id = request_context.get("apiId")
|
|
952
982
|
|
|
953
983
|
service_name = determine_service_name(
|
|
@@ -955,7 +985,7 @@ def create_inferred_span_from_api_gateway_websocket_event(
|
|
|
955
985
|
)
|
|
956
986
|
tags = {
|
|
957
987
|
"operation_name": "aws.apigateway.websocket",
|
|
958
|
-
"http.url":
|
|
988
|
+
"http.url": http_url,
|
|
959
989
|
"endpoint": endpoint,
|
|
960
990
|
"resource_names": endpoint,
|
|
961
991
|
"apiid": api_id,
|
|
@@ -1007,11 +1037,12 @@ def create_inferred_span_from_api_gateway_event(
|
|
|
1007
1037
|
)
|
|
1008
1038
|
method = event.get("httpMethod")
|
|
1009
1039
|
path = event.get("path")
|
|
1040
|
+
http_url = f"https://{domain}{path}"
|
|
1010
1041
|
resource_path = _get_resource_path(event, request_context)
|
|
1011
1042
|
resource = f"{method} {resource_path}"
|
|
1012
1043
|
tags = {
|
|
1013
1044
|
"operation_name": "aws.apigateway.rest",
|
|
1014
|
-
"http.url":
|
|
1045
|
+
"http.url": http_url,
|
|
1015
1046
|
"endpoint": path,
|
|
1016
1047
|
"http.method": method,
|
|
1017
1048
|
"resource_names": resource,
|
|
@@ -1073,12 +1104,13 @@ def create_inferred_span_from_http_api_event(
|
|
|
1073
1104
|
http = request_context.get("http") or {}
|
|
1074
1105
|
method = http.get("method")
|
|
1075
1106
|
path = event.get("rawPath")
|
|
1107
|
+
http_url = f"https://{domain}{path}"
|
|
1076
1108
|
resource_path = _get_resource_path(event, request_context)
|
|
1077
1109
|
resource = f"{method} {resource_path}"
|
|
1078
1110
|
tags = {
|
|
1079
1111
|
"operation_name": "aws.httpapi",
|
|
1080
1112
|
"endpoint": path,
|
|
1081
|
-
"http.url":
|
|
1113
|
+
"http.url": http_url,
|
|
1082
1114
|
"http.method": http.get("method"),
|
|
1083
1115
|
"http.protocol": http.get("protocol"),
|
|
1084
1116
|
"http.source_ip": http.get("sourceIp"),
|
|
@@ -1476,7 +1508,7 @@ def emit_telemetry_on_exception_outside_of_handler(
|
|
|
1476
1508
|
Emit an enhanced error metric and create a span for exceptions occurring outside the handler
|
|
1477
1509
|
"""
|
|
1478
1510
|
submit_errors_metric(None)
|
|
1479
|
-
if
|
|
1511
|
+
if config.trace_enabled:
|
|
1480
1512
|
span = tracer.trace(
|
|
1481
1513
|
"aws.lambda",
|
|
1482
1514
|
service="aws.lambda",
|
datadog_lambda/trigger.py
CHANGED
|
@@ -153,7 +153,7 @@ def parse_event_source(event: dict) -> _EventSource:
|
|
|
153
153
|
event_source = _EventSource(EventTypes.STEPFUNCTIONS)
|
|
154
154
|
|
|
155
155
|
event_record = get_first_record(event)
|
|
156
|
-
if event_record:
|
|
156
|
+
if event_record and isinstance(event_record, dict):
|
|
157
157
|
aws_event_source = event_record.get("eventSource") or event_record.get(
|
|
158
158
|
"EventSource"
|
|
159
159
|
)
|
|
@@ -301,7 +301,7 @@ def extract_http_tags(event):
|
|
|
301
301
|
if request_context and request_context.get("stage"):
|
|
302
302
|
domain_name = request_context.get("domainName")
|
|
303
303
|
if domain_name:
|
|
304
|
-
http_tags["http.url"] = domain_name
|
|
304
|
+
http_tags["http.url"] = f"https://{domain_name}"
|
|
305
305
|
|
|
306
306
|
path = request_context.get("path")
|
|
307
307
|
method = request_context.get("httpMethod")
|
datadog_lambda/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "
|
|
1
|
+
__version__ = "7.112.0"
|
datadog_lambda/wrapper.py
CHANGED
|
@@ -9,6 +9,7 @@ import ujson as json
|
|
|
9
9
|
from importlib import import_module
|
|
10
10
|
from time import time_ns
|
|
11
11
|
|
|
12
|
+
from datadog_lambda.asm import asm_set_context, asm_start_response, asm_start_request
|
|
12
13
|
from datadog_lambda.extension import should_use_extension, flush_extension
|
|
13
14
|
from datadog_lambda.cold_start import (
|
|
14
15
|
set_cold_start,
|
|
@@ -17,19 +18,19 @@ from datadog_lambda.cold_start import (
|
|
|
17
18
|
is_new_sandbox,
|
|
18
19
|
ColdStartTracer,
|
|
19
20
|
)
|
|
21
|
+
from datadog_lambda.config import config
|
|
20
22
|
from datadog_lambda.constants import (
|
|
21
23
|
TraceContextSource,
|
|
22
24
|
XraySubsegment,
|
|
23
25
|
Headers,
|
|
24
26
|
)
|
|
25
27
|
from datadog_lambda.module_name import modify_module_name
|
|
26
|
-
from datadog_lambda.patch import patch_all
|
|
27
28
|
from datadog_lambda.span_pointers import calculate_span_pointers
|
|
29
|
+
from datadog_lambda.tag_object import tag_object
|
|
28
30
|
from datadog_lambda.tracing import (
|
|
29
31
|
extract_dd_trace_context,
|
|
30
32
|
create_dd_dummy_metadata_subsegment,
|
|
31
33
|
inject_correlation_ids,
|
|
32
|
-
dd_tracing_enabled,
|
|
33
34
|
mark_trace_as_error_for_5xx_responses,
|
|
34
35
|
set_correlation_ids,
|
|
35
36
|
set_dd_trace_py_root,
|
|
@@ -45,64 +46,20 @@ from datadog_lambda.trigger import (
|
|
|
45
46
|
extract_http_status_code_tag,
|
|
46
47
|
)
|
|
47
48
|
|
|
48
|
-
|
|
49
|
-
if profiling_env_var:
|
|
49
|
+
if config.profiling_enabled:
|
|
50
50
|
from ddtrace.profiling import profiler
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
if llmobs_env_var:
|
|
52
|
+
if config.llmobs_enabled:
|
|
54
53
|
from ddtrace.llmobs import LLMObs
|
|
55
54
|
|
|
56
|
-
|
|
57
|
-
"DD_EXCEPTION_REPLAY_ENABLED", "false"
|
|
58
|
-
).lower() in ("true", "1")
|
|
59
|
-
if exception_replay_env_var:
|
|
55
|
+
if config.exception_replay_enabled:
|
|
60
56
|
from ddtrace.debugging._exception.replay import SpanExceptionHandler
|
|
61
57
|
from ddtrace.debugging._uploader import LogsIntakeUploaderV1
|
|
62
58
|
|
|
63
59
|
logger = logging.getLogger(__name__)
|
|
64
60
|
|
|
65
|
-
DD_FLUSH_TO_LOG = "DD_FLUSH_TO_LOG"
|
|
66
|
-
DD_LOGS_INJECTION = "DD_LOGS_INJECTION"
|
|
67
|
-
DD_MERGE_XRAY_TRACES = "DD_MERGE_XRAY_TRACES"
|
|
68
|
-
AWS_LAMBDA_FUNCTION_NAME = "AWS_LAMBDA_FUNCTION_NAME"
|
|
69
|
-
DD_LOCAL_TEST = "DD_LOCAL_TEST"
|
|
70
|
-
DD_TRACE_EXTRACTOR = "DD_TRACE_EXTRACTOR"
|
|
71
|
-
DD_TRACE_MANAGED_SERVICES = "DD_TRACE_MANAGED_SERVICES"
|
|
72
|
-
DD_ENCODE_AUTHORIZER_CONTEXT = "DD_ENCODE_AUTHORIZER_CONTEXT"
|
|
73
|
-
DD_DECODE_AUTHORIZER_CONTEXT = "DD_DECODE_AUTHORIZER_CONTEXT"
|
|
74
|
-
DD_COLD_START_TRACING = "DD_COLD_START_TRACING"
|
|
75
|
-
DD_MIN_COLD_START_DURATION = "DD_MIN_COLD_START_DURATION"
|
|
76
|
-
DD_COLD_START_TRACE_SKIP_LIB = "DD_COLD_START_TRACE_SKIP_LIB"
|
|
77
|
-
DD_CAPTURE_LAMBDA_PAYLOAD = "DD_CAPTURE_LAMBDA_PAYLOAD"
|
|
78
|
-
DD_CAPTURE_LAMBDA_PAYLOAD_MAX_DEPTH = "DD_CAPTURE_LAMBDA_PAYLOAD_MAX_DEPTH"
|
|
79
61
|
DD_REQUESTS_SERVICE_NAME = "DD_REQUESTS_SERVICE_NAME"
|
|
80
62
|
DD_SERVICE = "DD_SERVICE"
|
|
81
|
-
DD_ENV = "DD_ENV"
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
def get_env_as_int(env_key, default_value: int) -> int:
|
|
85
|
-
try:
|
|
86
|
-
return int(os.environ.get(env_key, default_value))
|
|
87
|
-
except Exception as e:
|
|
88
|
-
logger.warn(
|
|
89
|
-
f"Failed to parse {env_key} as int. Using default value: {default_value}. Error: {e}"
|
|
90
|
-
)
|
|
91
|
-
return default_value
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
dd_capture_lambda_payload_enabled = (
|
|
95
|
-
os.environ.get(DD_CAPTURE_LAMBDA_PAYLOAD, "false").lower() == "true"
|
|
96
|
-
)
|
|
97
|
-
|
|
98
|
-
if dd_capture_lambda_payload_enabled:
|
|
99
|
-
import datadog_lambda.tag_object as tag_object
|
|
100
|
-
|
|
101
|
-
tag_object.max_depth = get_env_as_int(
|
|
102
|
-
DD_CAPTURE_LAMBDA_PAYLOAD_MAX_DEPTH, tag_object.max_depth
|
|
103
|
-
)
|
|
104
|
-
|
|
105
|
-
env_env_var = os.environ.get(DD_ENV, None)
|
|
106
63
|
|
|
107
64
|
init_timestamp_ns = time_ns()
|
|
108
65
|
|
|
@@ -159,56 +116,16 @@ class _LambdaDecorator(object):
|
|
|
159
116
|
"""Executes when the wrapped function gets wrapped"""
|
|
160
117
|
try:
|
|
161
118
|
self.func = func
|
|
162
|
-
self.flush_to_log = os.environ.get(DD_FLUSH_TO_LOG, "").lower() == "true"
|
|
163
|
-
self.logs_injection = (
|
|
164
|
-
os.environ.get(DD_LOGS_INJECTION, "true").lower() == "true"
|
|
165
|
-
)
|
|
166
|
-
self.merge_xray_traces = (
|
|
167
|
-
os.environ.get(DD_MERGE_XRAY_TRACES, "false").lower() == "true"
|
|
168
|
-
)
|
|
169
|
-
self.function_name = os.environ.get(AWS_LAMBDA_FUNCTION_NAME, "function")
|
|
170
|
-
self.service = os.environ.get(DD_SERVICE, None)
|
|
171
|
-
self.extractor_env = os.environ.get(DD_TRACE_EXTRACTOR, None)
|
|
172
119
|
self.trace_extractor = None
|
|
173
120
|
self.span = None
|
|
174
121
|
self.inferred_span = None
|
|
175
|
-
depends_on_dd_tracing_enabled = (
|
|
176
|
-
lambda original_boolean: dd_tracing_enabled and original_boolean
|
|
177
|
-
)
|
|
178
|
-
self.make_inferred_span = depends_on_dd_tracing_enabled(
|
|
179
|
-
os.environ.get(DD_TRACE_MANAGED_SERVICES, "true").lower() == "true"
|
|
180
|
-
)
|
|
181
|
-
self.encode_authorizer_context = depends_on_dd_tracing_enabled(
|
|
182
|
-
os.environ.get(DD_ENCODE_AUTHORIZER_CONTEXT, "true").lower() == "true"
|
|
183
|
-
)
|
|
184
|
-
self.decode_authorizer_context = depends_on_dd_tracing_enabled(
|
|
185
|
-
os.environ.get(DD_DECODE_AUTHORIZER_CONTEXT, "true").lower() == "true"
|
|
186
|
-
)
|
|
187
|
-
self.cold_start_tracing = depends_on_dd_tracing_enabled(
|
|
188
|
-
os.environ.get(DD_COLD_START_TRACING, "true").lower() == "true"
|
|
189
|
-
)
|
|
190
|
-
self.min_cold_start_trace_duration = get_env_as_int(
|
|
191
|
-
DD_MIN_COLD_START_DURATION, 3
|
|
192
|
-
)
|
|
193
|
-
self.local_testing_mode = os.environ.get(
|
|
194
|
-
DD_LOCAL_TEST, "false"
|
|
195
|
-
).lower() in ("true", "1")
|
|
196
|
-
self.cold_start_trace_skip_lib = [
|
|
197
|
-
"ddtrace.internal.compat",
|
|
198
|
-
"ddtrace.filters",
|
|
199
|
-
]
|
|
200
|
-
if DD_COLD_START_TRACE_SKIP_LIB in os.environ:
|
|
201
|
-
try:
|
|
202
|
-
self.cold_start_trace_skip_lib = os.environ[
|
|
203
|
-
DD_COLD_START_TRACE_SKIP_LIB
|
|
204
|
-
].split(",")
|
|
205
|
-
except Exception:
|
|
206
|
-
logger.debug(f"Malformatted for env {DD_COLD_START_TRACE_SKIP_LIB}")
|
|
207
122
|
self.response = None
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
123
|
+
|
|
124
|
+
if config.profiling_enabled:
|
|
125
|
+
self.prof = profiler.Profiler(env=config.env, service=config.service)
|
|
126
|
+
|
|
127
|
+
if config.trace_extractor:
|
|
128
|
+
extractor_parts = config.trace_extractor.rsplit(".", 1)
|
|
212
129
|
if len(extractor_parts) == 2:
|
|
213
130
|
(mod_name, extractor_name) = extractor_parts
|
|
214
131
|
modified_extractor_name = modify_module_name(mod_name)
|
|
@@ -216,7 +133,7 @@ class _LambdaDecorator(object):
|
|
|
216
133
|
self.trace_extractor = getattr(extractor_module, extractor_name)
|
|
217
134
|
|
|
218
135
|
# Inject trace correlation ids to logs
|
|
219
|
-
if
|
|
136
|
+
if config.logs_injection:
|
|
220
137
|
inject_correlation_ids()
|
|
221
138
|
|
|
222
139
|
# This prevents a breaking change in ddtrace v0.49 regarding the service name
|
|
@@ -224,15 +141,13 @@ class _LambdaDecorator(object):
|
|
|
224
141
|
os.environ[DD_REQUESTS_SERVICE_NAME] = os.environ.get(
|
|
225
142
|
DD_SERVICE, "aws.lambda"
|
|
226
143
|
)
|
|
227
|
-
# Patch third-party libraries for tracing
|
|
228
|
-
patch_all()
|
|
229
144
|
|
|
230
145
|
# Enable LLM Observability
|
|
231
|
-
if
|
|
146
|
+
if config.llmobs_enabled:
|
|
232
147
|
LLMObs.enable()
|
|
233
148
|
|
|
234
149
|
# Enable Exception Replay
|
|
235
|
-
if
|
|
150
|
+
if config.exception_replay_enabled:
|
|
236
151
|
logger.debug("Enabling exception replay")
|
|
237
152
|
SpanExceptionHandler.enable()
|
|
238
153
|
|
|
@@ -247,10 +162,9 @@ class _LambdaDecorator(object):
|
|
|
247
162
|
self.response = self.func(event, context, **kwargs)
|
|
248
163
|
return self.response
|
|
249
164
|
except Exception:
|
|
250
|
-
|
|
251
|
-
from datadog_lambda.metric import submit_errors_metric
|
|
165
|
+
from datadog_lambda.metric import submit_errors_metric
|
|
252
166
|
|
|
253
|
-
|
|
167
|
+
submit_errors_metric(context)
|
|
254
168
|
|
|
255
169
|
if self.span:
|
|
256
170
|
self.span.set_traceback()
|
|
@@ -302,7 +216,7 @@ class _LambdaDecorator(object):
|
|
|
302
216
|
event,
|
|
303
217
|
context,
|
|
304
218
|
extractor=self.trace_extractor,
|
|
305
|
-
decode_authorizer_context=
|
|
219
|
+
decode_authorizer_context=config.decode_authorizer_context,
|
|
306
220
|
)
|
|
307
221
|
self.event_source = event_source
|
|
308
222
|
# Create a Datadog X-Ray subsegment with the trace context
|
|
@@ -316,26 +230,32 @@ class _LambdaDecorator(object):
|
|
|
316
230
|
XraySubsegment.TRACE_KEY,
|
|
317
231
|
)
|
|
318
232
|
|
|
319
|
-
if
|
|
320
|
-
set_dd_trace_py_root(trace_context_source,
|
|
321
|
-
if
|
|
233
|
+
if config.trace_enabled:
|
|
234
|
+
set_dd_trace_py_root(trace_context_source, config.merge_xray_traces)
|
|
235
|
+
if config.make_inferred_span:
|
|
322
236
|
self.inferred_span = create_inferred_span(
|
|
323
|
-
event, context, event_source,
|
|
237
|
+
event, context, event_source, config.decode_authorizer_context
|
|
324
238
|
)
|
|
239
|
+
|
|
240
|
+
if config.appsec_enabled:
|
|
241
|
+
asm_set_context(event_source)
|
|
242
|
+
|
|
325
243
|
self.span = create_function_execution_span(
|
|
326
244
|
context=context,
|
|
327
|
-
function_name=
|
|
245
|
+
function_name=config.function_name,
|
|
328
246
|
is_cold_start=is_cold_start(),
|
|
329
247
|
is_proactive_init=is_proactive_init(),
|
|
330
248
|
trace_context_source=trace_context_source,
|
|
331
|
-
merge_xray_traces=
|
|
249
|
+
merge_xray_traces=config.merge_xray_traces,
|
|
332
250
|
trigger_tags=self.trigger_tags,
|
|
333
251
|
parent_span=self.inferred_span,
|
|
334
252
|
span_pointers=calculate_span_pointers(event_source, event),
|
|
335
253
|
)
|
|
254
|
+
if config.appsec_enabled:
|
|
255
|
+
asm_start_request(self.span, event, event_source, self.trigger_tags)
|
|
336
256
|
else:
|
|
337
257
|
set_correlation_ids()
|
|
338
|
-
if
|
|
258
|
+
if config.profiling_enabled and is_new_sandbox():
|
|
339
259
|
self.prof.start(stop_on_exit=False, profile_children=True)
|
|
340
260
|
logger.debug("datadog_lambda_wrapper _before() done")
|
|
341
261
|
except Exception as e:
|
|
@@ -354,25 +274,34 @@ class _LambdaDecorator(object):
|
|
|
354
274
|
create_dd_dummy_metadata_subsegment(
|
|
355
275
|
self.trigger_tags, XraySubsegment.LAMBDA_FUNCTION_TAGS_KEY
|
|
356
276
|
)
|
|
357
|
-
should_trace_cold_start =
|
|
277
|
+
should_trace_cold_start = config.cold_start_tracing and is_new_sandbox()
|
|
358
278
|
if should_trace_cold_start:
|
|
359
279
|
trace_ctx = tracer.current_trace_context()
|
|
360
280
|
|
|
361
281
|
if self.span:
|
|
362
|
-
if
|
|
363
|
-
tag_object
|
|
364
|
-
tag_object
|
|
282
|
+
if config.capture_payload_enabled:
|
|
283
|
+
tag_object(self.span, "function.request", event)
|
|
284
|
+
tag_object(self.span, "function.response", self.response)
|
|
365
285
|
|
|
366
286
|
if status_code:
|
|
367
287
|
self.span.set_tag("http.status_code", status_code)
|
|
288
|
+
|
|
289
|
+
if config.appsec_enabled:
|
|
290
|
+
asm_start_response(
|
|
291
|
+
self.span,
|
|
292
|
+
status_code,
|
|
293
|
+
self.event_source,
|
|
294
|
+
response=self.response,
|
|
295
|
+
)
|
|
296
|
+
|
|
368
297
|
self.span.finish()
|
|
369
298
|
|
|
370
299
|
if self.inferred_span:
|
|
371
300
|
if status_code:
|
|
372
301
|
self.inferred_span.set_tag("http.status_code", status_code)
|
|
373
302
|
|
|
374
|
-
if
|
|
375
|
-
self.inferred_span.set_tag("peer.service",
|
|
303
|
+
if config.service:
|
|
304
|
+
self.inferred_span.set_tag("peer.service", config.service)
|
|
376
305
|
|
|
377
306
|
if InferredSpanInfo.is_async(self.inferred_span) and self.span:
|
|
378
307
|
self.inferred_span.finish(finish_time=self.span.start)
|
|
@@ -384,33 +313,35 @@ class _LambdaDecorator(object):
|
|
|
384
313
|
following_span = self.span or self.inferred_span
|
|
385
314
|
ColdStartTracer(
|
|
386
315
|
tracer,
|
|
387
|
-
|
|
316
|
+
config.function_name,
|
|
388
317
|
following_span.start_ns,
|
|
389
318
|
trace_ctx,
|
|
390
|
-
|
|
391
|
-
|
|
319
|
+
config.min_cold_start_trace_duration,
|
|
320
|
+
config.cold_start_trace_skip_lib,
|
|
392
321
|
).trace()
|
|
393
322
|
except Exception as e:
|
|
394
323
|
logger.debug("Failed to create cold start spans. %s", e)
|
|
395
324
|
|
|
396
|
-
if not
|
|
325
|
+
if not config.flush_to_log or should_use_extension:
|
|
397
326
|
from datadog_lambda.metric import flush_stats
|
|
398
327
|
|
|
399
328
|
flush_stats(context)
|
|
400
|
-
if should_use_extension and
|
|
329
|
+
if should_use_extension and config.local_test:
|
|
401
330
|
# when testing locally, the extension does not know when an
|
|
402
331
|
# invocation completes because it does not have access to the
|
|
403
332
|
# logs api
|
|
404
333
|
flush_extension()
|
|
405
334
|
|
|
406
|
-
if
|
|
335
|
+
if config.llmobs_enabled:
|
|
407
336
|
LLMObs.flush()
|
|
408
337
|
|
|
409
338
|
# Flush exception replay
|
|
410
|
-
if
|
|
339
|
+
if config.exception_replay_enabled:
|
|
411
340
|
LogsIntakeUploaderV1._instance.periodic()
|
|
412
341
|
|
|
413
|
-
if
|
|
342
|
+
if config.encode_authorizer_context and is_authorizer_response(
|
|
343
|
+
self.response
|
|
344
|
+
):
|
|
414
345
|
self._inject_authorizer_span_headers(
|
|
415
346
|
event.get("requestContext", {}).get("requestId")
|
|
416
347
|
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: datadog_lambda
|
|
3
|
-
Version:
|
|
3
|
+
Version: 7.112.0
|
|
4
4
|
Summary: The Datadog AWS Lambda Library
|
|
5
5
|
Home-page: https://github.com/DataDog/datadog-lambda-python
|
|
6
6
|
License: Apache-2.0
|
|
@@ -19,7 +19,7 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
19
19
|
Provides-Extra: dev
|
|
20
20
|
Requires-Dist: botocore (>=1.34.0,<2.0.0) ; extra == "dev"
|
|
21
21
|
Requires-Dist: datadog (>=0.51.0,<1.0.0)
|
|
22
|
-
Requires-Dist: ddtrace (>=
|
|
22
|
+
Requires-Dist: ddtrace (>=3.10.2,<4)
|
|
23
23
|
Requires-Dist: flake8 (>=5.0.4,<6.0.0) ; extra == "dev"
|
|
24
24
|
Requires-Dist: pytest (>=8.0.0,<9.0.0) ; extra == "dev"
|
|
25
25
|
Requires-Dist: pytest-benchmark (>=4.0,<5.0) ; extra == "dev"
|