datadog_lambda 5.94.0__tar.gz → 6.96.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 (28) hide show
  1. {datadog_lambda-5.94.0 → datadog_lambda-6.96.0}/PKG-INFO +5 -2
  2. {datadog_lambda-5.94.0 → datadog_lambda-6.96.0}/README.md +3 -0
  3. {datadog_lambda-5.94.0 → datadog_lambda-6.96.0}/datadog_lambda/api.py +2 -6
  4. {datadog_lambda-5.94.0 → datadog_lambda-6.96.0}/datadog_lambda/cold_start.py +2 -1
  5. {datadog_lambda-5.94.0 → datadog_lambda-6.96.0}/datadog_lambda/metric.py +23 -3
  6. {datadog_lambda-5.94.0 → datadog_lambda-6.96.0}/datadog_lambda/tracing.py +34 -16
  7. datadog_lambda-6.96.0/datadog_lambda/version.py +1 -0
  8. {datadog_lambda-5.94.0 → datadog_lambda-6.96.0}/pyproject.toml +2 -2
  9. datadog_lambda-5.94.0/datadog_lambda/version.py +0 -1
  10. {datadog_lambda-5.94.0 → datadog_lambda-6.96.0}/LICENSE +0 -0
  11. {datadog_lambda-5.94.0 → datadog_lambda-6.96.0}/LICENSE-3rdparty.csv +0 -0
  12. {datadog_lambda-5.94.0 → datadog_lambda-6.96.0}/NOTICE +0 -0
  13. {datadog_lambda-5.94.0 → datadog_lambda-6.96.0}/datadog_lambda/__init__.py +0 -0
  14. {datadog_lambda-5.94.0 → datadog_lambda-6.96.0}/datadog_lambda/constants.py +0 -0
  15. {datadog_lambda-5.94.0 → datadog_lambda-6.96.0}/datadog_lambda/dogstatsd.py +0 -0
  16. {datadog_lambda-5.94.0 → datadog_lambda-6.96.0}/datadog_lambda/extension.py +0 -0
  17. {datadog_lambda-5.94.0 → datadog_lambda-6.96.0}/datadog_lambda/handler.py +0 -0
  18. {datadog_lambda-5.94.0 → datadog_lambda-6.96.0}/datadog_lambda/logger.py +0 -0
  19. {datadog_lambda-5.94.0 → datadog_lambda-6.96.0}/datadog_lambda/module_name.py +0 -0
  20. {datadog_lambda-5.94.0 → datadog_lambda-6.96.0}/datadog_lambda/patch.py +0 -0
  21. {datadog_lambda-5.94.0 → datadog_lambda-6.96.0}/datadog_lambda/stats_writer.py +0 -0
  22. {datadog_lambda-5.94.0 → datadog_lambda-6.96.0}/datadog_lambda/statsd_writer.py +0 -0
  23. {datadog_lambda-5.94.0 → datadog_lambda-6.96.0}/datadog_lambda/tag_object.py +0 -0
  24. {datadog_lambda-5.94.0 → datadog_lambda-6.96.0}/datadog_lambda/tags.py +0 -0
  25. {datadog_lambda-5.94.0 → datadog_lambda-6.96.0}/datadog_lambda/thread_stats_writer.py +0 -0
  26. {datadog_lambda-5.94.0 → datadog_lambda-6.96.0}/datadog_lambda/trigger.py +0 -0
  27. {datadog_lambda-5.94.0 → datadog_lambda-6.96.0}/datadog_lambda/wrapper.py +0 -0
  28. {datadog_lambda-5.94.0 → datadog_lambda-6.96.0}/datadog_lambda/xray.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: datadog_lambda
3
- Version: 5.94.0
3
+ Version: 6.96.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
@@ -18,7 +18,7 @@ Classifier: Programming Language :: Python :: 3.12
18
18
  Provides-Extra: dev
19
19
  Requires-Dist: boto3 (>=1.28.0,<2.0.0) ; extra == "dev"
20
20
  Requires-Dist: datadog (>=0.41.0,<1.0.0)
21
- Requires-Dist: ddtrace (>=2.7.2)
21
+ Requires-Dist: ddtrace (>=2.9.0)
22
22
  Requires-Dist: flake8 (>=5.0.4,<6.0.0) ; extra == "dev"
23
23
  Requires-Dist: pytest (>=8.0.0,<9.0.0) ; extra == "dev"
24
24
  Requires-Dist: pytest-benchmark (>=4.0,<5.0) ; extra == "dev"
@@ -79,6 +79,9 @@ The Continuous Profiler works by spawning a thread which periodically wakes up a
79
79
 
80
80
  ## Major Version Notes
81
81
 
82
+ ### 6.x / Layer version 95+
83
+ - The release changed how Lambda's traceID is hashed if the incoming payload contains Step Functions context object. This change only affects those who uses inject Step Functions context object into Lambda payload.
84
+
82
85
  ### 5.x / Layer version 86+
83
86
  - Python3.7 support has been [deprecated](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html) by AWS, and support removed from this library.
84
87
 
@@ -47,6 +47,9 @@ The Continuous Profiler works by spawning a thread which periodically wakes up a
47
47
 
