datadog_lambda 6.109.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.
Files changed (30) hide show
  1. {datadog_lambda-6.109.0 → datadog_lambda-6.110.0}/PKG-INFO +2 -1
  2. {datadog_lambda-6.109.0 → datadog_lambda-6.110.0}/README.md +1 -0
  3. {datadog_lambda-6.109.0 → datadog_lambda-6.110.0}/datadog_lambda/trigger.py +33 -8
  4. datadog_lambda-6.110.0/datadog_lambda/version.py +1 -0
  5. {datadog_lambda-6.109.0 → datadog_lambda-6.110.0}/datadog_lambda/wrapper.py +16 -0
  6. {datadog_lambda-6.109.0 → datadog_lambda-6.110.0}/pyproject.toml +1 -1
  7. datadog_lambda-6.109.0/datadog_lambda/version.py +0 -1
  8. {datadog_lambda-6.109.0 → datadog_lambda-6.110.0}/LICENSE +0 -0
  9. {datadog_lambda-6.109.0 → datadog_lambda-6.110.0}/LICENSE-3rdparty.csv +0 -0
  10. {datadog_lambda-6.109.0 → datadog_lambda-6.110.0}/NOTICE +0 -0
  11. {datadog_lambda-6.109.0 → datadog_lambda-6.110.0}/datadog_lambda/__init__.py +0 -0
  12. {datadog_lambda-6.109.0 → datadog_lambda-6.110.0}/datadog_lambda/api.py +0 -0
  13. {datadog_lambda-6.109.0 → datadog_lambda-6.110.0}/datadog_lambda/cold_start.py +0 -0
  14. {datadog_lambda-6.109.0 → datadog_lambda-6.110.0}/datadog_lambda/constants.py +0 -0
  15. {datadog_lambda-6.109.0 → datadog_lambda-6.110.0}/datadog_lambda/dogstatsd.py +0 -0
  16. {datadog_lambda-6.109.0 → datadog_lambda-6.110.0}/datadog_lambda/extension.py +0 -0
  17. {datadog_lambda-6.109.0 → datadog_lambda-6.110.0}/datadog_lambda/fips.py +0 -0
  18. {datadog_lambda-6.109.0 → datadog_lambda-6.110.0}/datadog_lambda/handler.py +0 -0
  19. {datadog_lambda-6.109.0 → datadog_lambda-6.110.0}/datadog_lambda/logger.py +0 -0
  20. {datadog_lambda-6.109.0 → datadog_lambda-6.110.0}/datadog_lambda/metric.py +0 -0
  21. {datadog_lambda-6.109.0 → datadog_lambda-6.110.0}/datadog_lambda/module_name.py +0 -0
  22. {datadog_lambda-6.109.0 → datadog_lambda-6.110.0}/datadog_lambda/patch.py +0 -0
  23. {datadog_lambda-6.109.0 → datadog_lambda-6.110.0}/datadog_lambda/span_pointers.py +0 -0
  24. {datadog_lambda-6.109.0 → datadog_lambda-6.110.0}/datadog_lambda/stats_writer.py +0 -0
  25. {datadog_lambda-6.109.0 → datadog_lambda-6.110.0}/datadog_lambda/statsd_writer.py +0 -0
  26. {datadog_lambda-6.109.0 → datadog_lambda-6.110.0}/datadog_lambda/tag_object.py +0 -0
  27. {datadog_lambda-6.109.0 → datadog_lambda-6.110.0}/datadog_lambda/tags.py +0 -0
  28. {datadog_lambda-6.109.0 → datadog_lambda-6.110.0}/datadog_lambda/thread_stats_writer.py +0 -0
  29. {datadog_lambda-6.109.0 → datadog_lambda-6.110.0}/datadog_lambda/tracing.py +0 -0
  30. {datadog_lambda-6.109.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.109.0
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
@@ -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
- if request_context.get("domainName"):
291
- http_tags["http.url"] = request_context.get("domainName")
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
- headers = event.get("headers")
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
- # "GET /my/endpoint" = > "/my/endpoint"
314
- http_tags["http.route"] = route.split(" ")[-1]
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,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "datadog_lambda"
3
- version = "6.109.0"
3
+ version = "6.110.0"
4
4
  description = "The Datadog AWS Lambda Library"
5
5
  authors = ["Datadog, Inc. <dev@datadoghq.com>"]
6
6
  license = "Apache-2.0"
@@ -1 +0,0 @@
1
- __version__ = "6.109.0"