datadog_lambda 7.112.0__tar.gz → 8.114.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 (31) hide show
  1. {datadog_lambda-7.112.0 → datadog_lambda-8.114.0}/PKG-INFO +2 -2
  2. {datadog_lambda-7.112.0 → datadog_lambda-8.114.0}/datadog_lambda/__init__.py +4 -3
  3. {datadog_lambda-7.112.0 → datadog_lambda-8.114.0}/datadog_lambda/asm.py +78 -11
  4. {datadog_lambda-7.112.0 → datadog_lambda-8.114.0}/datadog_lambda/tracing.py +51 -14
  5. {datadog_lambda-7.112.0 → datadog_lambda-8.114.0}/datadog_lambda/trigger.py +11 -2
  6. datadog_lambda-8.114.0/datadog_lambda/version.py +1 -0
  7. {datadog_lambda-7.112.0 → datadog_lambda-8.114.0}/datadog_lambda/wrapper.py +52 -19
  8. {datadog_lambda-7.112.0 → datadog_lambda-8.114.0}/pyproject.toml +2 -2
  9. datadog_lambda-7.112.0/datadog_lambda/version.py +0 -1
  10. {datadog_lambda-7.112.0 → datadog_lambda-8.114.0}/LICENSE +0 -0
  11. {datadog_lambda-7.112.0 → datadog_lambda-8.114.0}/LICENSE-3rdparty.csv +0 -0
  12. {datadog_lambda-7.112.0 → datadog_lambda-8.114.0}/NOTICE +0 -0
  13. {datadog_lambda-7.112.0 → datadog_lambda-8.114.0}/README.md +0 -0
  14. {datadog_lambda-7.112.0 → datadog_lambda-8.114.0}/datadog_lambda/api.py +0 -0
  15. {datadog_lambda-7.112.0 → datadog_lambda-8.114.0}/datadog_lambda/cold_start.py +0 -0
  16. {datadog_lambda-7.112.0 → datadog_lambda-8.114.0}/datadog_lambda/config.py +0 -0
  17. {datadog_lambda-7.112.0 → datadog_lambda-8.114.0}/datadog_lambda/constants.py +0 -0
  18. {datadog_lambda-7.112.0 → datadog_lambda-8.114.0}/datadog_lambda/dogstatsd.py +0 -0
  19. {datadog_lambda-7.112.0 → datadog_lambda-8.114.0}/datadog_lambda/extension.py +0 -0
  20. {datadog_lambda-7.112.0 → datadog_lambda-8.114.0}/datadog_lambda/handler.py +0 -0
  21. {datadog_lambda-7.112.0 → datadog_lambda-8.114.0}/datadog_lambda/logger.py +0 -0
  22. {datadog_lambda-7.112.0 → datadog_lambda-8.114.0}/datadog_lambda/metric.py +0 -0
  23. {datadog_lambda-7.112.0 → datadog_lambda-8.114.0}/datadog_lambda/module_name.py +0 -0
  24. {datadog_lambda-7.112.0 → datadog_lambda-8.114.0}/datadog_lambda/patch.py +0 -0
  25. {datadog_lambda-7.112.0 → datadog_lambda-8.114.0}/datadog_lambda/span_pointers.py +0 -0
  26. {datadog_lambda-7.112.0 → datadog_lambda-8.114.0}/datadog_lambda/stats_writer.py +0 -0
  27. {datadog_lambda-7.112.0 → datadog_lambda-8.114.0}/datadog_lambda/statsd_writer.py +0 -0
  28. {datadog_lambda-7.112.0 → datadog_lambda-8.114.0}/datadog_lambda/tag_object.py +0 -0
  29. {datadog_lambda-7.112.0 → datadog_lambda-8.114.0}/datadog_lambda/tags.py +0 -0
  30. {datadog_lambda-7.112.0 → datadog_lambda-8.114.0}/datadog_lambda/thread_stats_writer.py +0 -0
  31. {datadog_lambda-7.112.0 → datadog_lambda-8.114.0}/datadog_lambda/xray.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: datadog_lambda
