datadog_lambda 6.98.0__py3-none-any.whl → 6.100.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/metric.py CHANGED
@@ -125,6 +125,7 @@ def flush_stats(lambda_context=None):
125
125
  lambda_stats.flush()
126
126
 
127
127
  if extension_thread_stats is not None:
128
+ tags = None
128
129
  if lambda_context is not None:
129
130
  tags = get_enhanced_metrics_tags(lambda_context)
130
131
  split_arn = lambda_context.invoked_function_arn.split(":")
@@ -0,0 +1,158 @@
1
+ from itertools import chain
2
+ import logging
3
+ from typing import List
4
+
5
+ from ddtrace._trace.utils_botocore.span_pointers.dynamodb import (
6
+ _aws_dynamodb_item_span_pointer_description,
7
+ )
8
+ from ddtrace._trace.utils_botocore.span_pointers.s3 import (
9
+ _aws_s3_object_span_pointer_description,
10
+ )
11
+ from ddtrace._trace._span_pointer import _SpanPointerDirection
12
+ from ddtrace._trace._span_pointer import _SpanPointerDescription
13
+ from datadog_lambda.trigger import EventTypes
14
+
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ def calculate_span_pointers(
20
+ event_source,
21
+ event,
22
+ ) -> List[_SpanPointerDescription]:
23
+ try:
24
+ if event_source.equals(EventTypes.S3):
25
+ return _calculate_s3_span_pointers_for_event(event)
26
+
27
+ elif event_source.equals(EventTypes.DYNAMODB):
28
+ return _calculate_dynamodb_span_pointers_for_event(event)
29
+
30
+ except Exception as e:
31
+ logger.warning(
32
+ "failed to calculate span pointers for event: %s",
33
+ e,
34
+ )
35
+
36
+ return []
37
+
38
+
39
+ def _calculate_s3_span_pointers_for_event(event) -> List[_SpanPointerDescription]:
40
+ # Example event:
41
+ # https://docs.aws.amazon.com/lambda/latest/dg/with-s3.html
42
+
43
+ return list(
44
+ chain.from_iterable(
45
+ _calculate_s3_span_pointers_for_event_record(record)
46
+ for record in event.get("Records", [])
47
+ )
48
+ )
49
+
50
+
51
+ def _calculate_s3_span_pointers_for_event_record(
52
+ record,
53
+ ) -> List[_SpanPointerDescription]:
54
+ # Event types:
55
+ # https://docs.aws.amazon.com/AmazonS3/latest/userguide/notification-how-to-event-types-and-destinations.html
56
+
57
+ if record.get("eventName").startswith("ObjectCreated:"):
58
+ s3_information = record.get("s3", None)
59
+ if s3_information is not None:
60
+ return _calculate_s3_span_pointers_for_object_created_s3_information(
61
+ s3_information
62
+ )
63
+
64
+ return []
65
+
66
+
67
+ def _calculate_s3_span_pointers_for_object_created_s3_information(
68
+ s3_information,
69
+ ) -> List[_SpanPointerDescription]:
70
+ try:
71
+ bucket = s3_information["bucket"]["name"]
72
+ key = s3_information["object"]["key"]
73
+ etag = s3_information["object"]["eTag"]
74
+
75
+ except KeyError as e:
76
+ logger.warning(
77
+ "missing s3 information required to make a span pointer: %s",
78
+ e,
79
+ )
80
+ return []
81
+
82
+ try:
83
+ return [
84
+ _aws_s3_object_span_pointer_description(
85
+ pointer_direction=_SpanPointerDirection.UPSTREAM,
86
+ bucket=bucket,
87
+ key=key,
88
+ etag=etag,
89
+ )
90
+ ]
91
+
92
+ except Exception as e:
93
+ logger.warning(
94
+ "failed to generate S3 span pointer: %s",
95
+ e,
96
+ )
97
+ return []
98
+
99
+
100
+ def _calculate_dynamodb_span_pointers_for_event(event) -> List[_SpanPointerDescription]:
101
+ # Example event:
102
+ # https://docs.aws.amazon.com/lambda/latest/dg/with-ddb.html
103
+
104
+ return list(
105
+ chain.from_iterable(
106
+ _calculate_dynamodb_span_pointers_for_event_record(record)
107
+ for record in event.get("Records", [])
108
+ )
109
+ )
110
+
111
+
112
+ def _calculate_dynamodb_span_pointers_for_event_record(
113
+ record,
114
+ ) -> List[_SpanPointerDescription]:
115
+ try:
116
+ table_name = _extract_table_name_from_dynamodb_stream_record(record)
117
+ primary_key = record["dynamodb"]["Keys"]
118
+
119
+ except Exception as e:
120
+ logger.warning(
121
+ "missing DynamoDB information required to make a span pointer: %s",
122
+ e,
123
+ )
124
+ return []
125
+
126
+ try:
127
+ return [
128
+ _aws_dynamodb_item_span_pointer_description(
129
+ pointer_direction=_SpanPointerDirection.UPSTREAM,
130
+ table_name=table_name,
131
+ primary_key=primary_key,
132
+ )
133
+ ]
134
+
135
+ except Exception as e:
136
+ logger.warning(
137
+ "failed to generate DynamoDB span pointer: %s",
138
+ e,
139
+ )
140
+ return []
141
+
142
+
143
+ def _extract_table_name_from_dynamodb_stream_record(record) -> str:
144
+ # Example eventSourceARN:
145
+ # arn:aws:dynamodb:us-east-2:123456789012:table/my-table/stream/2024-06-10T19:26:16.525
146
+ event_source_arn = record["eventSourceARN"]
147
+
148
+ [_arn, _aws, _dynamodb, _region, _account, dynamodb_info] = event_source_arn.split(
149
+ ":", maxsplit=5
150
+ )
151
+ if _arn != "arn" or _aws != "aws" or _dynamodb != "dynamodb":
152
+ raise ValueError(f"unexpected eventSourceARN format: {event_source_arn}")
153
+
154
+ [_table, table_name, _stream, _timestamp] = dynamodb_info.split("/")
155
+ if _table != "table" or _stream != "stream":
156
+ raise ValueError(f"unexpected eventSourceARN format: {event_source_arn}")
157
+
158
+ return table_name
@@ -27,8 +27,11 @@ class ThreadStatsWriter(StatsWriter):
27
27
  Modified based on `datadog.threadstats.base.ThreadStats.flush()`,