48
48
  ## Major Version Notes
49
49
 
50
+ ### 6.x / Layer version 95+
51
+ - The release changed how Lambda's traceID is hashed if the incoming payload contains Step Functions context object. This change only affects those who uses inject Step Functions context object into Lambda payload.
52
+
50
53
  ### 5.x / Layer version 86+
51
54
  - Python3.7 support has been [deprecated](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html) by AWS, and support removed from this library.
52
55
 
@@ -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 when the extension is present
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
- import_node.end_time_ns - import_node.start_time_ns
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
  ):
@@ -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
- init_api()
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
- flush_in_thread = os.environ.get("DD_FLUSH_IN_THREAD", "").lower() == "true"
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,6 +111,9 @@ 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
@@ -72,6 +72,8 @@ is_lambda_context = os.environ.get(XrayDaemon.FUNCTION_NAME_HEADER_NAME) != ""
72
72
  propagator = HTTPPropagator()
73
73
 
74
74
  DD_TRACE_JAVA_TRACE_ID_PADDING = "00000000"
75
+ HIGHER_64_BITS = "HIGHER_64_BITS"
76
+ LOWER_64_BITS = "LOWER_64_BITS"
75
77
 
76
78
 
77
79
  def _convert_xray_trace_id(xray_trace_id):
@@ -273,7 +275,7 @@ def extract_context_from_sqs_or_sns_event_or_context(event, lambda_context):
273
275
  else:
274
276
  # Handle case where trace context is injected into attributes.AWSTraceHeader
275
277
  # example: Root=1-654321ab-000000001234567890abcdef;Parent=0123456789abcdef;Sampled=1
276
- attrs = first_record.get("attributes")
278
+ attrs = event.get("Records")[0].get("attributes")
277
279
  if attrs:
278
280
  x_ray_header = attrs.get("AWSTraceHeader")
279
281
  if x_ray_header:
@@ -354,14 +356,16 @@ def extract_context_from_kinesis_event(event, lambda_context):
354
356
  return extract_context_from_lambda_context(lambda_context)
355
357
 
356
358
 
357
- def _deterministic_md5_hash(s: str) -> int:
358
- """MD5 here is to generate trace_id, not for any encryption."""
359
- hex_number = hashlib.md5(s.encode("ascii")).hexdigest()
360
- binary = bin(int(hex_number, 16))
361
- binary_str = str(binary)
362
- binary_str_remove_0b = binary_str[2:].rjust(128, "0")
363
- most_significant_64_bits_without_leading_1 = "0" + binary_str_remove_0b[1:-64]
364
- result = int(most_significant_64_bits_without_leading_1, 2)
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)
365
369
  if result == 0:
366
370
  return 1
367
371
  return result
@@ -376,13 +380,27 @@ def extract_context_from_step_functions(event, lambda_context):
376
380
  execution_id = event.get("Execution").get("Id")
377
381
  state_name = event.get("State").get("Name")
378
382
  state_entered_time = event.get("State").get("EnteredTime")
379
- trace_id = _deterministic_md5_hash(execution_id)
380
- parent_id = _deterministic_md5_hash(
381
- f"{execution_id}#{state_name}#{state_entered_time}"
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
382
390
  )
391
+
383
392
  sampling_priority = SamplingPriority.AUTO_KEEP
384
393
  return Context(
385
- trace_id=trace_id, span_id=parent_id, sampling_priority=sampling_priority
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
+ },
386
404
  )
387
405
  except Exception as e:
388
406
  logger.debug("The Step Functions trace extractor returned with error %s", e)
@@ -1246,9 +1264,9 @@ def create_function_execution_span(
1246
1264
  "function_version": function_version,
1247
1265
  "request_id": context.aws_request_id,
1248
1266
  "resource_names": context.function_name,
1249
- "functionname": context.function_name.lower()
1250
- if context.function_name
1251
- else None,
1267
+ "functionname": (
1268
+ context.function_name.lower() if context.function_name else None
1269
+ ),
1252
1270
  "datadog_lambda": datadog_lambda_version,
1253
1271
  "dd_trace": ddtrace_version,
1254
1272
  "span.name": "aws.lambda",
@@ -0,0 +1 @@
1
+ __version__ = "6.96.0"
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "datadog_lambda"
3
- version = "5.94.0"
3
+ version = "6.96.0"
4
4
  description = "The Datadog AWS Lambda Library"
5
5
  authors = ["Datadog, Inc. <dev@datadoghq.com>"]
6
6
  license = "Apache-2.0"
@@ -27,7 +27,7 @@ classifiers = [
27
27
  python = ">=3.8.0,<4"
28
28
  datadog = ">=0.41.0,<1.0.0"
29
29
  wrapt = "^1.11.2"
30
- ddtrace = ">=2.7.2"
30
+ ddtrace = ">=2.9.0"
31
31
  ujson = ">=5.9.0"
32
32
  urllib3 = [
33
33
  {version = "<2.0.0", python = "<3.11", optional = true},
@@ -1 +0,0 @@
1
- __version__ = "5.94.0"
File without changes
File without changes