3
- Version: 7.112.0
3
+ Version: 8.114.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
@@ -19,7 +19,7 @@ Classifier: Programming Language :: Python :: 3.13
19
19
  Provides-Extra: dev
20
20
  Requires-Dist: botocore (>=1.34.0,<2.0.0) ; extra == "dev"
21
21
  Requires-Dist: datadog (>=0.51.0,<1.0.0)
22
- Requires-Dist: ddtrace (>=3.10.2,<4)
22
+ Requires-Dist: ddtrace (>=3.11.0,<4)
23
23
  Requires-Dist: flake8 (>=5.0.4,<6.0.0) ; extra == "dev"
24
24
  Requires-Dist: pytest (>=8.0.0,<9.0.0) ; extra == "dev"
25
25
  Requires-Dist: pytest-benchmark (>=4.0,<5.0) ; extra == "dev"
@@ -3,10 +3,11 @@ import os
3
3
 
4
4
 
5
5
  if os.environ.get("DD_INSTRUMENTATION_TELEMETRY_ENABLED") is None:
6
- os.environ["DD_INSTRUMENTATION_TELEMETRY_ENABLED"] = "false"
6
+ # Telemetry is required for Appsec Software Composition Analysis
7
+ os.environ["DD_INSTRUMENTATION_TELEMETRY_ENABLED"] = os.environ.get(
8
+ "DD_APPSEC_ENABLED", "false"
9
+ )
7
10
 
8
- if os.environ.get("DD_API_SECURITY_ENABLED") is None:
9
- os.environ["DD_API_SECURITY_ENABLED"] = "False"
10
11
 
11
12
  initialize_cold_start_tracing()
12
13
 
@@ -1,9 +1,12 @@
1
- from copy import deepcopy
2
1
  import logging
2
+ import urllib.parse
3
+ from copy import deepcopy
3
4
  from typing import Any, Dict, List, Optional, Union
4
5
 
5
6
  from ddtrace.contrib.internal.trace_utils import _get_request_header_client_ip
6
7
  from ddtrace.internal import core
8
+ from ddtrace.internal.utils import get_blocked
9
+ from ddtrace.internal.utils import http as http_utils
7
10
  from ddtrace.trace import Span
8
11
 
