datadog_lambda 5.93.0__tar.gz → 6.95.0__tar.gz
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-5.93.0 → datadog_lambda-6.95.0}/PKG-INFO +1 -1
- {datadog_lambda-5.93.0 → datadog_lambda-6.95.0}/datadog_lambda/api.py +2 -6
- {datadog_lambda-5.93.0 → datadog_lambda-6.95.0}/datadog_lambda/cold_start.py +2 -1
- {datadog_lambda-5.93.0 → datadog_lambda-6.95.0}/datadog_lambda/handler.py +17 -2
- {datadog_lambda-5.93.0 → datadog_lambda-6.95.0}/datadog_lambda/metric.py +26 -6
- {datadog_lambda-5.93.0 → datadog_lambda-6.95.0}/datadog_lambda/tags.py +4 -2
- {datadog_lambda-5.93.0 → datadog_lambda-6.95.0}/datadog_lambda/tracing.py +66 -16
- datadog_lambda-6.95.0/datadog_lambda/version.py +1 -0
- {datadog_lambda-5.93.0 → datadog_lambda-6.95.0}/pyproject.toml +1 -1
- datadog_lambda-5.93.0/datadog_lambda/version.py +0 -1
- {datadog_lambda-5.93.0 → datadog_lambda-6.95.0}/LICENSE +0 -0
- {datadog_lambda-5.93.0 → datadog_lambda-6.95.0}/LICENSE-3rdparty.csv +0 -0
- {datadog_lambda-5.93.0 → datadog_lambda-6.95.0}/NOTICE +0 -0
- {datadog_lambda-5.93.0 → datadog_lambda-6.95.0}/README.md +0 -0
- {datadog_lambda-5.93.0 → datadog_lambda-6.95.0}/datadog_lambda/__init__.py +0 -0
- {datadog_lambda-5.93.0 → datadog_lambda-6.95.0}/datadog_lambda/constants.py +0 -0
- {datadog_lambda-5.93.0 → datadog_lambda-6.95.0}/datadog_lambda/dogstatsd.py +0 -0
- {datadog_lambda-5.93.0 → datadog_lambda-6.95.0}/datadog_lambda/extension.py +0 -0
- {datadog_lambda-5.93.0 → datadog_lambda-6.95.0}/datadog_lambda/logger.py +0 -0
- {datadog_lambda-5.93.0 → datadog_lambda-6.95.0}/datadog_lambda/module_name.py +0 -0
- {datadog_lambda-5.93.0 → datadog_lambda-6.95.0}/datadog_lambda/patch.py +0 -0
- {datadog_lambda-5.93.0 → datadog_lambda-6.95.0}/datadog_lambda/stats_writer.py +0 -0
- {datadog_lambda-5.93.0 → datadog_lambda-6.95.0}/datadog_lambda/statsd_writer.py +0 -0
- {datadog_lambda-5.93.0 → datadog_lambda-6.95.0}/datadog_lambda/tag_object.py +0 -0
- {datadog_lambda-5.93.0 → datadog_lambda-6.95.0}/datadog_lambda/thread_stats_writer.py +0 -0
- {datadog_lambda-5.93.0 → datadog_lambda-6.95.0}/datadog_lambda/trigger.py +0 -0
- {datadog_lambda-5.93.0 → datadog_lambda-6.95.0}/datadog_lambda/wrapper.py +0 -0
- {datadog_lambda-5.93.0 → datadog_lambda-6.95.0}/datadog_lambda/xray.py +0 -0
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import logging
|
|
3
3
|
import base64
|
|
4
|
-
from datadog_lambda.extension import should_use_extension
|
|
5
4
|
|
|
6
5
|
logger = logging.getLogger(__name__)
|
|
7
6
|
KMS_ENCRYPTION_CONTEXT_KEY = "LambdaFunctionName"
|
|
@@ -48,13 +47,10 @@ def decrypt_kms_api_key(kms_client, ciphertext):
|
|
|
48
47
|
|
|
49
48
|
|
|
50
49
|
def init_api():
|
|
51
|
-
if (
|
|
52
|
-
not should_use_extension
|
|
53
|
-
and not os.environ.get("DD_FLUSH_TO_LOG", "").lower() == "true"
|
|
54
|
-
):
|
|
50
|
+
if not os.environ.get("DD_FLUSH_TO_LOG", "").lower() == "true":
|
|
55
51
|
# Make sure that this package would always be lazy-loaded/outside from the critical path
|
|
56
52
|
# since underlying packages are quite heavy to load
|
|
57
|
-
# and useless
|
|
53
|
+
# and useless with the extension unless sending metrics with timestamps
|
|
58
54
|
from datadog import api
|
|
59
55
|
|
|
60
56
|
if not api._api_key:
|
|
@@ -197,7 +197,8 @@ class ColdStartTracer(object):
|
|
|
197
197
|
|
|
198
198
|
def trace_tree(self, import_node: ImportNode, parent_span):
|
|
199
199
|
if (
|
|
200
|
-
|
|
200
|
+
not self.trace_ctx
|
|
201
|
+
or import_node.end_time_ns - import_node.start_time_ns
|
|
201
202
|
< self.min_duration_ms * 1e6
|
|
202
203
|
or import_node.module_name in self.ignored_libs
|
|
203
204
|
):
|
|
@@ -7,6 +7,9 @@ from __future__ import absolute_import
|
|
|
7
7
|
from importlib import import_module
|
|
8
8
|
|
|
9
9
|
import os
|
|
10
|
+
from time import time_ns
|
|
11
|
+
|
|
12
|
+
from datadog_lambda.tracing import emit_telemetry_on_exception_outside_of_handler
|
|
10
13
|
from datadog_lambda.wrapper import datadog_lambda_wrapper
|
|
11
14
|
from datadog_lambda.module_name import modify_module_name
|
|
12
15
|
|
|
@@ -27,5 +30,17 @@ if len(parts) != 2:
|
|
|
27
30
|
|
|
28
31
|
(mod_name, handler_name) = parts
|
|
29
32
|
modified_mod_name = modify_module_name(mod_name)
|
|
30
|
-
|
|
31
|
-
|
|
33
|
+
|
|
34
|
+
try:
|
|
35
|
+
handler_load_start_time_ns = time_ns()
|
|
36
|
+
handler_module = import_module(modified_mod_name)
|
|
37
|
+
handler_func = getattr(handler_module, handler_name)
|
|
38
|
+
except Exception as e:
|
|
39
|
+
emit_telemetry_on_exception_outside_of_handler(
|
|
40
|
+
e,
|
|
41
|
+
modified_mod_name,
|
|
42
|
+
handler_load_start_time_ns,
|
|
43
|
+
)
|
|
44
|
+
raise
|
|
45
|
+
|
|
46
|
+
handler = datadog_lambda_wrapper(handler_func)
|
|
@@ -10,13 +10,13 @@ import ujson as json
|
|
|
10
10
|
|
|
11
11
|
from datadog_lambda.extension import should_use_extension
|
|
12
12
|
from datadog_lambda.tags import get_enhanced_metrics_tags, dd_lambda_layer_tag
|
|
13
|
-
from datadog_lambda.api import init_api
|
|
14
13
|
|
|
15
14
|
logger = logging.getLogger(__name__)
|
|
16
15
|
|
|
17
16
|
lambda_stats = None
|
|
17
|
+
extension_thread_stats = None
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
flush_in_thread = os.environ.get("DD_FLUSH_IN_THREAD", "").lower() == "true"
|
|
20
20
|
|
|
21
21
|
if should_use_extension:
|
|
22
22
|
from datadog_lambda.statsd_writer import StatsDWriter
|
|
@@ -28,8 +28,9 @@ else:
|
|
|
28
28
|
# end of invocation. To make metrics submitted from a long-running Lambda
|
|
29
29
|
# function available sooner, consider using the Datadog Lambda extension.
|
|
30
30
|
from datadog_lambda.thread_stats_writer import ThreadStatsWriter
|
|
31
|
+
from datadog_lambda.api import init_api
|
|
31
32
|
|
|
32
|
-
|
|
33
|
+
init_api()
|
|
33
34
|
lambda_stats = ThreadStatsWriter(flush_in_thread)
|
|
34
35
|
|
|
35
36
|
enhanced_metrics_enabled = (
|
|
@@ -57,6 +58,22 @@ def lambda_metric(metric_name, value, timestamp=None, tags=None, force_async=Fal
|
|
|
57
58
|
tags = [] if tags is None else list(tags)
|
|
58
59
|
tags.append(dd_lambda_layer_tag)
|
|
59
60
|
|
|
61
|
+
if should_use_extension and timestamp is not None:
|
|
62
|
+
# The extension does not support timestamps for distributions so we create a
|
|
63
|
+
# a thread stats writer to submit metrics with timestamps to the API
|
|
64
|
+
global extension_thread_stats
|
|
65
|
+
if extension_thread_stats is None:
|
|
66
|
+
from datadog_lambda.thread_stats_writer import ThreadStatsWriter
|
|
67
|
+
from datadog_lambda.api import init_api
|
|
68
|
+
|
|
69
|
+
init_api()
|
|
70
|
+
extension_thread_stats = ThreadStatsWriter(flush_in_thread)
|
|
71
|
+
|
|
72
|
+
extension_thread_stats.distribution(
|
|
73
|
+
metric_name, value, tags=tags, timestamp=timestamp
|
|
74
|
+
)
|
|
75
|
+
return
|
|
76
|
+
|
|
60
77
|
if should_use_extension:
|
|
61
78
|
logger.debug(
|
|
62
79
|
"Sending metric %s value %s to Datadog via extension", metric_name, value
|
|
@@ -94,13 +111,16 @@ def write_metric_point_to_stdout(metric_name, value, timestamp=None, tags=[]):
|
|
|
94
111
|
def flush_stats():
|
|
95
112
|
lambda_stats.flush()
|
|
96
113
|
|
|
114
|
+
if extension_thread_stats is not None:
|
|
115
|
+
extension_thread_stats.flush()
|
|
116
|
+
|
|
97
117
|
|
|
98
118
|
def submit_enhanced_metric(metric_name, lambda_context):
|
|
99
119
|
"""Submits the enhanced metric with the given name
|
|
100
120
|
|
|
101
121
|
Args:
|
|
102
122
|
metric_name (str): metric name w/o enhanced prefix i.e. "invocations" or "errors"
|
|
103
|
-
lambda_context (
|
|
123
|
+
lambda_context (object): Lambda context dict passed to the function by AWS
|
|
104
124
|
"""
|
|
105
125
|
if not enhanced_metrics_enabled:
|
|
106
126
|
logger.debug(
|
|
@@ -118,7 +138,7 @@ def submit_invocations_metric(lambda_context):
|
|
|
118
138
|
"""Increment aws.lambda.enhanced.invocations by 1, applying runtime, layer, and cold_start tags
|
|
119
139
|
|
|
120
140
|
Args:
|
|
121
|
-
lambda_context (
|
|
141
|
+
lambda_context (object): Lambda context dict passed to the function by AWS
|
|
122
142
|
"""
|
|
123
143
|
submit_enhanced_metric("invocations", lambda_context)
|
|
124
144
|
|
|
@@ -127,6 +147,6 @@ def submit_errors_metric(lambda_context):
|
|
|
127
147
|
"""Increment aws.lambda.enhanced.errors by 1, applying runtime, layer, and cold_start tags
|
|
128
148
|
|
|
129
149
|
Args:
|
|
130
|
-
lambda_context (
|
|
150
|
+
lambda_context (object): Lambda context dict passed to the function by AWS
|
|
131
151
|
"""
|
|
132
152
|
submit_enhanced_metric("errors", lambda_context)
|
|
@@ -55,9 +55,11 @@ def parse_lambda_tags_from_arn(lambda_context):
|
|
|
55
55
|
|
|
56
56
|
def get_enhanced_metrics_tags(lambda_context):
|
|
57
57
|
"""Get the list of tags to apply to enhanced metrics"""
|
|
58
|
-
tags =
|
|
58
|
+
tags = []
|
|
59
|
+
if lambda_context:
|
|
60
|
+
tags = parse_lambda_tags_from_arn(lambda_context)
|
|
61
|
+
tags.append(f"memorysize:{lambda_context.memory_limit_in_mb}")
|
|
59
62
|
tags.append(get_cold_start_tag())
|
|
60
|
-
tags.append(f"memorysize:{lambda_context.memory_limit_in_mb}")
|
|
61
63
|
tags.append(runtime_tag)
|
|
62
64
|
tags.append(library_version_tag)
|
|
63
65
|
return tags
|
|
@@ -6,6 +6,7 @@ import hashlib
|
|
|
6
6
|
import logging
|
|
7
7
|
import os
|
|
8
8
|
import base64
|
|
9
|
+
import traceback
|
|
9
10
|
import ujson as json
|
|
10
11
|
from datetime import datetime, timezone
|
|
11
12
|
from typing import Optional, Dict
|
|
@@ -71,6 +72,8 @@ is_lambda_context = os.environ.get(XrayDaemon.FUNCTION_NAME_HEADER_NAME) != ""
|
|
|
71
72
|
propagator = HTTPPropagator()
|
|
72
73
|
|
|
73
74
|
DD_TRACE_JAVA_TRACE_ID_PADDING = "00000000"
|
|
75
|
+
HIGHER_64_BITS = "HIGHER_64_BITS"
|
|
76
|
+
LOWER_64_BITS = "LOWER_64_BITS"
|
|
74
77
|
|
|
75
78
|
|
|
76
79
|
def _convert_xray_trace_id(xray_trace_id):
|
|
@@ -272,7 +275,7 @@ def extract_context_from_sqs_or_sns_event_or_context(event, lambda_context):
|
|
|
272
275
|
else:
|
|
273
276
|
# Handle case where trace context is injected into attributes.AWSTraceHeader
|
|
274
277
|
# example: Root=1-654321ab-000000001234567890abcdef;Parent=0123456789abcdef;Sampled=1
|
|
275
|
-
attrs =
|
|
278
|
+
attrs = event.get("Records")[0].get("attributes")
|
|
276
279
|
if attrs:
|
|
277
280
|
x_ray_header = attrs.get("AWSTraceHeader")
|
|
278
281
|
if x_ray_header:
|
|
@@ -353,14 +356,16 @@ def extract_context_from_kinesis_event(event, lambda_context):
|
|
|
353
356
|
return extract_context_from_lambda_context(lambda_context)
|
|
354
357
|
|
|
355
358
|
|
|
356
|
-
def
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
359
|
+
def _deterministic_sha256_hash(s: str, part: str) -> (int, int):
|
|
360
|
+
sha256_hash = hashlib.sha256(s.encode()).hexdigest()
|
|
361
|
+
|
|
362
|
+
# First two chars is '0b'. zfill to ensure 256 bits, but we only care about the first 128 bits
|
|
363
|
+
binary_hash = bin(int(sha256_hash, 16))[2:].zfill(256)
|
|
364
|
+
if part == HIGHER_64_BITS:
|
|
365
|
+
updated_binary_hash = "0" + binary_hash[1:64]
|
|
366
|
+
else:
|
|
367
|
+
updated_binary_hash = "0" + binary_hash[65:128]
|
|
368
|
+
result = int(updated_binary_hash, 2)
|
|
364
369
|
if result == 0:
|
|
365
370
|
return 1
|
|
366
371
|
return result
|
|
@@ -375,13 +380,27 @@ def extract_context_from_step_functions(event, lambda_context):
|
|
|
375
380
|
execution_id = event.get("Execution").get("Id")
|
|
376
381
|
state_name = event.get("State").get("Name")
|
|
377
382
|
state_entered_time = event.get("State").get("EnteredTime")
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
383
|
+
# returning 128 bits since 128bit traceId will be break up into
|
|
384
|
+
# traditional traceId and _dd.p.tid tag
|
|
385
|
+
# https://github.com/DataDog/dd-trace-py/blob/3e34d21cb9b5e1916e549047158cb119317b96ab/ddtrace/propagation/http.py#L232-L240
|
|
386
|
+
trace_id = _deterministic_sha256_hash(execution_id, LOWER_64_BITS)
|
|
387
|
+
|
|
388
|
+
parent_id = _deterministic_sha256_hash(
|
|
389
|
+
f"{execution_id}#{state_name}#{state_entered_time}", HIGHER_64_BITS
|
|
381
390
|
)
|
|
391
|
+
|
|
382
392
|
sampling_priority = SamplingPriority.AUTO_KEEP
|
|
383
393
|
return Context(
|
|
384
|
-
trace_id=trace_id,
|
|
394
|
+
trace_id=trace_id,
|
|
395
|
+
span_id=parent_id,
|
|
396
|
+
sampling_priority=sampling_priority,
|
|
397
|
+
# take the higher 64 bits as _dd.p.tid tag and use hex to encode
|
|
398
|
+
# [2:] to remove '0x' in the hex str
|
|
399
|
+
meta={
|
|
400
|
+
"_dd.p.tid": hex(
|
|
401
|
+
_deterministic_sha256_hash(execution_id, HIGHER_64_BITS)
|
|
402
|
+
)[2:]
|
|
403
|
+
},
|
|
385
404
|
)
|
|
386
405
|
except Exception as e:
|
|
387
406
|
logger.debug("The Step Functions trace extractor returned with error %s", e)
|
|
@@ -1245,9 +1264,9 @@ def create_function_execution_span(
|
|
|
1245
1264
|
"function_version": function_version,
|
|
1246
1265
|
"request_id": context.aws_request_id,
|
|
1247
1266
|
"resource_names": context.function_name,
|
|
1248
|
-
"functionname":
|
|
1249
|
-
|
|
1250
|
-
|
|
1267
|
+
"functionname": (
|
|
1268
|
+
context.function_name.lower() if context.function_name else None
|
|
1269
|
+
),
|
|
1251
1270
|
"datadog_lambda": datadog_lambda_version,
|
|
1252
1271
|
"dd_trace": ddtrace_version,
|
|
1253
1272
|
"span.name": "aws.lambda",
|
|
@@ -1320,3 +1339,34 @@ class InferredSpanInfo(object):
|
|
|
1320
1339
|
e,
|
|
1321
1340
|
)
|
|
1322
1341
|
return False
|
|
1342
|
+
|
|
1343
|
+
|
|
1344
|
+
def emit_telemetry_on_exception_outside_of_handler(
|
|
1345
|
+
exception, resource_name, handler_load_start_time_ns
|
|
1346
|
+
):
|
|
1347
|
+
"""
|
|
1348
|
+
Emit an enhanced error metric and create a span for exceptions occurring outside the handler
|
|
1349
|
+
"""
|
|
1350
|
+
submit_errors_metric(None)
|
|
1351
|
+
if dd_tracing_enabled:
|
|
1352
|
+
span = tracer.trace(
|
|
1353
|
+
"aws.lambda",
|
|
1354
|
+
service="aws.lambda",
|
|
1355
|
+
resource=resource_name,
|
|
1356
|
+
span_type="serverless",
|
|
1357
|
+
)
|
|
1358
|
+
span.start_ns = handler_load_start_time_ns
|
|
1359
|
+
|
|
1360
|
+
tags = {
|
|
1361
|
+
"error.status": 500,
|
|
1362
|
+
"error.type": type(exception).__name__,
|
|
1363
|
+
"error.message": exception,
|
|
1364
|
+
"error.stack": traceback.format_exc(),
|
|
1365
|
+
"resource_names": resource_name,
|
|
1366
|
+
"resource.name": resource_name,
|
|
1367
|
+
"operation_name": "aws.lambda",
|
|
1368
|
+
"status": "error",
|
|
1369
|
+
}
|
|
1370
|
+
span.set_tags(tags)
|
|
1371
|
+
span.error = 1
|
|
1372
|
+
span.finish()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "6.95.0"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "5.93.0"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|