datadog_lambda 6.108.0__tar.gz → 6.110.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-6.108.0 → datadog_lambda-6.110.0}/PKG-INFO +2 -1
- {datadog_lambda-6.108.0 → datadog_lambda-6.110.0}/README.md +1 -0
- {datadog_lambda-6.108.0 → datadog_lambda-6.110.0}/datadog_lambda/dogstatsd.py +1 -1
- {datadog_lambda-6.108.0 → datadog_lambda-6.110.0}/datadog_lambda/metric.py +12 -0
- {datadog_lambda-6.108.0 → datadog_lambda-6.110.0}/datadog_lambda/trigger.py +33 -8
- datadog_lambda-6.110.0/datadog_lambda/version.py +1 -0
- {datadog_lambda-6.108.0 → datadog_lambda-6.110.0}/datadog_lambda/wrapper.py +16 -0
- {datadog_lambda-6.108.0 → datadog_lambda-6.110.0}/pyproject.toml +1 -1
- datadog_lambda-6.108.0/datadog_lambda/version.py +0 -1
- {datadog_lambda-6.108.0 → datadog_lambda-6.110.0}/LICENSE +0 -0
- {datadog_lambda-6.108.0 → datadog_lambda-6.110.0}/LICENSE-3rdparty.csv +0 -0
- {datadog_lambda-6.108.0 → datadog_lambda-6.110.0}/NOTICE +0 -0
- {datadog_lambda-6.108.0 → datadog_lambda-6.110.0}/datadog_lambda/__init__.py +0 -0
- {datadog_lambda-6.108.0 → datadog_lambda-6.110.0}/datadog_lambda/api.py +0 -0
- {datadog_lambda-6.108.0 → datadog_lambda-6.110.0}/datadog_lambda/cold_start.py +0 -0
- {datadog_lambda-6.108.0 → datadog_lambda-6.110.0}/datadog_lambda/constants.py +0 -0
- {datadog_lambda-6.108.0 → datadog_lambda-6.110.0}/datadog_lambda/extension.py +0 -0
- {datadog_lambda-6.108.0 → datadog_lambda-6.110.0}/datadog_lambda/fips.py +0 -0
- {datadog_lambda-6.108.0 → datadog_lambda-6.110.0}/datadog_lambda/handler.py +0 -0
- {datadog_lambda-6.108.0 → datadog_lambda-6.110.0}/datadog_lambda/logger.py +0 -0
- {datadog_lambda-6.108.0 → datadog_lambda-6.110.0}/datadog_lambda/module_name.py +0 -0
- {datadog_lambda-6.108.0 → datadog_lambda-6.110.0}/datadog_lambda/patch.py +0 -0
- {datadog_lambda-6.108.0 → datadog_lambda-6.110.0}/datadog_lambda/span_pointers.py +0 -0
- {datadog_lambda-6.108.0 → datadog_lambda-6.110.0}/datadog_lambda/stats_writer.py +0 -0
- {datadog_lambda-6.108.0 → datadog_lambda-6.110.0}/datadog_lambda/statsd_writer.py +0 -0
- {datadog_lambda-6.108.0 → datadog_lambda-6.110.0}/datadog_lambda/tag_object.py +0 -0
- {datadog_lambda-6.108.0 → datadog_lambda-6.110.0}/datadog_lambda/tags.py +0 -0
- {datadog_lambda-6.108.0 → datadog_lambda-6.110.0}/datadog_lambda/thread_stats_writer.py +0 -0
- {datadog_lambda-6.108.0 → datadog_lambda-6.110.0}/datadog_lambda/tracing.py +0 -0
- {datadog_lambda-6.108.0 → datadog_lambda-6.110.0}/datadog_lambda/xray.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: datadog_lambda
|
|
3
|
-
Version: 6.
|
|
3
|
+
Version: 6.110.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
|
|
@@ -60,6 +60,7 @@ Besides the environment variables supported by dd-trace-py, the datadog-lambda-p
|
|
|
60
60
|
| DD_COLD_START_TRACE_SKIP_LIB | optionally skip creating Cold Start Spans for a comma-separated list of libraries. Useful to limit depth or skip known libraries. | `ddtrace.internal.compat,ddtrace.filters` |
|
|
61
61
|
| DD_CAPTURE_LAMBDA_PAYLOAD | [Captures incoming and outgoing AWS Lambda payloads][1] in the Datadog APM spans for Lambda invocations. | `false` |
|
|
62
62
|
| DD_CAPTURE_LAMBDA_PAYLOAD_MAX_DEPTH | Determines the level of detail captured from AWS Lambda payloads, which are then assigned as tags for the `aws.lambda` span. It specifies the nesting depth of the JSON payload structure to process. Once the specified maximum depth is reached, the tag's value is set to the stringified value of any nested elements beyond this level. <br> For example, given the input payload: <pre>{<br> "lv1" : {<br> "lv2": {<br> "lv3": "val"<br> }<br> }<br>}</pre> If the depth is set to `2`, the resulting tag's key is set to `function.request.lv1.lv2` and the value is `{\"lv3\": \"val\"}`. <br> If the depth is set to `0`, the resulting tag's key is set to `function.request` and value is `{\"lv1\":{\"lv2\":{\"lv3\": \"val\"}}}` | `10` |
|
|
63
|
+
| DD_EXCEPTION_REPLAY_ENABLED | When set to `true`, the Lambda will run with Error Tracking Exception Replay enabled, capturing local variables. | `false` |
|
|
63
64
|
|
|
64
65
|
|
|
65
66
|
## Opening Issues
|
|
@@ -29,6 +29,7 @@ Besides the environment variables supported by dd-trace-py, the datadog-lambda-p
|
|
|
29
29
|
| DD_COLD_START_TRACE_SKIP_LIB | optionally skip creating Cold Start Spans for a comma-separated list of libraries. Useful to limit depth or skip known libraries. | `ddtrace.internal.compat,ddtrace.filters` |
|
|
30
30
|
| DD_CAPTURE_LAMBDA_PAYLOAD | [Captures incoming and outgoing AWS Lambda payloads][1] in the Datadog APM spans for Lambda invocations. | `false` |
|
|
31
31
|
| DD_CAPTURE_LAMBDA_PAYLOAD_MAX_DEPTH | Determines the level of detail captured from AWS Lambda payloads, which are then assigned as tags for the `aws.lambda` span. It specifies the nesting depth of the JSON payload structure to process. Once the specified maximum depth is reached, the tag's value is set to the stringified value of any nested elements beyond this level. <br> For example, given the input payload: <pre>{<br> "lv1" : {<br> "lv2": {<br> "lv3": "val"<br> }<br> }<br>}</pre> If the depth is set to `2`, the resulting tag's key is set to `function.request.lv1.lv2` and the value is `{\"lv3\": \"val\"}`. <br> If the depth is set to `0`, the resulting tag's key is set to `function.request` and value is `{\"lv1\":{\"lv2\":{\"lv3\": \"val\"}}}` | `10` |
|
|
32
|
+
| DD_EXCEPTION_REPLAY_ENABLED | When set to `true`, the Lambda will run with Error Tracking Exception Replay enabled, capturing local variables. | `false` |
|
|
32
33
|
|
|
33
34
|
|
|
34
35
|
## Opening Issues
|
|
@@ -97,7 +97,7 @@ class DogStatsd(object):
|
|
|
97
97
|
value,
|
|
98
98
|
metric_type,
|
|
99
99
|
("|#" + ",".join(self.normalize_tags(tags))) if tags else "",
|
|
100
|
-
("|T" + str(timestamp)) if timestamp is not None else "",
|
|
100
|
+
("|T" + str(int(timestamp))) if timestamp is not None else "",
|
|
101
101
|
)
|
|
102
102
|
|
|
103
103
|
def _report(self, metric, metric_type, value, tags, timestamp):
|
|
@@ -111,6 +111,18 @@ def lambda_metric(metric_name, value, timestamp=None, tags=None, force_async=Fal
|
|
|
111
111
|
if isinstance(timestamp, datetime):
|
|
112
112
|
timestamp = int(timestamp.timestamp())
|
|
113
113
|
|
|
114
|
+
else:
|
|
115
|
+
try:
|
|
116
|
+
timestamp = int(timestamp)
|
|
117
|
+
except Exception:
|
|
118
|
+
logger.debug(
|
|
119
|
+
"Ignoring metric submission for metric '%s' because the timestamp cannot "
|
|
120
|
+
"be turned into an integer: %r",
|
|
121
|
+
metric_name,
|
|
122
|
+
timestamp,
|
|
123
|
+
)
|
|
124
|
+
return
|
|
125
|
+
|
|
114
126
|
timestamp_floor = int((datetime.now() - timedelta(hours=4)).timestamp())
|
|
115
127
|
if timestamp < timestamp_floor:
|
|
116
128
|
logger.warning(
|
|
@@ -114,10 +114,14 @@ def parse_event_source(event: dict) -> _EventSource:
|
|
|
114
114
|
|
|
115
115
|
event_source = None
|
|
116
116
|
|
|
117
|
+
# Get requestContext safely and ensure it's a dictionary
|
|
117
118
|
request_context = event.get("requestContext")
|
|
119
|
+
if not isinstance(request_context, dict):
|
|
120
|
+
request_context = None
|
|
121
|
+
|
|
118
122
|
if request_context and request_context.get("stage"):
|
|
119
123
|
if "domainName" in request_context and detect_lambda_function_url_domain(
|
|
120
|
-
request_context.get("domainName")
|
|
124
|
+
request_context.get("domainName", "")
|
|
121
125
|
):
|
|
122
126
|
return _EventSource(EventTypes.LAMBDA_FUNCTION_URL)
|
|
123
127
|
event_source = _EventSource(EventTypes.API_GATEWAY)
|
|
@@ -171,6 +175,8 @@ def parse_event_source(event: dict) -> _EventSource:
|
|
|
171
175
|
|
|
172
176
|
def detect_lambda_function_url_domain(domain: str) -> bool:
|
|
173
177
|
# e.g. "etsn5fibjr.lambda-url.eu-south-1.amazonaws.com"
|
|
178
|
+
if not isinstance(domain, str):
|
|
179
|
+
return False
|
|
174
180
|
domain_parts = domain.split(".")
|
|
175
181
|
if len(domain_parts) < 2:
|
|
176
182
|
return False
|
|
@@ -283,17 +289,28 @@ def extract_http_tags(event):
|
|
|
283
289
|
Extracts HTTP facet tags from the triggering event
|
|
284
290
|
"""
|
|
285
291
|
http_tags = {}
|
|
292
|
+
|
|
293
|
+
# Safely get request_context and ensure it's a dictionary
|
|
286
294
|
request_context = event.get("requestContext")
|
|
295
|
+
if not isinstance(request_context, dict):
|
|
296
|
+
request_context = None
|
|
297
|
+
|
|
287
298
|
path = event.get("path")
|
|
288
299
|
method = event.get("httpMethod")
|
|
300
|
+
|
|
289
301
|
if request_context and request_context.get("stage"):
|
|
290
|
-
|
|
291
|
-
|
|
302
|
+
domain_name = request_context.get("domainName")
|
|
303
|
+
if domain_name:
|
|
304
|
+
http_tags["http.url"] = domain_name
|
|
292
305
|
|
|
293
306
|
path = request_context.get("path")
|
|
294
307
|
method = request_context.get("httpMethod")
|
|
308
|
+
|
|
295
309
|
# Version 2.0 HTTP API Gateway
|
|
296
|
-
apigateway_v2_http = request_context.get("http")
|
|
310
|
+
apigateway_v2_http = request_context.get("http", {})
|
|
311
|
+
if not isinstance(apigateway_v2_http, dict):
|
|
312
|
+
apigateway_v2_http = {}
|
|
313
|
+
|
|
297
314
|
if event.get("version") == "2.0" and apigateway_v2_http:
|
|
298
315
|
path = apigateway_v2_http.get("path")
|
|
299
316
|
method = apigateway_v2_http.get("method")
|
|
@@ -303,15 +320,23 @@ def extract_http_tags(event):
|
|
|
303
320
|
if method:
|
|
304
321
|
http_tags["http.method"] = method
|
|
305
322
|
|
|
306
|
-
|
|
323
|
+
# Safely get headers
|
|
324
|
+
headers = event.get("headers", {})
|
|
325
|
+
if not isinstance(headers, dict):
|
|
326
|
+
headers = {}
|
|
327
|
+
|
|
307
328
|
if headers and headers.get("Referer"):
|
|
308
329
|
http_tags["http.referer"] = headers.get("Referer")
|
|
309
330
|
|
|
310
331
|
# Try to get `routeKey` from API GW v2; otherwise try to get `resource` from API GW v1
|
|
311
332
|
route = event.get("routeKey") or event.get("resource")
|
|
312
|
-
if route:
|
|
313
|
-
|
|
314
|
-
|
|
333
|
+
if route and isinstance(route, str):
|
|
334
|
+
try:
|
|
335
|
+
# "GET /my/endpoint" = > "/my/endpoint"
|
|
336
|
+
http_tags["http.route"] = route.split(" ")[-1]
|
|
337
|
+
except Exception:
|
|
338
|
+
# If splitting fails, use the route as is
|
|
339
|
+
http_tags["http.route"] = route
|
|
315
340
|
|
|
316
341
|
return http_tags
|
|
317
342
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "6.110.0"
|
|
@@ -53,6 +53,13 @@ llmobs_env_var = os.environ.get("DD_LLMOBS_ENABLED", "false").lower() in ("true"
|
|
|
53
53
|
if llmobs_env_var:
|
|
54
54
|
from ddtrace.llmobs import LLMObs
|
|
55
55
|
|
|
56
|
+
exception_replay_env_var = os.environ.get(
|
|
57
|
+
"DD_EXCEPTION_REPLAY_ENABLED", "false"
|
|
58
|
+
).lower() in ("true", "1")
|
|
59
|
+
if exception_replay_env_var:
|
|
60
|
+
from ddtrace.debugging._exception.replay import SpanExceptionHandler
|
|
61
|
+
from ddtrace.debugging._uploader import LogsIntakeUploaderV1
|
|
62
|
+
|
|
56
63
|
logger = logging.getLogger(__name__)
|
|
57
64
|
|
|
58
65
|
DD_FLUSH_TO_LOG = "DD_FLUSH_TO_LOG"
|
|
@@ -224,6 +231,11 @@ class _LambdaDecorator(object):
|
|
|
224
231
|
if llmobs_env_var:
|
|
225
232
|
LLMObs.enable()
|
|
226
233
|
|
|
234
|
+
# Enable Exception Replay
|
|
235
|
+
if exception_replay_env_var:
|
|
236
|
+
logger.debug("Enabling exception replay")
|
|
237
|
+
SpanExceptionHandler.enable()
|
|
238
|
+
|
|
227
239
|
logger.debug("datadog_lambda_wrapper initialized")
|
|
228
240
|
except Exception as e:
|
|
229
241
|
logger.error(format_err_with_traceback(e))
|
|
@@ -394,6 +406,10 @@ class _LambdaDecorator(object):
|
|
|
394
406
|
if llmobs_env_var:
|
|
395
407
|
LLMObs.flush()
|
|
396
408
|
|
|
409
|
+
# Flush exception replay
|
|
410
|
+
if exception_replay_env_var:
|
|
411
|
+
LogsIntakeUploaderV1._instance.periodic()
|
|
412
|
+
|
|
397
413
|
if self.encode_authorizer_context and is_authorizer_response(self.response):
|
|
398
414
|
self._inject_authorizer_span_headers(
|
|
399
415
|
event.get("requestContext", {}).get("requestId")
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "6.108.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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|