9
12
  from datadog_lambda.trigger import (
@@ -50,6 +53,7 @@ def asm_set_context(event_source: _EventSource):
50
53
  This allows the AppSecSpanProcessor to know information about the event
51
54
  at the moment the span is created and skip it when not relevant.
52
55
  """
56
+
53
57
  if event_source.event_type not in _http_event_types:
54
58
  core.set_item("appsec_skip_next_lambda_event", True)
55
59
 
@@ -69,17 +73,16 @@ def asm_start_request(
69
73
  route: Optional[str] = None
70
74
 
71
75
  if event_source.event_type == EventTypes.ALB:
72
- headers = event.get("headers")
73
- multi_value_request_headers = event.get("multiValueHeaders")
74
- if multi_value_request_headers:
75
- request_headers = _to_single_value_headers(multi_value_request_headers)
76
- else:
77
- request_headers = headers or {}
78
-
79
76
  raw_uri = event.get("path")
80
- parsed_query = event.get("multiValueQueryStringParameters") or event.get(
81
- "queryStringParameters"
82
- )
77
+
78
+ if event_source.subtype == EventSubtypes.ALB:
79
+ request_headers = event.get("headers", {})
80
+ parsed_query = event.get("queryStringParameters")
81
+ if event_source.subtype == EventSubtypes.ALB_MULTI_VALUE_HEADERS:
82
+ request_headers = _to_single_value_headers(
83
+ event.get("multiValueHeaders", {})
84
+ )
85
+ parsed_query = event.get("multiValueQueryStringParameters")
83
86
 
84
87
  elif event_source.event_type == EventTypes.LAMBDA_FUNCTION_URL:
85
88
  request_headers = event.get("headers", {})
@@ -126,6 +129,14 @@ def asm_start_request(
126
129
  span.set_tag_str("http.client_ip", request_ip)
127
130
  span.set_tag_str("network.client.ip", request_ip)
128
131
 
132
+ # Encode the parsed query and append it to reconstruct the original raw URI expected by AppSec.
133
+ if parsed_query:
134
+ try:
135
+ encoded_query = urllib.parse.urlencode(parsed_query, doseq=True)
136
+ raw_uri += "?" + encoded_query # type: ignore
137
+ except Exception:
138
+ pass
139
+
129
140
  core.dispatch(
130
141
  # The matching listener is registered in ddtrace.appsec._handlers
131
142
  "aws_lambda.start_request",
@@ -182,3 +193,59 @@ def asm_start_response(
182
193
  response_headers,
183
194
  ),
184
195
  )
196
+
197
+ if isinstance(response, dict) and "statusCode" in response:
198
+ body = response.get("body")
199
+ else:
200
+ body = response
201
+
202
+ core.dispatch(
203
+ # The matching listener is registered in ddtrace.appsec._handlers
204
+ "aws_lambda.parse_body",
205
+ (body,),
206
+ )
207
+
208
+
209
+ def get_asm_blocked_response(
210
+ event_source: _EventSource,
211
+ ) -> Optional[Dict[str, Any]]:
212
+ """Get the blocked response for the given event source."""
213
+ if event_source.event_type not in _http_event_types:
214
+ return None
215
+
216
+ blocked = get_blocked()
217
+ if not blocked:
218
+ return None
219
+
220
+ desired_type = blocked.get("type", "auto")
221
+ if desired_type == "none":
222
+ content_type = "text/plain; charset=utf-8"
223
+ content = ""
224
+ else:
225
+ content_type = blocked.get("content-type", "application/json")
226
+ content = http_utils._get_blocked_template(content_type)
227
+
228
+ response = {
229
+ "statusCode": blocked.get("status_code", 403),
230
+ "body": content,
231
+ "isBase64Encoded": False,
232
+ }
233
+
234
+ needs_multi_value_headers = event_source.equals(
235
+ EventTypes.ALB, EventSubtypes.ALB_MULTI_VALUE_HEADERS
236
+ )
237
+
238
+ if needs_multi_value_headers:
239
+ response["multiValueHeaders"] = {
240
+ "content-type": [content_type],
241
+ }
242
+ if "location" in blocked:
243
+ response["multiValueHeaders"]["location"] = [blocked["location"]]
244
+ else:
245
+ response["headers"] = {
246
+ "content-type": content_type,
247
+ }
248
+ if "location" in blocked:
249
+ response["headers"]["location"] = blocked["location"]
250
+
251
+ return response
@@ -4,6 +4,7 @@
4
4
  # Copyright 2019 Datadog, Inc.
5
5
  import logging
6
6
  import os
7
+ import re
7
8
  import traceback
8
9
  import ujson as json
9
10
  from datetime import datetime, timezone
@@ -856,15 +857,31 @@ def create_service_mapping(val):
856
857
  return new_service_mapping
857
858
 
858
859
 
859
- def determine_service_name(service_mapping, specific_key, generic_key, default_value):
860
- service_name = service_mapping.get(specific_key)
861
- if service_name is None:
862
- service_name = service_mapping.get(generic_key, default_value)
863
- return service_name
860
+ def determine_service_name(
861
+ service_mapping, specific_key, generic_key, extracted_key, fallback=None
862
+ ):
863
+ # Check for mapped service (specific key first, then generic key)
864
+ mapped_service = service_mapping.get(specific_key) or service_mapping.get(
865
+ generic_key
866
+ )
867
+ if mapped_service:
868
+ return mapped_service
869
+
870
+ # Check if AWS service representation is disabled
871
+ aws_service_representation = os.environ.get(
872
+ "DD_TRACE_AWS_SERVICE_REPRESENTATION_ENABLED", ""
873
+ ).lower()
874
+ if aws_service_representation in ("false", "0"):
875
+ return fallback
876
+
877
+ # Use extracted_key if it exists and is not empty, otherwise use fallback
878
+ return (
879
+ extracted_key.strip() if extracted_key and extracted_key.strip() else fallback
880
+ )
864
881
 
865
882
 
866
883
  # Initialization code
867
- service_mapping_str = os.getenv("DD_SERVICE_MAPPING", "")
884
+ service_mapping_str = os.environ.get("DD_SERVICE_MAPPING", "")
868
885
  service_mapping = create_service_mapping(service_mapping_str)
869
886
 
870
887
  _dd_origin = {"_dd.origin": "lambda"}
@@ -988,6 +1005,7 @@ def create_inferred_span_from_api_gateway_websocket_event(
988
1005
  "http.url": http_url,
989
1006
  "endpoint": endpoint,
990
1007
  "resource_names": endpoint,
1008
+ "span.kind": "server",
991
1009
  "apiid": api_id,
992
1010
  "apiname": api_id,
993
1011
  "stage": request_context.get("stage"),
@@ -1046,6 +1064,7 @@ def create_inferred_span_from_api_gateway_event(
1046
1064
  "endpoint": path,
1047
1065
  "http.method": method,
1048
1066
  "resource_names": resource,
1067
+ "span.kind": "server",
1049
1068
  "apiid": api_id,
1050
1069
  "apiname": api_id,
1051
1070
  "stage": request_context.get("stage"),
@@ -1150,12 +1169,13 @@ def create_inferred_span_from_sqs_event(event, context):
1150
1169
  event_source_arn = event_record.get("eventSourceARN")
1151
1170
  queue_name = event_source_arn.split(":")[-1]
1152
1171
  service_name = determine_service_name(
1153
- service_mapping, queue_name, "lambda_sqs", "sqs"
1172
+ service_mapping, queue_name, "lambda_sqs", queue_name, "sqs"
1154
1173
  )
1155
1174
  attrs = event_record.get("attributes") or {}
1156
1175
  tags = {
1157
1176
  "operation_name": "aws.sqs",
1158
1177
  "resource_names": queue_name,
1178
+ "span.kind": "server",
1159
1179
  "queuename": queue_name,
1160
1180
  "event_source_arn": event_source_arn,
1161
1181
  "receipt_handle": event_record.get("receiptHandle"),
@@ -1217,11 +1237,12 @@ def create_inferred_span_from_sns_event(event, context):
1217
1237
  topic_arn = sns_message.get("TopicArn")
1218
1238
  topic_name = topic_arn.split(":")[-1]
1219
1239
  service_name = determine_service_name(
1220
- service_mapping, topic_name, "lambda_sns", "sns"
1240
+ service_mapping, topic_name, "lambda_sns", topic_name, "sns"
1221
1241
  )
1222
1242
  tags = {
1223
1243
  "operation_name": "aws.sns",
1224
1244
  "resource_names": topic_name,
1245
+ "span.kind": "server",
1225
1246
  "topicname": topic_name,
1226
1247
  "topic_arn": topic_arn,
1227
1248
  "message_id": sns_message.get("MessageId"),
@@ -1252,15 +1273,16 @@ def create_inferred_span_from_kinesis_event(event, context):
1252
1273
  event_record = get_first_record(event)
1253
1274
  event_source_arn = event_record.get("eventSourceARN")
1254
1275
  event_id = event_record.get("eventID")
1255
- stream_name = event_source_arn.split(":")[-1]
1276
+ stream_name = re.sub(r"^stream/", "", (event_source_arn or "").split(":")[-1])
1256
1277
  shard_id = event_id.split(":")[0]
1257
1278
  service_name = determine_service_name(
1258
- service_mapping, stream_name, "lambda_kinesis", "kinesis"
1279
+ service_mapping, stream_name, "lambda_kinesis", stream_name, "kinesis"
1259
1280
  )
1260
1281
  kinesis = event_record.get("kinesis") or {}
1261
1282
  tags = {
1262
1283
  "operation_name": "aws.kinesis",
1263
1284
  "resource_names": stream_name,
1285
+ "span.kind": "server",
1264
1286
  "streamname": stream_name,
1265
1287
  "shardid": shard_id,
1266
1288
  "event_source_arn": event_source_arn,
@@ -1287,12 +1309,13 @@ def create_inferred_span_from_dynamodb_event(event, context):
1287
1309
  event_source_arn = event_record.get("eventSourceARN")
1288
1310
  table_name = event_source_arn.split("/")[1]
1289
1311
  service_name = determine_service_name(
1290
- service_mapping, table_name, "lambda_dynamodb", "dynamodb"
1312
+ service_mapping, table_name, "lambda_dynamodb", table_name, "dynamodb"
1291
1313
  )
1292
1314
  dynamodb_message = event_record.get("dynamodb") or {}
1293
1315
  tags = {
1294
1316
  "operation_name": "aws.dynamodb",
1295
1317
  "resource_names": table_name,
1318
+ "span.kind": "server",
1296
1319
  "tablename": table_name,
1297
1320
  "event_source_arn": event_source_arn,
1298
1321
  "event_id": event_record.get("eventID"),
@@ -1321,11 +1344,12 @@ def create_inferred_span_from_s3_event(event, context):
1321
1344
  obj = s3.get("object") or {}
1322
1345
  bucket_name = bucket.get("name")
1323
1346
  service_name = determine_service_name(
1324
- service_mapping, bucket_name, "lambda_s3", "s3"
1347
+ service_mapping, bucket_name, "lambda_s3", bucket_name, "s3"
1325
1348
  )
1326
1349
  tags = {
1327
1350
  "operation_name": "aws.s3",
1328
1351
  "resource_names": bucket_name,
1352
+ "span.kind": "server",
1329
1353
  "event_name": event_record.get("eventName"),
1330
1354
  "bucketname": bucket_name,
1331
1355
  "bucket_arn": bucket.get("arn"),
@@ -1351,11 +1375,12 @@ def create_inferred_span_from_s3_event(event, context):
1351
1375
  def create_inferred_span_from_eventbridge_event(event, context):
1352
1376
  source = event.get("source")
1353
1377
  service_name = determine_service_name(
1354
- service_mapping, source, "lambda_eventbridge", "eventbridge"
1378
+ service_mapping, source, "lambda_eventbridge", source, "eventbridge"
1355
1379
  )
1356
1380
  tags = {
1357
1381
  "operation_name": "aws.eventbridge",
1358
1382
  "resource_names": source,
1383
+ "span.kind": "server",
1359
1384
  "detail_type": event.get("detail-type"),
1360
1385
  }
1361
1386
  InferredSpanInfo.set_tags(
@@ -1429,9 +1454,21 @@ def create_function_execution_span(
1429
1454
  tags["_dd.parent_source"] = trace_context_source
1430
1455
  tags.update(trigger_tags)
1431
1456
  tracer.set_tags(_dd_origin)
1457
+ # Determine service name based on config and env var
1458
+ if config.service:
1459
+ service_name = config.service
1460
+ else:
1461
+ aws_service_representation = os.environ.get(
1462
+ "DD_TRACE_AWS_SERVICE_REPRESENTATION_ENABLED", ""
1463
+ ).lower()
1464
+ if aws_service_representation in ("false", "0"):
1465
+ service_name = "aws.lambda"
1466
+ else:
1467
+ service_name = function_name if function_name else "aws.lambda"
1468
+
1432
1469
  span = tracer.trace(
1433
1470
  "aws.lambda",
1434
- service="aws.lambda",
1471
+ service=service_name,
1435
1472
  resource=function_name,
1436
1473
  span_type="serverless",
1437
1474
  )
@@ -54,6 +54,10 @@ class EventSubtypes(_stringTypedEnum):
54
54
  WEBSOCKET = "websocket"
55
55
  HTTP_API = "http-api"
56
56
 
57
+ ALB = "alb" # regular alb
58
+ # ALB with the multi-value headers option checked on the target group
59
+ ALB_MULTI_VALUE_HEADERS = "alb-multi-value-headers"
60
+
57
61
 
58
62
  class _EventSource:
59
63
  """
@@ -133,7 +137,12 @@ def parse_event_source(event: dict) -> _EventSource:
133
137
  event_source.subtype = EventSubtypes.WEBSOCKET
134
138
 
135
139
  if request_context and request_context.get("elb"):
136
- event_source = _EventSource(EventTypes.ALB)
140
+ if "multiValueHeaders" in event:
141
+ event_source = _EventSource(
142
+ EventTypes.ALB, EventSubtypes.ALB_MULTI_VALUE_HEADERS
143
+ )
144
+ else:
145
+ event_source = _EventSource(EventTypes.ALB, EventSubtypes.ALB)
137
146
 
138
147
  if event.get("awslogs"):
139
148
  event_source = _EventSource(EventTypes.CLOUDWATCH_LOGS)
@@ -288,7 +297,7 @@ def extract_http_tags(event):
288
297
  """
289
298
  Extracts HTTP facet tags from the triggering event
290
299
  """
291
- http_tags = {}
300
+ http_tags = {"span.kind": "server"}
292
301
 
293
302
  # Safely get request_context and ensure it's a dictionary
294
303
  request_context = event.get("requestContext")
@@ -0,0 +1 @@
1
+ __version__ = "8.114.0"
@@ -9,7 +9,7 @@ import ujson as json
9
9
  from importlib import import_module
10
10
  from time import time_ns
11
11
 
12
- from datadog_lambda.asm import asm_set_context, asm_start_response, asm_start_request
12
+ from ddtrace.internal._exceptions import BlockingException
13
13
  from datadog_lambda.extension import should_use_extension, flush_extension
14
14
  from datadog_lambda.cold_start import (
15
15
  set_cold_start,
@@ -46,6 +46,17 @@ from datadog_lambda.trigger import (
46
46
  extract_http_status_code_tag,
47
47
  )
48
48
 
49
+ if config.appsec_enabled:
50
+ from datadog_lambda.asm import (
51
+ asm_set_context,
52
+ asm_start_response,
53
+ asm_start_request,
54
+ get_asm_blocked_response,
55
+ )
56
+ from ddtrace.internal.appsec.product import start
57
+
58
+ start()
59
+
49
60
  if config.profiling_enabled:
50
61
  from ddtrace.profiling import profiler
51
62
 
@@ -120,6 +131,7 @@ class _LambdaDecorator(object):
120
131
  self.span = None
121
132
  self.inferred_span = None
122
133
  self.response = None
134
+ self.blocking_response = None
123
135
 
124
136
  if config.profiling_enabled:
125
137
  self.prof = profiler.Profiler(env=config.env, service=config.service)
@@ -159,8 +171,12 @@ class _LambdaDecorator(object):
159
171
  """Executes when the wrapped function gets called"""
160
172
  self._before(event, context)
161
173
  try:
174
+ if self.blocking_response:
175
+ return self.blocking_response
162
176
  self.response = self.func(event, context, **kwargs)
163
177
  return self.response
178
+ except BlockingException:
179
+ self.blocking_response = get_asm_blocked_response(self.event_source)
164
180
  except Exception:
165
181
  from datadog_lambda.metric import submit_errors_metric
166
182
 
@@ -171,6 +187,8 @@ class _LambdaDecorator(object):
171
187
  raise
172
188
  finally:
173
189
  self._after(event, context)
190
+ if self.blocking_response:
191
+ return self.blocking_response
174
192
 
175
193
  def _inject_authorizer_span_headers(self, request_id):
176
194
  reference_span = self.inferred_span if self.inferred_span else self.span
@@ -203,6 +221,7 @@ class _LambdaDecorator(object):
203
221
  def _before(self, event, context):
204
222
  try:
205
223
  self.response = None
224
+ self.blocking_response = None
206
225
  set_cold_start(init_timestamp_ns)
207
226
 
208
227
  if not should_use_extension:
@@ -253,6 +272,7 @@ class _LambdaDecorator(object):
253
272
  )
254
273
  if config.appsec_enabled:
255
274
  asm_start_request(self.span, event, event_source, self.trigger_tags)
275
+ self.blocking_response = get_asm_blocked_response(self.event_source)
256
276
  else:
257
277
  set_correlation_ids()
258
278
  if config.profiling_enabled and is_new_sandbox():
@@ -264,6 +284,34 @@ class _LambdaDecorator(object):
264
284
  def _after(self, event, context):
265
285
  try:
266
286
  status_code = extract_http_status_code_tag(self.trigger_tags, self.response)
287
+
288
+ if self.span:
289
+ if config.appsec_enabled and not self.blocking_response:
290
+ asm_start_response(
291
+ self.span,
292
+ status_code,
293
+ self.event_source,
294
+ response=self.response,
295
+ )
296
+ self.blocking_response = get_asm_blocked_response(self.event_source)
297
+
298
+ if self.blocking_response:
299
+ status_code = str(self.blocking_response.get("statusCode"))
300
+ if config.capture_payload_enabled and self.response:
301
+ tag_object(
302
+ self.span, "function.blocked_response", self.response
303
+ )
304
+ self.response = self.blocking_response
305
+
306
+ if config.capture_payload_enabled:
307
+ tag_object(self.span, "function.request", event)
308
+ tag_object(self.span, "function.response", self.response)
309
+
310
+ if status_code:
311
+ self.span.set_tag("http.status_code", status_code)
312
+
313
+ self.span.finish()
314
+
267
315
  if status_code:
268
316
  self.trigger_tags["http.status_code"] = status_code
269
317
  mark_trace_as_error_for_5xx_responses(context, status_code, self.span)
@@ -278,28 +326,13 @@ class _LambdaDecorator(object):
278
326
  if should_trace_cold_start:
279
327
  trace_ctx = tracer.current_trace_context()
280
328
 
281
- if self.span:
282
- if config.capture_payload_enabled:
283
- tag_object(self.span, "function.request", event)
284
- tag_object(self.span, "function.response", self.response)
285
-
286
- if status_code:
287
- self.span.set_tag("http.status_code", status_code)
288
-
289
- if config.appsec_enabled:
290
- asm_start_response(
291
- self.span,
292
- status_code,
293
- self.event_source,
294
- response=self.response,
295
- )
296
-
297
- self.span.finish()
298
-
299
329
  if self.inferred_span:
300
330
  if status_code:
301
331
  self.inferred_span.set_tag("http.status_code", status_code)
302
332
 
333
+ if self.trigger_tags and (route := self.trigger_tags.get("http.route")):
334
+ self.inferred_span.set_tag("http.route", route)
335
+
303
336
  if config.service:
304
337
  self.inferred_span.set_tag("peer.service", config.service)
305
338
 
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "datadog_lambda"
3
- version = "7.112.0"
3
+ version = "8.114.0"
4
4
  description = "The Datadog AWS Lambda Library"
5
5
  authors = ["Datadog, Inc. <dev@datadoghq.com>"]
6
6
  license = "Apache-2.0"
@@ -28,7 +28,7 @@ classifiers = [
28
28
  python = ">=3.8.0,<4"
29
29
  datadog = ">=0.51.0,<1.0.0"
30
30
  wrapt = "^1.11.2"
31
- ddtrace = ">=3.10.2,<4"
31
+ ddtrace = ">=3.11.0,<4"
32
32
  ujson = ">=5.9.0"
33
33
  botocore = { version = "^1.34.0", optional = true }
34
34
  requests = { version ="^2.22.0", optional = true }
@@ -1 +0,0 @@
1
- __version__ = "7.112.0"