datadog_lambda 5.93.0__py3-none-any.whl → 6.95.0__py3-none-any.whl

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/api.py CHANGED
@@ -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
  ):
datadog_lambda/handler.py CHANGED
@@ -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
- handler_module = import_module(modified_mod_name)
31
- handler = datadog_lambda_wrapper(getattr(handler_module, handler_name))
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)
datadog_lambda/metric.py CHANGED
@@ -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,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 (dict): Lambda context dict passed to the function by AWS
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 (dict): Lambda context dict passed to the function by AWS
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 (dict): Lambda context dict passed to the function by AWS
150
+ lambda_context (object): Lambda context dict passed to the function by AWS
131
151
  """
132
152
  submit_enhanced_metric("errors", lambda_context)
datadog_lambda/tags.py CHANGED
@@ -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 = parse_lambda_tags_from_arn(lambda_context)
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
datadog_lambda/tracing.py CHANGED
@@ -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 = first_record.get("attributes")
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 _deterministic_md5_hash(s: str) -> int:
357
- """MD5 here is to generate trace_id, not for any encryption."""
358
- hex_number = hashlib.md5(s.encode("ascii")).hexdigest()
359
- binary = bin(int(hex_number, 16))
360
- binary_str = str(binary)
361
- binary_str_remove_0b = binary_str[2:].rjust(128, "0")
362
- most_significant_64_bits_without_leading_1 = "0" + binary_str_remove_0b[1:-64]
363
- 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)
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
- trace_id = _deterministic_md5_hash(execution_id)
379
- parent_id = _deterministic_md5_hash(
380
- 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
381
390
  )
391
+
382
392
  sampling_priority = SamplingPriority.AUTO_KEEP
383
393
  return Context(
384
- 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
+ },
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": context.function_name.lower()
1249
- if context.function_name
1250
- else None,
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()
datadog_lambda/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "5.93.0"
1
+ __version__ = "6.95.0"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: datadog_lambda
3
- Version: 5.93.0
3
+ Version: 6.95.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
@@ -1,27 +1,27 @@
1
1
  datadog_lambda/__init__.py,sha256=F_KG5XbNq9QEzbioec7DXzyTv3oYJHxeEgXE88dxXRQ,637
2
- datadog_lambda/api.py,sha256=TFg7gCek088_C53cZQQHDoLXGlTAhP2AD8NAuWYOVco,3653
3
- datadog_lambda/cold_start.py,sha256=8-MsHGUo4fH88HrcOpjnHa6uRBEHOW3bI-fhiRNZQzg,8171
2
+ datadog_lambda/api.py,sha256=0yxijMKG6a6drq4ZRkm_mu5Q0tAE2DkwRoIOHIZaZnU,3570
3
+ datadog_lambda/cold_start.py,sha256=x-wj0F9QDoxzmYWhZRTajxmSCYM1ZNM6XXTs_61BP-w,8205
4
4
  datadog_lambda/constants.py,sha256=DeujbnguBT9nDioiaYlgQQdZ6Ps53sWXmYhruLVoCHE,1669
5
5
  datadog_lambda/dogstatsd.py,sha256=HCyl72oQUSF3E4y1ivrHaGTHL9WG1asGjB1Xo2D_Abc,4769
6
6
  datadog_lambda/extension.py,sha256=ZU64QpA2K9K9C0jfqusBgpiWQe0QA2dcJCNk7UgjVfw,621
7
- datadog_lambda/handler.py,sha256=P-ImnqDohdNBqiA-YGlhJf83FHgweCOC0LgzudZlc7A,992
7
+ datadog_lambda/handler.py,sha256=YuReCUXLyJCNTeRP_VgWzjCqUl6K6IRs_PU6RHm0VeE,1351
8
8
  datadog_lambda/logger.py,sha256=nGxNMouF7wcjmoPsgivzzjNLvSy3WbGtKElxOvITZDg,766
9
- datadog_lambda/metric.py,sha256=JzErzS_ZCYbQ7iojnDSSVSypWj_EyrSSqO2-CQ3cWSE,4595
9
+ datadog_lambda/metric.py,sha256=NwuBetjv4teRkw6dzaE0du46mvom61VBOxMZHNt00PQ,5376
10
10
  datadog_lambda/module_name.py,sha256=5FmOCjjgjq78b6a83QePZZFmqahAoy9XHdUNWdq2D1Q,139
11
11
  datadog_lambda/patch.py,sha256=6a-BqovSRKsU5hTQpzxgY-_bducT-UEVCLvd3fdxeWc,4710
12
12
  datadog_lambda/stats_writer.py,sha256=SIac96wu45AxDOZ4GraCbK3r1RKr4AFgXcEPHg1VX0A,243
13
13
  datadog_lambda/statsd_writer.py,sha256=F4SCJ6-J6YfvQNh0uQfAkP6QYiAtV3-MCsxz4QnaBBI,403
14
14
  datadog_lambda/tag_object.py,sha256=cZ7W9Ae5k3YxLOZzN5Hu8UqvOKtq5AWARele0L18Gjs,2091
15
- datadog_lambda/tags.py,sha256=PMoxJhIGMWrG7798rqdK8oBp5UzWJrOJaCcK83AeQ3o,2487
15
+ datadog_lambda/tags.py,sha256=wy6uH8eAGMn7cfZEdHpL9uEGoM85bVyyXhYwSQtfHHc,2532
16
16
  datadog_lambda/thread_stats_writer.py,sha256=fkjMDgrzwACrK_ZrCwl9mHz5U3CMLEyrsaondjdM3r8,2522
17
- datadog_lambda/tracing.py,sha256=wngNyiUuHcuy_TmWeKEQBsJiKSj0JyNoHHxhBOlm9Zs,47962
17
+ datadog_lambda/tracing.py,sha256=KYDWgxJLNmNoFKMzGnzRMNXEgFah7K5xtMTXfrWY6c8,49651
18
18
  datadog_lambda/trigger.py,sha256=JK5am_XmEvueuLKrrF7k08sWnZVDOD6QvQjrsUJx1dM,12177
19
- datadog_lambda/version.py,sha256=uZmcswY6DeOIt6kTpeQsTfsvVvo4yNIPQZ1BLpNMuQE,23
19
+ datadog_lambda/version.py,sha256=y6iFUeWy_m4LLPKgNDvkfArSwdqMy3dZEtKJUo4vLKY,23
20
20
  datadog_lambda/wrapper.py,sha256=aU2sQdtv_7g1Cb1gAAxkEy4zUcFSsZVwRGgHreIWwB0,15272
21
21
  datadog_lambda/xray.py,sha256=jvA4Fk76PLMgsjUoUZ7gp2otv53hFt39Nvso1ZNaivg,3749
22
- datadog_lambda-5.93.0.dist-info/LICENSE,sha256=4yQmjpKp1MKL7DdRDPVHkKYc2W0aezm5SIDske8oAdM,11379
23
- datadog_lambda-5.93.0.dist-info/LICENSE-3rdparty.csv,sha256=9CDAR1GKawwTbZkqt1RP0uwEcaRM3RhOeTB5tWXr8Ts,1381
24
- datadog_lambda-5.93.0.dist-info/METADATA,sha256=sm8-SEF9ywrAI4x8fMpviEuH4_Fub6NChFTEFmNjtWw,7289
25
- datadog_lambda-5.93.0.dist-info/NOTICE,sha256=Jue-d8mQ1ENIHDZdYc2-X8mVYtScXb8pzF1pTLN-kRc,141
26
- datadog_lambda-5.93.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
27
- datadog_lambda-5.93.0.dist-info/RECORD,,
22
+ datadog_lambda-6.95.0.dist-info/LICENSE,sha256=4yQmjpKp1MKL7DdRDPVHkKYc2W0aezm5SIDske8oAdM,11379
23
+ datadog_lambda-6.95.0.dist-info/LICENSE-3rdparty.csv,sha256=9CDAR1GKawwTbZkqt1RP0uwEcaRM3RhOeTB5tWXr8Ts,1381
24
+ datadog_lambda-6.95.0.dist-info/METADATA,sha256=mfSnI4xkVVF76yqannEPjibGRi1wcrQlCIzgKzxnMIw,7289
25
+ datadog_lambda-6.95.0.dist-info/NOTICE,sha256=Jue-d8mQ1ENIHDZdYc2-X8mVYtScXb8pzF1pTLN-kRc,141
26
+ datadog_lambda-6.95.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
27
+ datadog_lambda-6.95.0.dist-info/RECORD,,