datadog_lambda 8.122.0__tar.gz → 8.124.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 (32) hide show
  1. {datadog_lambda-8.122.0 → datadog_lambda-8.124.0}/PKG-INFO +11 -6
  2. {datadog_lambda-8.122.0 → datadog_lambda-8.124.0}/datadog_lambda/cold_start.py +17 -0
  3. {datadog_lambda-8.122.0 → datadog_lambda-8.124.0}/datadog_lambda/config.py +12 -0
  4. datadog_lambda-8.124.0/datadog_lambda/durable.py +69 -0
  5. {datadog_lambda-8.122.0 → datadog_lambda-8.124.0}/datadog_lambda/tracing.py +10 -10
  6. datadog_lambda-8.124.0/datadog_lambda/version.py +1 -0
  7. {datadog_lambda-8.122.0 → datadog_lambda-8.124.0}/datadog_lambda/wrapper.py +29 -8
  8. {datadog_lambda-8.122.0 → datadog_lambda-8.124.0}/pyproject.toml +20 -7
  9. datadog_lambda-8.122.0/datadog_lambda/version.py +0 -1
  10. {datadog_lambda-8.122.0 → datadog_lambda-8.124.0}/LICENSE +0 -0
  11. {datadog_lambda-8.122.0 → datadog_lambda-8.124.0}/LICENSE-3rdparty.csv +0 -0
  12. {datadog_lambda-8.122.0 → datadog_lambda-8.124.0}/NOTICE +0 -0
  13. {datadog_lambda-8.122.0 → datadog_lambda-8.124.0}/README.md +0 -0
  14. {datadog_lambda-8.122.0 → datadog_lambda-8.124.0}/datadog_lambda/__init__.py +0 -0
  15. {datadog_lambda-8.122.0 → datadog_lambda-8.124.0}/datadog_lambda/api.py +0 -0
  16. {datadog_lambda-8.122.0 → datadog_lambda-8.124.0}/datadog_lambda/asm.py +0 -0
  17. {datadog_lambda-8.122.0 → datadog_lambda-8.124.0}/datadog_lambda/constants.py +0 -0
  18. {datadog_lambda-8.122.0 → datadog_lambda-8.124.0}/datadog_lambda/dogstatsd.py +0 -0
  19. {datadog_lambda-8.122.0 → datadog_lambda-8.124.0}/datadog_lambda/extension.py +0 -0
  20. {datadog_lambda-8.122.0 → datadog_lambda-8.124.0}/datadog_lambda/handler.py +0 -0
  21. {datadog_lambda-8.122.0 → datadog_lambda-8.124.0}/datadog_lambda/logger.py +0 -0
  22. {datadog_lambda-8.122.0 → datadog_lambda-8.124.0}/datadog_lambda/metric.py +0 -0
  23. {datadog_lambda-8.122.0 → datadog_lambda-8.124.0}/datadog_lambda/module_name.py +0 -0
  24. {datadog_lambda-8.122.0 → datadog_lambda-8.124.0}/datadog_lambda/patch.py +0 -0
  25. {datadog_lambda-8.122.0 → datadog_lambda-8.124.0}/datadog_lambda/span_pointers.py +0 -0
  26. {datadog_lambda-8.122.0 → datadog_lambda-8.124.0}/datadog_lambda/stats_writer.py +0 -0
  27. {datadog_lambda-8.122.0 → datadog_lambda-8.124.0}/datadog_lambda/statsd_writer.py +0 -0
  28. {datadog_lambda-8.122.0 → datadog_lambda-8.124.0}/datadog_lambda/tag_object.py +0 -0
  29. {datadog_lambda-8.122.0 → datadog_lambda-8.124.0}/datadog_lambda/tags.py +0 -0
  30. {datadog_lambda-8.122.0 → datadog_lambda-8.124.0}/datadog_lambda/thread_stats_writer.py +0 -0
  31. {datadog_lambda-8.122.0 → datadog_lambda-8.124.0}/datadog_lambda/trigger.py +0 -0
  32. {datadog_lambda-8.122.0 → datadog_lambda-8.124.0}/datadog_lambda/xray.py +0 -0
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: datadog_lambda
3
- Version: 8.122.0
3
+ Version: 8.124.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
7
7
  Keywords: datadog,aws,lambda,layer
8
8
  Author: Datadog, Inc.
9
9
  Author-email: dev@datadoghq.com
10
- Requires-Python: >=3.8.0,<4
10
+ Requires-Python: >=3.8.0,<3.15
11
11
  Classifier: License :: OSI Approved :: Apache Software License