28
28
  to gain better control over exception handling.
29
29
  """
30
+ original_constant_tags = self.thread_stats.constant_tags.copy()
30
31
  if tags:
31
- self.thread_stats.constant_tags = self.thread_stats.constant_tags + tags
32
+ # Temporarily add tags for this flush
33
+ self.thread_stats.constant_tags = original_constant_tags + tags
34
+
32
35
  _, dists = self.thread_stats._get_aggregate_metrics_and_dists(float("inf"))
33
36
  count_dists = len(dists)
34
37
  if not count_dists:
@@ -62,6 +65,9 @@ class ThreadStatsWriter(StatsWriter):
62
65
  logger.debug(
63
66
  "Flush #%s failed", self.thread_stats.flush_count, exc_info=True
64
67
  )
68
+ finally:
69
+ # Reset constant_tags to its original state
70
+ self.thread_stats.constant_tags = original_constant_tags
65
71
 
66
72
  def stop(self):
67
73
  self.thread_stats.stop()
datadog_lambda/tracing.py CHANGED
@@ -407,6 +407,14 @@ def extract_context_from_step_functions(event, lambda_context):
407
407
  return extract_context_from_lambda_context(lambda_context)
408
408
 
409
409
 
410
+ def is_legacy_lambda_step_function(event):
411
+ """
412
+ Check if the event is a step function that called a legacy lambda
413
+ """
414
+ event = event.get("Payload", {})
415
+ return "Execution" in event and "StateMachine" in event and "State" in event
416
+
417
+
410
418
  def extract_context_custom_extractor(extractor, event, lambda_context):
411
419
  """
412
420
  Extract Datadog trace context using a custom trace extractor function
@@ -1251,6 +1259,7 @@ def create_function_execution_span(
1251
1259
  merge_xray_traces,
1252
1260
  trigger_tags,
1253
1261
  parent_span=None,
1262
+ span_pointers=None,
1254
1263
  ):
1255
1264
  tags = None
1256
1265
  if context:
@@ -1288,6 +1297,14 @@ def create_function_execution_span(
1288
1297
  span.set_tags(tags)
1289
1298
  if parent_span:
1290
1299
  span.parent_id = parent_span.span_id
1300
+ if span_pointers:
1301
+ for span_pointer_description in span_pointers:
1302
+ span._add_span_pointer(
1303
+ pointer_kind=span_pointer_description.pointer_kind,
1304
+ pointer_direction=span_pointer_description.pointer_direction,
1305
+ pointer_hash=span_pointer_description.pointer_hash,
1306
+ extra_attributes=span_pointer_description.extra_attributes,
1307
+ )
1291
1308
  return span
1292
1309
 
1293
1310
 
datadog_lambda/trigger.py CHANGED
@@ -299,6 +299,12 @@ def extract_http_tags(event):
299
299
  if headers and headers.get("Referer"):
300
300
  http_tags["http.referer"] = headers.get("Referer")
301
301
 
302
+ # Try to get `routeKey` from API GW v2; otherwise try to get `resource` from API GW v1
303
+ route = event.get("routeKey") or event.get("resource")
304
+ if route:
305
+ # "GET /my/endpoint" = > "/my/endpoint"
306
+ http_tags["http.route"] = route.split(" ")[-1]
307
+
302
308
  return http_tags
303
309
 
304
310
 
datadog_lambda/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "6.98.0"
1
+ __version__ = "6.100.0"
datadog_lambda/wrapper.py CHANGED
@@ -30,6 +30,7 @@ from datadog_lambda.metric import (
30
30
  )
31
31
  from datadog_lambda.module_name import modify_module_name
32
32
  from datadog_lambda.patch import patch_all
33
+ from datadog_lambda.span_pointers import calculate_span_pointers
33
34
  from datadog_lambda.tracing import (
34
35
  extract_dd_trace_context,
35
36
  create_dd_dummy_metadata_subsegment,
@@ -44,6 +45,7 @@ from datadog_lambda.tracing import (
44
45
  is_authorizer_response,
45
46
  tracer,
46
47
  propagator,
48
+ is_legacy_lambda_step_function,
47
49
  )
48
50
  from datadog_lambda.trigger import (
49
51
  extract_trigger_tags,
@@ -277,6 +279,8 @@ class _LambdaDecorator(object):
277
279
  self.response = None
278
280
  set_cold_start(init_timestamp_ns)
279
281
  submit_invocations_metric(context)
282
+ if is_legacy_lambda_step_function(event):
283
+ event = event["Payload"]
280
284
  self.trigger_tags = extract_trigger_tags(event, context)
281
285
  # Extract Datadog trace context and source from incoming requests
282
286
  dd_context, trace_context_source, event_source = extract_dd_trace_context(
@@ -304,14 +308,15 @@ class _LambdaDecorator(object):
304
308
  event, context, event_source, self.decode_authorizer_context
305
309
  )
306
310
  self.span = create_function_execution_span(
307
- context,
308
- self.function_name,
309
- is_cold_start(),
310
- is_proactive_init(),
311
- trace_context_source,
312
- self.merge_xray_traces,
313
- self.trigger_tags,
311
+ context=context,
312
+ function_name=self.function_name,
313
+ is_cold_start=is_cold_start(),
314
+ is_proactive_init=is_proactive_init(),
315
+ trace_context_source=trace_context_source,
316
+ merge_xray_traces=self.merge_xray_traces,
317
+ trigger_tags=self.trigger_tags,
314
318
  parent_span=self.inferred_span,
319
+ span_pointers=calculate_span_pointers(event_source, event),
315
320
  )
316
321
  else:
317
322
  set_correlation_ids()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: datadog_lambda
3
- Version: 6.98.0
3
+ Version: 6.100.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
@@ -15,10 +15,11 @@ Classifier: Programming Language :: Python :: 3.9
15
15
  Classifier: Programming Language :: Python :: 3.10
16
16
  Classifier: Programming Language :: Python :: 3.11
17
17
  Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
18
19
  Provides-Extra: dev
19
20
  Requires-Dist: boto3 (>=1.34.0,<2.0.0) ; extra == "dev"
20
21
  Requires-Dist: datadog (>=0.41.0,<1.0.0)
21
- Requires-Dist: ddtrace (>=2.10.0)
22
+ Requires-Dist: ddtrace (>=2.15.0)
22
23
  Requires-Dist: flake8 (>=5.0.4,<6.0.0) ; extra == "dev"
23
24
  Requires-Dist: pytest (>=8.0.0,<9.0.0) ; extra == "dev"
24
25
  Requires-Dist: pytest-benchmark (>=4.0,<5.0) ; extra == "dev"
@@ -6,22 +6,23 @@ datadog_lambda/dogstatsd.py,sha256=HCyl72oQUSF3E4y1ivrHaGTHL9WG1asGjB1Xo2D_Abc,4
6
6
  datadog_lambda/extension.py,sha256=ZU64QpA2K9K9C0jfqusBgpiWQe0QA2dcJCNk7UgjVfw,621
7
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=RF9Nkl22bzMblSHFC0NJHxgCUe_D4X5ECXCAudTHPRE,6256
9
+ datadog_lambda/metric.py,sha256=hxA5nmKkUKpoo13f0Z5Ug87F9A-fv1hP-l2fWYfq6ao,6276
10
10
  datadog_lambda/module_name.py,sha256=5FmOCjjgjq78b6a83QePZZFmqahAoy9XHdUNWdq2D1Q,139
11
11
  datadog_lambda/patch.py,sha256=6a-BqovSRKsU5hTQpzxgY-_bducT-UEVCLvd3fdxeWc,4710
12
+ datadog_lambda/span_pointers.py,sha256=zGD3QVIM8X2qx0nzVRoZPlWPKvUdG6ubIdcb3m9Dx54,4647
12
13
  datadog_lambda/stats_writer.py,sha256=SIac96wu45AxDOZ4GraCbK3r1RKr4AFgXcEPHg1VX0A,243
13
14
  datadog_lambda/statsd_writer.py,sha256=F4SCJ6-J6YfvQNh0uQfAkP6QYiAtV3-MCsxz4QnaBBI,403
14
15
  datadog_lambda/tag_object.py,sha256=cZ7W9Ae5k3YxLOZzN5Hu8UqvOKtq5AWARele0L18Gjs,2091
15
16
  datadog_lambda/tags.py,sha256=wy6uH8eAGMn7cfZEdHpL9uEGoM85bVyyXhYwSQtfHHc,2532
16
- datadog_lambda/thread_stats_writer.py,sha256=I_jNCiUFYaZT25d6hOe_uoG2PTw7J-Fqa3xclbRAkAo,2635
17
- datadog_lambda/tracing.py,sha256=KYDWgxJLNmNoFKMzGnzRMNXEgFah7K5xtMTXfrWY6c8,49651
18
- datadog_lambda/trigger.py,sha256=JK5am_XmEvueuLKrrF7k08sWnZVDOD6QvQjrsUJx1dM,12177
19
- datadog_lambda/version.py,sha256=fu-1_SekFgd6iZRh1NdgF2Ph2fr9IGw4br2xwstITo0,23
20
- datadog_lambda/wrapper.py,sha256=-wTyQJe-dQ1tajo7Rl5rjzZTcEK0N6aNrCYBBFjkXoU,15590
17
+ datadog_lambda/thread_stats_writer.py,sha256=zOHdzdkQFCHnLU09CSYWsGq1optbTKzHVLcDXDjtiHU,2891
18
+ datadog_lambda/tracing.py,sha256=RfaOVnCAkAXTo6AgIRnk8l_m2BNUic7H7GYrrDSz7RE,50341
19
+ datadog_lambda/trigger.py,sha256=3S7u-LBHxX-o2Ds8BAtXEQMNPXz-Av8OdWBQuozLl54,12445
20
+ datadog_lambda/version.py,sha256=ou4_yqeEM8ttPZPEwnBoJQIKcx2ElpmxTWI-dGHwd7A,24
21
+ datadog_lambda/wrapper.py,sha256=xTOO-NhrndReagcPJ0Ab9Xd8NRMCqsgZH4bJ7mpQXnI,15972
21
22
  datadog_lambda/xray.py,sha256=jvA4Fk76PLMgsjUoUZ7gp2otv53hFt39Nvso1ZNaivg,3749
22
- datadog_lambda-6.98.0.dist-info/LICENSE,sha256=4yQmjpKp1MKL7DdRDPVHkKYc2W0aezm5SIDske8oAdM,11379
23
- datadog_lambda-6.98.0.dist-info/LICENSE-3rdparty.csv,sha256=9CDAR1GKawwTbZkqt1RP0uwEcaRM3RhOeTB5tWXr8Ts,1381
24
- datadog_lambda-6.98.0.dist-info/METADATA,sha256=e7ILlUK1jOhf3wXD-49RficIJba2Z6gixo_h9gaB2uk,7417
25
- datadog_lambda-6.98.0.dist-info/NOTICE,sha256=Jue-d8mQ1ENIHDZdYc2-X8mVYtScXb8pzF1pTLN-kRc,141
26
- datadog_lambda-6.98.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
27
- datadog_lambda-6.98.0.dist-info/RECORD,,
23
+ datadog_lambda-6.100.0.dist-info/LICENSE,sha256=4yQmjpKp1MKL7DdRDPVHkKYc2W0aezm5SIDske8oAdM,11379
24
+ datadog_lambda-6.100.0.dist-info/LICENSE-3rdparty.csv,sha256=9CDAR1GKawwTbZkqt1RP0uwEcaRM3RhOeTB5tWXr8Ts,1381
25
+ datadog_lambda-6.100.0.dist-info/METADATA,sha256=eHjxub7su5CIyqRlKXcXV2fSwA9NMTjqNlN2-KwXDno,7469
26
+ datadog_lambda-6.100.0.dist-info/NOTICE,sha256=Jue-d8mQ1ENIHDZdYc2-X8mVYtScXb8pzF1pTLN-kRc,141
27
+ datadog_lambda-6.100.0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
28
+ datadog_lambda-6.100.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 1.9.0
2
+ Generator: poetry-core 1.9.1
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any