12
12
  Classifier: Programming Language :: Python :: 3
13
13
  Classifier: Programming Language :: Python :: 3.8
@@ -21,12 +21,17 @@ Provides-Extra: dev
21
21
  Requires-Dist: botocore (>=1.34.0,<2.0.0) ; extra == "dev"
22
22
  Requires-Dist: datadog (>=0.51.0,<1.0.0)
23
23
  Requires-Dist: ddtrace (>=3.19.1,<4) ; python_version >= "3.8" and python_version < "3.10"
24
- Requires-Dist: ddtrace (>=4.1.1,<5) ; python_version >= "3.10"
24
+ Requires-Dist: ddtrace (>=4.1.1,<5,!=4.6.*) ; python_version >= "3.10"
25
25
  Requires-Dist: flake8 (>=5.0.4,<6.0.0) ; extra == "dev"
26
- Requires-Dist: pytest (>=8.0.0,<9.0.0) ; extra == "dev"
26
+ Requires-Dist: pytest (>=8.3.4,<9.0.0) ; (python_version >= "3.8" and python_version < "3.10") and (extra == "dev")
27
+ Requires-Dist: pytest (>=9.0.3,<10.0.0) ; (python_version >= "3.10") and (extra == "dev")
27
28
  Requires-Dist: pytest-benchmark (>=4.0,<5.0) ; extra == "dev"
28
- Requires-Dist: requests (>=2.22.0,<3.0.0) ; extra == "dev"
29
- Requires-Dist: ujson (>=5.9.0)
29
+ Requires-Dist: requests (>=2.22.0,<3.0.0) ; (python_version >= "3.8" and python_version < "3.10") and (extra == "dev")
30
+ Requires-Dist: requests (>=2.33.0,<3.0.0) ; (python_version >= "3.10") and (extra == "dev")
31
+ Requires-Dist: ujson (>=5.10.0,<5.12.0) ; python_version >= "3.8" and python_version < "3.10"
32
+ Requires-Dist: ujson (>=5.12.0,<6.0.0) ; python_version >= "3.10"
33
+ Requires-Dist: urllib3 (>=1.25.4,<1.27) ; python_version >= "3.8" and python_version < "3.10"
34
+ Requires-Dist: urllib3 (>=2.6.3,<3.0.0) ; python_version >= "3.10"
30
35
  Requires-Dist: wrapt (>=1.11.2,<2.0.0)
31
36
  Project-URL: Repository, https://github.com/DataDog/datadog-lambda-python
32
37
  Description-Content-Type: text/markdown
@@ -63,6 +63,19 @@ def get_proactive_init_tag():
63
63
  )
64
64
 
65
65
 
66
+ def is_managed_instances_mode():
67
+ """
68
+ Checks if the Lambda function is running in managed instances mode.
69
+ In managed instances mode, we should not create cold start tracing spans
70
+ as the gap between the sandbox initialization and the first
71
+ invocation might not be be a great experience.
72
+
73
+ Returns:
74
+ bool: True if running in managed instances mode, False otherwise
75
+ """
76
+ return config.aws_lambda_initialization_type == "lambda-managed-instances"
77
+
78
+
66
79
  class ImportNode(object):
67
80
  def __init__(self, module_name, full_file_path, start_time_ns, end_time_ns=None):
68
81
  self.module_name = module_name
@@ -145,6 +158,10 @@ def wrap_find_spec(original_find_spec):
145
158
 
146
159
 
147
160
  def initialize_cold_start_tracing():
161
+ # Skip cold start tracing initialization in managed instances mode
162
+ if is_managed_instances_mode():
163
+ return
164
+
148
165
  if is_new_sandbox() and config.cold_start_tracing:
149
166
  from sys import meta_path
150
167
 
@@ -98,6 +98,7 @@ class Config:
98
98
  integration_test = _get_env("DD_INTEGRATION_TEST", "false", as_bool)
99
99
 
100
100
  aws_lambda_function_name = _get_env("AWS_LAMBDA_FUNCTION_NAME")
101
+ aws_lambda_initialization_type = _get_env("AWS_LAMBDA_INITIALIZATION_TYPE")
101
102
 
102
103
  @property
103
104
  def function_name(self):
@@ -140,6 +141,17 @@ if config.is_gov_region or config.fips_mode_enabled:
140
141
  "enabled" if config.fips_mode_enabled else "not enabled",
141
142
  )
142
143
 
144
+ # Remote configuration relies on /dev/shm which is unavailable in Lambda.
145
+ # Disable it before ddtrace loads to avoid the shared-memory warning.
146
+ if "DD_REMOTE_CONFIGURATION_ENABLED" not in os.environ:
147
+ os.environ["DD_REMOTE_CONFIGURATION_ENABLED"] = "false"
148
+
149
+ # disable css to prevent double counting in lambda
150
+ os.environ["DD_TRACE_STATS_COMPUTATION_ENABLED"] = "false"
151
+
152
+ # unset css aliases to ensure it is disabled
153
+ if "DD_TRACE_COMPUTE_STATS" in os.environ:
154
+ del os.environ["DD_TRACE_COMPUTE_STATS"]
143
155
 
144
156
  if (
145
157
  "DD_INSTRUMENTATION_TELEMETRY_ENABLED" not in os.environ
@@ -0,0 +1,69 @@
1
+ # Unless explicitly stated otherwise all files in this repository are licensed
2
+ # under the Apache License Version 2.0.
3
+ # This product includes software developed at Datadog (https://www.datadoghq.com/).
4
+ # Copyright 2019 Datadog, Inc.
5
+ import logging
6
+ import re
7
+
8
+ logger = logging.getLogger(__name__)
9
+
10
+
11
+ def _parse_durable_execution_arn(arn):
12
+ """
13
+ Parses a DurableExecutionArn to extract execution name and ID.
14
+ ARN format:
15
+ arn:aws:lambda:{region}:{account}:function:{func}:{version}/durable-execution/{name}/{id}
16
+ Returns (execution_name, execution_id) or None if parsing fails.
17
+ """
18
+ match = re.search(r"/durable-execution/([^/]+)/([^/]+)$", arn)
19
+ if not match:
20
+ return None
21
+ execution_name, execution_id = match.group(1), match.group(2)
22
+ if not execution_name or not execution_id:
23
+ return None
24
+ return execution_name, execution_id
25
+
26
+
27
+ def extract_durable_function_tags(event):
28
+ """
29
+ Extracts durable function tags from the Lambda event payload.
30
+ Returns a dict with durable function tags, or an empty dict if the event
31
+ is not a durable function invocation.
32
+ """
33
+ if not isinstance(event, dict):
34
+ return {}
35
+
36
+ durable_execution_arn = event.get("DurableExecutionArn")
37
+ if not isinstance(durable_execution_arn, str):
38
+ return {}
39
+
40
+ parsed = _parse_durable_execution_arn(durable_execution_arn)
41
+ if not parsed:
42
+ logger.error("Failed to parse DurableExecutionArn: %s", durable_execution_arn)
43
+ return {}
44
+ execution_name, execution_id = parsed
45
+ # Use the number of operations to determine if it's the first invocation. This is
46
+ # what the durable execution SDK does to determine the replay status.
47
+ operations = event.get("InitialExecutionState", {}).get("Operations", [])
48
+ is_first_invocation = len(operations) == 1
49
+ return {
50
+ "aws_lambda.durable_function.execution_name": execution_name,
51
+ "aws_lambda.durable_function.execution_id": execution_id,
52
+ "aws_lambda.durable_function.first_invocation": str(
53
+ is_first_invocation
54
+ ).lower(),
55
+ }
56
+
57
+
58
+ VALID_DURABLE_STATUSES = {"SUCCEEDED", "FAILED", "PENDING"}
59
+
60
+
61
+ def extract_durable_execution_status(response, event):
62
+ if not isinstance(event, dict) or "DurableExecutionArn" not in event:
63
+ return None
64
+ if not isinstance(response, dict):
65
+ return None
66
+ status = response.get("Status")
67
+ if status not in VALID_DURABLE_STATUSES:
68
+ return None
69
+ return status
@@ -977,16 +977,12 @@ def process_injected_data(event, request_time_epoch_ms, args, tags):
977
977
  start_time_ns = int(
978
978
  injected_authorizer_data.get(Headers.Parent_Span_Finish_Time)
979
979
  )
980
- finish_time_ns = (
981
- request_time_epoch_ms
982
- + (
983
- int(
984
- event["requestContext"]["authorizer"].get(
985
- "integrationLatency", 0
986
- )
987
- )
988
- )
989
- ) * 1e6
980
+ integration_latency = int(
981
+ event["requestContext"]["authorizer"].get("integrationLatency", 0)
982
+ )
983
+ finish_time_ns = max(
984
+ start_time_ns, (request_time_epoch_ms + integration_latency) * 1e6
985
+ )
990
986
  upstream_authorizer_span = insert_upstream_authorizer_span(
991
987
  args, tags, start_time_ns, finish_time_ns
992
988
  )
@@ -1449,6 +1445,7 @@ def create_function_execution_span(
1449
1445
  trace_context_source,
1450
1446
  merge_xray_traces,
1451
1447
  trigger_tags,
1448
+ durable_function_tags=None,
1452
1449
  parent_span=None,
1453
1450
  span_pointers=None,
1454
1451
  ):
@@ -1459,6 +1456,7 @@ def create_function_execution_span(
1459
1456
  function_arn = ":".join(tk[0:7]) if len(tk) > 7 else function_arn
1460
1457
  function_version = tk[7] if len(tk) > 7 else "$LATEST"
1461
1458
  tags = {
1459
+ "span.kind": "server",
1462
1460
  "cold_start": str(is_cold_start).lower(),
1463
1461
  "function_arn": function_arn,
1464
1462
  "function_version": function_version,
@@ -1477,6 +1475,8 @@ def create_function_execution_span(
1477
1475
  if trace_context_source == TraceContextSource.XRAY and merge_xray_traces:
1478
1476
  tags["_dd.parent_source"] = trace_context_source
1479
1477
  tags.update(trigger_tags)
1478
+ if durable_function_tags:
1479
+ tags.update(durable_function_tags)
1480
1480
  tracer.set_tags(_dd_origin)
1481
1481
  # Determine service name based on config and env var
1482
1482
  if config.service:
@@ -0,0 +1 @@
1
+ __version__ = "8.124.0"
@@ -16,6 +16,7 @@ from datadog_lambda.cold_start import (
16
16
  is_cold_start,
17
17
  is_proactive_init,
18
18
  is_new_sandbox,
19
+ is_managed_instances_mode,
19
20
  ColdStartTracer,
20
21
  )
21
22
  from datadog_lambda.config import config
@@ -41,6 +42,10 @@ from datadog_lambda.tracing import (
41
42
  tracer,
42
43
  propagator,
43
44
  )
45
+ from datadog_lambda.durable import (
46
+ extract_durable_function_tags,
47
+ extract_durable_execution_status,
48
+ )
44
49
  from datadog_lambda.trigger import (
45
50
  extract_trigger_tags,
46
51
  extract_http_status_code_tag,
@@ -151,7 +156,7 @@ class _LambdaDecorator(object):
151
156
  if config.trace_extractor:
152
157
  extractor_parts = config.trace_extractor.rsplit(".", 1)
153
158
  if len(extractor_parts) == 2:
154
- (mod_name, extractor_name) = extractor_parts
159
+ mod_name, extractor_name = extractor_parts
155
160
  modified_extractor_name = modify_module_name(mod_name)
156
161
  extractor_module = import_module(modified_extractor_name)
157
162
  self.trace_extractor = getattr(extractor_module, extractor_name)
@@ -186,7 +191,6 @@ class _LambdaDecorator(object):
186
191
  if self.blocking_response:
187
192
  return self.blocking_response
188
193
  self.response = self.func(event, context, **kwargs)
189
- return self.response
190
194
  except BlockingException:
191
195
  self.blocking_response = get_asm_blocked_response(self.event_source)
192
196
  except Exception:
@@ -199,8 +203,9 @@ class _LambdaDecorator(object):
199
203
  raise
200
204
  finally:
201
205
  self._after(event, context)
202
- if self.blocking_response:
203
- return self.blocking_response
206
+ if self.blocking_response:
207
+ return self.blocking_response
208
+ return self.response
204
209
 
205
210
  def _inject_authorizer_span_headers(self, request_id):
206
211
  reference_span = self.inferred_span if self.inferred_span else self.span
@@ -242,6 +247,7 @@ class _LambdaDecorator(object):
242
247
  submit_invocations_metric(context)
243
248
 
244
249
  self.trigger_tags = extract_trigger_tags(event, context)
250
+ self.durable_function_tags = extract_durable_function_tags(event)
245
251
  # Extract Datadog trace context and source from incoming requests
246
252
  dd_context, trace_context_source, event_source = extract_dd_trace_context(
247
253
  event,
@@ -279,6 +285,7 @@ class _LambdaDecorator(object):
279
285
  trace_context_source=trace_context_source,
280
286
  merge_xray_traces=config.merge_xray_traces,
281
287
  trigger_tags=self.trigger_tags,
288
+ durable_function_tags=self.durable_function_tags,
282
289
  parent_span=self.inferred_span,
283
290
  span_pointers=calculate_span_pointers(event_source, event),
284
291
  )
@@ -288,7 +295,7 @@ class _LambdaDecorator(object):
288
295
  else:
289
296
  set_correlation_ids()
290
297
  if config.profiling_enabled and profiler and is_new_sandbox():
291
- self.prof.start(stop_on_exit=False, profile_children=True)
298
+ self.prof.start()
292
299
  logger.debug("datadog_lambda_wrapper _before() done")
293
300
  except Exception as e:
294
301
  logger.error(format_err_with_traceback(e))
@@ -301,6 +308,16 @@ class _LambdaDecorator(object):
301
308
 
302
309
  status_code = extract_http_status_code_tag(self.trigger_tags, self.response)
303
310
 
311
+ # Skip creating cold start spans in managed instances mode
312
+ # In managed instances, the tracer library handles cold start independently
313
+ should_trace_cold_start = (
314
+ config.cold_start_tracing
315
+ and is_new_sandbox()
316
+ and not is_managed_instances_mode()
317
+ )
318
+ if should_trace_cold_start:
319
+ trace_ctx = tracer.current_trace_context()
320
+
304
321
  if self.span:
305
322
  if config.appsec_enabled and not self.blocking_response:
306
323
  asm_start_response(
@@ -326,6 +343,13 @@ class _LambdaDecorator(object):
326
343
  if status_code:
327
344
  self.span.set_tag("http.status_code", status_code)
328
345
 
346
+ durable_status = extract_durable_execution_status(self.response, event)
347
+ if durable_status:
348
+ self.span.set_tag(
349
+ "aws_lambda.durable_function.execution_status",
350
+ durable_status,
351
+ )
352
+
329
353
  self.span.finish()
330
354
 
331
355
  if status_code:
@@ -338,9 +362,6 @@ class _LambdaDecorator(object):
338
362
  create_dd_dummy_metadata_subsegment(
339
363
  self.trigger_tags, XraySubsegment.LAMBDA_FUNCTION_TAGS_KEY
340
364
  )
341
- should_trace_cold_start = config.cold_start_tracing and is_new_sandbox()
342
- if should_trace_cold_start:
343
- trace_ctx = tracer.current_trace_context()
344
365
 
345
366
  if self.inferred_span:
346
367
  if status_code:
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "datadog_lambda"
3
- version = "8.122.0"
3
+ version = "8.124.0"
4
4
  description = "The Datadog AWS Lambda Library"
5
5
  authors = ["Datadog, Inc. <dev@datadoghq.com>"]
6
6
  license = "Apache-2.0"
@@ -26,17 +26,30 @@ classifiers = [
26
26
  ]
27
27
 
28
28
  [tool.poetry.dependencies]
29
- python = ">=3.8.0,<4"
29
+ python = ">=3.8.0,<3.15"
30
30
  datadog = ">=0.51.0,<1.0.0"
31
31
  wrapt = "^1.11.2"
32
32
  ddtrace = [
33
33
  {version = ">=3.19.1,<4", python = ">=3.8,<3.10"},
34
- {version = ">=4.1.1,<5", python = ">=3.10"}
34
+ {version = ">=4.1.1,<5,!=4.6.*", python = ">=3.10"}
35
+ ]
36
+ ujson = [
37
+ {version = ">=5.10.0,<5.12.0", python = ">=3.8,<3.10"},
38
+ {version = "^5.12.0", python = ">=3.10"},
39
+ ]
40
+ urllib3 = [
41
+ {version = ">=1.25.4,<1.27", python = ">=3.8,<3.10"},
42
+ {version = "^2.6.3", python = ">=3.10"},
35
43
  ]
36
- ujson = ">=5.9.0"
37
44
  botocore = { version = "^1.34.0", optional = true }
38
- requests = { version ="^2.22.0", optional = true }
39
- pytest = { version= "^8.0.0", optional = true }
45
+ requests = [
46
+ { version = "^2.22.0", optional = true, python = ">=3.8,<3.10" },
47
+ { version = "^2.33.0", optional = true, python = ">=3.10" },
48
+ ]
49
+ pytest = [
50
+ { version = "^8.3.4", optional = true, python = ">=3.8,<3.10" },
51
+ { version = "^9.0.3", optional = true, python = ">=3.10" },
52
+ ]
40
53
  pytest-benchmark = { version = "^4.0", optional = true }
41
54
  flake8 = { version = "^5.0.4", optional = true }
42
55
 
@@ -54,4 +67,4 @@ requires = ["poetry-core>=1.0.0"]
54
67
  build-backend = "poetry.core.masonry.api"
55
68
 
56
69
  [tool.pytest.ini_options]
57
- addopts = "--benchmark-disable --benchmark-autosave"
70
+ addopts = "--benchmark-disable --benchmark-autosave"
@@ -1 +0,0 @@
1
- __version__ = "8.122.0"