datadog_lambda 5.91.0__py3-none-any.whl → 5.93.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/tracing.py CHANGED
@@ -5,8 +5,8 @@
5
5
  import hashlib
6
6
  import logging
7
7
  import os
8
- import json
9
8
  import base64
9
+ import ujson as json
10
10
  from datetime import datetime, timezone
11
11
  from typing import Optional, Dict
12
12
 
@@ -66,8 +66,12 @@ if dd_tracing_enabled:
66
66
 
67
67
  telemetry_writer.enable()
68
68
 
69
+ is_lambda_context = os.environ.get(XrayDaemon.FUNCTION_NAME_HEADER_NAME) != ""
70
+
69
71
  propagator = HTTPPropagator()
70
72
 
73
+ DD_TRACE_JAVA_TRACE_ID_PADDING = "00000000"
74
+
71
75
 
72
76
  def _convert_xray_trace_id(xray_trace_id):
73
77
  """
@@ -91,7 +95,7 @@ def _convert_xray_sampling(xray_sampled):
91
95
 
92
96
 
93
97
  def _get_xray_trace_context():
94
- if not is_lambda_context():
98
+ if not is_lambda_context:
95
99
  return None
96
100
 
97
101
  xray_trace_entity = parse_xray_header(
@@ -107,11 +111,7 @@ def _get_xray_trace_context():
107
111
  logger.debug(
108
112
  "Converted trace context %s from X-Ray segment %s",
109
113
  trace_context,
110
- (
111
- xray_trace_entity["trace_id"],
112
- xray_trace_entity["parent_id"],
113
- xray_trace_entity["sampled"],
114
- ),
114
+ xray_trace_entity,
115
115
  )
116
116
  return trace_context
117
117
 
@@ -122,7 +122,9 @@ def _get_dd_trace_py_context():
122
122
  return None
123
123
 
124
124
  logger.debug(
125
- "found dd trace context: %s", (span.context.trace_id, span.context.span_id)
125
+ "found dd trace context: trace_id=%s span_id=%s",
126
+ span.context.trace_id,
127
+ span.context.span_id,
126
128
  )
127
129
  return span.context
128
130
 
@@ -233,43 +235,63 @@ def extract_context_from_sqs_or_sns_event_or_context(event, lambda_context):
233
235
 
234
236
  # logic to deal with SNS => SQS event
235
237
  if "body" in first_record:
236
- body_str = first_record.get("body", {})
238
+ body_str = first_record.get("body")
237
239
  try:
238
240
  body = json.loads(body_str)
239
241
  if body.get("Type", "") == "Notification" and "TopicArn" in body:
240
242
  logger.debug("Found SNS message inside SQS event")
241
243
  first_record = get_first_record(create_sns_event(body))
242
244
  except Exception:
243
- first_record = event.get("Records")[0]
244
245
  pass
245
246
 
246
- msg_attributes = first_record.get(
247
- "messageAttributes",
248
- first_record.get("Sns", {}).get("MessageAttributes", {}),
249
- )
250
- dd_payload = msg_attributes.get("_datadog", {})
251
- # SQS uses dataType and binaryValue/stringValue
252
- # SNS uses Type and Value
253
- dd_json_data_type = dd_payload.get("Type", dd_payload.get("dataType", ""))
254
- if dd_json_data_type == "Binary":
255
- dd_json_data = dd_payload.get(
256
- "binaryValue",
257
- dd_payload.get("Value", r"{}"),
258
- )
259
- dd_json_data = base64.b64decode(dd_json_data)
260
- elif dd_json_data_type == "String":
261
- dd_json_data = dd_payload.get(
262
- "stringValue",
263
- dd_payload.get("Value", r"{}"),
264
- )
247
+ msg_attributes = first_record.get("messageAttributes")
248
+ if msg_attributes is None:
249
+ sns_record = first_record.get("Sns") or {}
250
+ msg_attributes = sns_record.get("MessageAttributes") or {}
251
+ dd_payload = msg_attributes.get("_datadog")
252
+ if dd_payload:
253
+ # SQS uses dataType and binaryValue/stringValue
254
+ # SNS uses Type and Value
255
+ dd_json_data = None
256
+ dd_json_data_type = dd_payload.get("Type") or dd_payload.get("dataType")
257
+ if dd_json_data_type == "Binary":
258
+ dd_json_data = dd_payload.get("binaryValue") or dd_payload.get("Value")
259
+ if dd_json_data:
260
+ dd_json_data = base64.b64decode(dd_json_data)
261
+ elif dd_json_data_type == "String":
262
+ dd_json_data = dd_payload.get("stringValue") or dd_payload.get("Value")
263
+ else:
264
+ logger.debug(
265
+ "Datadog Lambda Python only supports extracting trace"
266
+ "context from String or Binary SQS/SNS message attributes"
267
+ )
268
+
269
+ if dd_json_data:
270
+ dd_data = json.loads(dd_json_data)
271
+ return propagator.extract(dd_data)
265
272
  else:
266
- logger.debug(
267
- "Datadog Lambda Python only supports extracting trace"
268
- "context from String or Binary SQS/SNS message attributes"
269
- )
270
- return extract_context_from_lambda_context(lambda_context)
271
- dd_data = json.loads(dd_json_data)
272
- return propagator.extract(dd_data)
273
+ # Handle case where trace context is injected into attributes.AWSTraceHeader
274
+ # example: Root=1-654321ab-000000001234567890abcdef;Parent=0123456789abcdef;Sampled=1
275
+ attrs = first_record.get("attributes")
276
+ if attrs:
277
+ x_ray_header = attrs.get("AWSTraceHeader")
278
+ if x_ray_header:
279
+ x_ray_context = parse_xray_header(x_ray_header)
280
+ trace_id_parts = x_ray_context.get("trace_id", "").split("-")
281
+ if len(trace_id_parts) > 2 and trace_id_parts[2].startswith(
282
+ DD_TRACE_JAVA_TRACE_ID_PADDING
283
+ ):
284
+ # If it starts with eight 0's padding,
285
+ # then this AWSTraceHeader contains Datadog injected trace context
286
+ logger.debug(
287
+ "Found dd-trace injected trace context from AWSTraceHeader"
288
+ )
289
+ return Context(
290
+ trace_id=int(trace_id_parts[2][8:], 16),
291
+ span_id=int(x_ray_context["parent_id"], 16),
292
+ sampling_priority=float(x_ray_context["sampled"]),
293
+ )
294
+ return extract_context_from_lambda_context(lambda_context)
273
295
  except Exception as e:
274
296
  logger.debug("The trace extractor returned with error %s", e)
275
297
  return extract_context_from_lambda_context(lambda_context)
@@ -313,21 +335,22 @@ def extract_context_from_kinesis_event(event, lambda_context):
313
335
  """
314
336
  try:
315
337
  record = get_first_record(event)
316
- data = record.get("kinesis", {}).get("data", None)
338
+ kinesis = record.get("kinesis")
339
+ if not kinesis:
340
+ return extract_context_from_lambda_context(lambda_context)
341
+ data = kinesis.get("data")
317
342
  if data:
318
343
  b64_bytes = data.encode("ascii")
319
344
  str_bytes = base64.b64decode(b64_bytes)
320
345
  data_str = str_bytes.decode("ascii")
321
346
  data_obj = json.loads(data_str)
322
347
  dd_ctx = data_obj.get("_datadog")
323
-
324
- if not dd_ctx:
325
- return extract_context_from_lambda_context(lambda_context)
326
-
327
- return propagator.extract(dd_ctx)
348
+ if dd_ctx:
349
+ return propagator.extract(dd_ctx)
328
350
  except Exception as e:
329
351
  logger.debug("The trace extractor returned with error %s", e)
330
- return extract_context_from_lambda_context(lambda_context)
352
+
353
+ return extract_context_from_lambda_context(lambda_context)
331
354
 
332
355
 
333
356
  def _deterministic_md5_hash(s: str) -> int:
@@ -354,7 +377,7 @@ def extract_context_from_step_functions(event, lambda_context):
354
377
  state_entered_time = event.get("State").get("EnteredTime")
355
378
  trace_id = _deterministic_md5_hash(execution_id)
356
379
  parent_id = _deterministic_md5_hash(
357
- execution_id + "#" + state_name + "#" + state_entered_time
380
+ f"{execution_id}#{state_name}#{state_entered_time}"
358
381
  )
359
382
  sampling_priority = SamplingPriority.AUTO_KEEP
360
383
  return Context(
@@ -370,11 +393,7 @@ def extract_context_custom_extractor(extractor, event, lambda_context):
370
393
  Extract Datadog trace context using a custom trace extractor function
371
394
  """
372
395
  try:
373
- (
374
- trace_id,
375
- parent_id,
376
- sampling_priority,
377
- ) = extractor(event, lambda_context)
396
+ trace_id, parent_id, sampling_priority = extractor(event, lambda_context)
378
397
  return Context(
379
398
  trace_id=int(trace_id),
380
399
  span_id=int(parent_id),
@@ -400,15 +419,20 @@ def is_authorizer_response(response) -> bool:
400
419
 
401
420
  def get_injected_authorizer_data(event, is_http_api) -> dict:
402
421
  try:
403
- authorizer_headers = event.get("requestContext", {}).get("authorizer")
422
+ req_ctx = event.get("requestContext")
423
+ if not req_ctx:
424
+ return None
425
+ authorizer_headers = req_ctx.get("authorizer")
404
426
  if not authorizer_headers:
405
427
  return None
406
428
 
407
- dd_data_raw = (
408
- authorizer_headers.get("lambda", {}).get("_datadog")
409
- if is_http_api
410
- else authorizer_headers.get("_datadog")
411
- )
429
+ if is_http_api:
430
+ lambda_hdr = authorizer_headers.get("lambda")
431
+ if not lambda_hdr:
432
+ return None
433
+ dd_data_raw = lambda_hdr.get("_datadog")
434
+ else:
435
+ dd_data_raw = authorizer_headers.get("_datadog")
412
436
 
413
437
  if not dd_data_raw:
414
438
  return None
@@ -422,16 +446,19 @@ def get_injected_authorizer_data(event, is_http_api) -> dict:
422
446
  # that case, we use the injected Authorizing_Request_Id to tell if it's cached. But token
423
447
  # authorizers don't pass on the requestId. The Authorizing_Request_Id can't work for all
424
448
  # cases neither. As a result, we combine both methods as shown below.
425
- if authorizer_headers.get("integrationLatency", 0) > 0 or event.get(
426
- "requestContext", {}
427
- ).get("requestId") == injected_data.get(Headers.Authorizing_Request_Id):
449
+ if authorizer_headers.get("integrationLatency", 0) > 0:
428
450
  return injected_data
429
- else:
451
+ req_ctx = event.get("requestContext")
452
+ if not req_ctx:
430
453
  return None
454
+ if req_ctx.get("requestId") == injected_data.get(
455
+ Headers.Authorizing_Request_Id
456
+ ):
457
+ return injected_data
458
+ return None
431
459
 
432
460
  except Exception as e:
433
461
  logger.debug("Failed to check if invocated by an authorizer. error %s", e)
434
- return None
435
462
 
436
463
 
437
464
  def extract_dd_trace_context(
@@ -503,8 +530,8 @@ def get_dd_trace_context_obj():
503
530
  xray_context = _get_xray_trace_context() # xray (sub)segment
504
531
  except Exception as e:
505
532
  logger.debug(
506
- "get_dd_trace_context couldn't read from segment from x-ray, with error %s"
507
- % e
533
+ "get_dd_trace_context couldn't read from segment from x-ray, with error %s",
534
+ e,
508
535
  )
509
536
  if not xray_context:
510
537
  return None
@@ -543,7 +570,7 @@ def set_correlation_ids():
543
570
 
544
571
  TODO: Remove me when Datadog tracer is natively supported in Lambda.
545
572
  """
546
- if not is_lambda_context():
573
+ if not is_lambda_context:
547
574
  logger.debug("set_correlation_ids is only supported in LambdaContext")
548
575
  return
549
576
  if dd_tracing_enabled:
@@ -587,14 +614,6 @@ def inject_correlation_ids():
587
614
  logger.debug("logs injection configured")
588
615
 
589
616
 
590
- def is_lambda_context():
591
- """
592
- Return True if the X-Ray context is `LambdaContext`, rather than the
593
- regular `Context` (e.g., when testing lambda functions locally).
594
- """
595
- return os.environ.get(XrayDaemon.FUNCTION_NAME_HEADER_NAME, "") != ""
596
-
597
-
598
617
  def set_dd_trace_py_root(trace_context_source, merge_xray_traces):
599
618
  if trace_context_source == TraceContextSource.EVENT or merge_xray_traces:
600
619
  context = Context(
@@ -609,8 +628,9 @@ def set_dd_trace_py_root(trace_context_source, merge_xray_traces):
609
628
 
610
629
  tracer.context_provider.activate(context)
611
630
  logger.debug(
612
- "Set dd trace root context to: %s",
613
- (context.trace_id, context.span_id),
631
+ "Set dd trace root context to: trace_id=%s span_id=%s",
632
+ context.trace_id,
633
+ context.span_id,
614
634
  )
615
635
 
616
636
 
@@ -671,9 +691,7 @@ def create_inferred_span(
671
691
  event_source.to_string(),
672
692
  e,
673
693
  )
674
- return None
675
694
  logger.debug("Unable to infer a span: unknown event type")
676
- return None
677
695
 
678
696
 
679
697
  def create_service_mapping(val):
@@ -695,20 +713,22 @@ def determine_service_name(service_mapping, specific_key, generic_key, default_v
695
713
  return service_name
696
714
 
697
715
 
698
- service_mapping = {}
699
716
  # Initialization code
700
717
  service_mapping_str = os.getenv("DD_SERVICE_MAPPING", "")
701
718
  service_mapping = create_service_mapping(service_mapping_str)
702
719
 
720
+ _dd_origin = {"_dd.origin": "lambda"}
721
+
703
722
 
704
723
  def create_inferred_span_from_lambda_function_url_event(event, context):
705
724
  request_context = event.get("requestContext")
706
725
  api_id = request_context.get("apiId")
707
726
  domain = request_context.get("domainName")
708
727
  service_name = determine_service_name(service_mapping, api_id, "lambda_url", domain)
709
- method = request_context.get("http", {}).get("method")
710
- path = request_context.get("http", {}).get("path")
711
- resource = "{0} {1}".format(method, path)
728
+ http = request_context.get("http")
729
+ method = http.get("method") if http else None
730
+ path = http.get("path") if http else None
731
+ resource = f"{method} {path}"
712
732
  tags = {
713
733
  "operation_name": "aws.lambda.url",
714
734
  "http.url": domain + path,
@@ -718,25 +738,23 @@ def create_inferred_span_from_lambda_function_url_event(event, context):
718
738
  "request_id": context.aws_request_id,
719
739
  }
720
740
  request_time_epoch = request_context.get("timeEpoch")
721
- args = {
722
- "service": service_name,
723
- "resource": resource,
724
- "span_type": "http",
725
- }
726
- tracer.set_tags(
727
- {"_dd.origin": "lambda"}
728
- ) # function urls don't count as lambda_inferred,
741
+ tracer.set_tags(_dd_origin) # function urls don't count as lambda_inferred,
729
742
  # because they're in the same service as the inferring lambda function
730
- span = tracer.trace("aws.lambda.url", **args)
743
+ span = tracer.trace(
744
+ "aws.lambda.url", service=service_name, resource=resource, span_type="http"
745
+ )
731
746
  InferredSpanInfo.set_tags(tags, tag_source="self", synchronicity="sync")
732
747
  if span:
733
748
  span.set_tags(tags)
734
- span.start = request_time_epoch / 1000
749
+ span.start_ns = int(request_time_epoch) * 1e6
735
750
  return span
736
751
 
737
752
 
738
753
  def is_api_gateway_invocation_async(event):
739
- return event.get("headers", {}).get("X-Amz-Invocation-Type") == "Event"
754
+ hdrs = event.get("headers")
755
+ if not hdrs:
756
+ return False
757
+ return hdrs.get("X-Amz-Invocation-Type") == "Event"
740
758
 
741
759
 
742
760
  def insert_upstream_authorizer_span(
@@ -836,7 +854,7 @@ def create_inferred_span_from_api_gateway_websocket_event(
836
854
  "resource": endpoint,
837
855
  "span_type": "web",
838
856
  }
839
- tracer.set_tags({"_dd.origin": "lambda"})
857
+ tracer.set_tags(_dd_origin)
840
858
  upstream_authorizer_span = None
841
859
  finish_time_ns = None
842
860
  if decode_authorizer_context:
@@ -867,7 +885,8 @@ def create_inferred_span_from_api_gateway_event(
867
885
  )
868
886
  method = event.get("httpMethod")
869
887
  path = event.get("path")
870
- resource = "{0} {1}".format(method, path)
888
+ resource_path = _get_resource_path(event, request_context)
889
+ resource = f"{method} {resource_path}"
871
890
  tags = {
872
891
  "operation_name": "aws.apigateway.rest",
873
892
  "http.url": domain + path,
@@ -889,7 +908,7 @@ def create_inferred_span_from_api_gateway_event(
889
908
  "resource": resource,
890
909
  "span_type": "http",
891
910
  }
892
- tracer.set_tags({"_dd.origin": "lambda"})
911
+ tracer.set_tags(_dd_origin)
893
912
  upstream_authorizer_span = None
894
913
  finish_time_ns = None
895
914
  if decode_authorizer_context:
@@ -910,6 +929,16 @@ def create_inferred_span_from_api_gateway_event(
910
929
  return span
911
930
 
912
931
 
932
+ def _get_resource_path(event, request_context):
933
+ route_key = request_context.get("routeKey") or ""
934
+ if "{" in route_key:
935
+ try:
936
+ return route_key.split(" ")[1]
937
+ except Exception as e:
938
+ logger.debug("Error parsing routeKey: %s", e)
939
+ return event.get("rawPath") or request_context.get("resourcePath") or route_key
940
+
941
+
913
942
  def create_inferred_span_from_http_api_event(
914
943
  event, context, decode_authorizer_context: bool = True
915
944
  ):
@@ -919,17 +948,19 @@ def create_inferred_span_from_http_api_event(
919
948
  service_name = determine_service_name(
920
949
  service_mapping, api_id, "lambda_api_gateway", domain
921
950
  )
922
- method = request_context.get("http", {}).get("method")
951
+ http = request_context.get("http") or {}
952
+ method = http.get("method")
923
953
  path = event.get("rawPath")
924
- resource = "{0} {1}".format(method, path)
954
+ resource_path = _get_resource_path(event, request_context)
955
+ resource = f"{method} {resource_path}"
925
956
  tags = {
926
957
  "operation_name": "aws.httpapi",
927
958
  "endpoint": path,
928
959
  "http.url": domain + path,
929
- "http.method": request_context.get("http", {}).get("method"),
930
- "http.protocol": request_context.get("http", {}).get("protocol"),
931
- "http.source_ip": request_context.get("http", {}).get("sourceIp"),
932
- "http.user_agent": request_context.get("http", {}).get("userAgent"),
960
+ "http.method": http.get("method"),
961
+ "http.protocol": http.get("protocol"),
962
+ "http.source_ip": http.get("sourceIp"),
963
+ "http.user_agent": http.get("userAgent"),
933
964
  "resource_names": resource,
934
965
  "request_id": context.aws_request_id,
935
966
  "apiid": api_id,
@@ -941,12 +972,7 @@ def create_inferred_span_from_http_api_event(
941
972
  InferredSpanInfo.set_tags(tags, tag_source="self", synchronicity="async")
942
973
  else:
943
974
  InferredSpanInfo.set_tags(tags, tag_source="self", synchronicity="sync")
944
- args = {
945
- "service": service_name,
946
- "resource": resource,
947
- "span_type": "http",
948
- }
949
- tracer.set_tags({"_dd.origin": "lambda"})
975
+ tracer.set_tags(_dd_origin)
950
976
  inferred_span_start_ns = request_time_epoch_ms * 1e6
951
977
  if decode_authorizer_context:
952
978
  injected_authorizer_data = get_injected_authorizer_data(event, True)
@@ -954,7 +980,9 @@ def create_inferred_span_from_http_api_event(
954
980
  inferred_span_start_ns = injected_authorizer_data.get(
955
981
  Headers.Parent_Span_Finish_Time
956
982
  )
957
- span = tracer.trace("aws.httpapi", **args)
983
+ span = tracer.trace(
984
+ "aws.httpapi", service=service_name, resource=resource, span_type="http"
985
+ )
958
986
  if span:
959
987
  span.set_tags(tags)
960
988
  span.start_ns = int(inferred_span_start_ns)
@@ -970,21 +998,17 @@ def create_inferred_span_from_sqs_event(event, context):
970
998
  service_name = determine_service_name(
971
999
  service_mapping, queue_name, "lambda_sqs", "sqs"
972
1000
  )
1001
+ attrs = event_record.get("attributes") or {}
973
1002
  tags = {
974
1003
  "operation_name": "aws.sqs",
975
1004
  "resource_names": queue_name,
976
1005
  "queuename": queue_name,
977
1006
  "event_source_arn": event_source_arn,
978
1007
  "receipt_handle": event_record.get("receiptHandle"),
979
- "sender_id": event_record.get("attributes", {}).get("SenderId"),
1008
+ "sender_id": attrs.get("SenderId"),
980
1009
  }
981
1010
  InferredSpanInfo.set_tags(tags, tag_source="self", synchronicity="async")
982
- request_time_epoch = event_record.get("attributes", {}).get("SentTimestamp")
983
- args = {
984
- "service": service_name,
985
- "resource": queue_name,
986
- "span_type": "web",
987
- }
1011
+ request_time_epoch = attrs.get("SentTimestamp")
988
1012
  start_time = int(request_time_epoch) / 1000
989
1013
 
990
1014
  upstream_span = None
@@ -1013,15 +1037,17 @@ def create_inferred_span_from_sqs_event(event, context):
1013
1037
 
1014
1038
  except Exception as e:
1015
1039
  logger.debug(
1016
- "Unable to create upstream span from SQS message, with error %s" % e
1040
+ "Unable to create upstream span from SQS message, with error %s", e
1017
1041
  )
1018
1042
  pass
1019
1043
 
1020
1044
  # trace context needs to be set again as it is reset
1021
1045
  # when sns_span.finish executes
1022
1046
  tracer.context_provider.activate(trace_ctx)
1023
- tracer.set_tags({"_dd.origin": "lambda"})
1024
- span = tracer.trace("aws.sqs", **args)
1047
+ tracer.set_tags(_dd_origin)
1048
+ span = tracer.trace(
1049
+ "aws.sqs", service=service_name, resource=queue_name, span_type="web"
1050
+ )
1025
1051
  if span:
1026
1052
  span.set_tags(tags)
1027
1053
  span.start = start_time
@@ -1033,8 +1059,8 @@ def create_inferred_span_from_sqs_event(event, context):
1033
1059
 
1034
1060
  def create_inferred_span_from_sns_event(event, context):
1035
1061
  event_record = get_first_record(event)
1036
- sns_message = event_record.get("Sns")
1037
- topic_arn = event_record.get("Sns", {}).get("TopicArn")
1062
+ sns_message = event_record.get("Sns") or {}
1063
+ topic_arn = sns_message.get("TopicArn")
1038
1064
  topic_name = topic_arn.split(":")[-1]
1039
1065
  service_name = determine_service_name(
1040
1066
  service_mapping, topic_name, "lambda_sns", "sns"
@@ -1049,21 +1075,19 @@ def create_inferred_span_from_sns_event(event, context):
1049
1075
  }
1050
1076
 
1051
1077
  # Subject not available in SNS => SQS scenario
1052
- if "Subject" in sns_message and sns_message["Subject"]:
1053
- tags["subject"] = sns_message.get("Subject")
1078
+ subject = sns_message.get("Subject")
1079
+ if subject:
1080
+ tags["subject"] = subject
1054
1081
 
1055
1082
  InferredSpanInfo.set_tags(tags, tag_source="self", synchronicity="async")
1056
1083
  sns_dt_format = "%Y-%m-%dT%H:%M:%S.%fZ"
1057
- timestamp = event_record.get("Sns", {}).get("Timestamp")
1084
+ timestamp = sns_message.get("Timestamp")
1058
1085
  dt = datetime.strptime(timestamp, sns_dt_format)
1059
1086
 
1060
- args = {
1061
- "service": service_name,
1062
- "resource": topic_name,
1063
- "span_type": "web",
1064
- }
1065
- tracer.set_tags({"_dd.origin": "lambda"})
1066
- span = tracer.trace("aws.sns", **args)
1087
+ tracer.set_tags(_dd_origin)
1088
+ span = tracer.trace(
1089
+ "aws.sns", service=service_name, resource=topic_name, span_type="web"
1090
+ )
1067
1091
  if span:
1068
1092
  span.set_tags(tags)
1069
1093
  span.start = dt.replace(tzinfo=timezone.utc).timestamp()
@@ -1079,6 +1103,7 @@ def create_inferred_span_from_kinesis_event(event, context):
1079
1103
  service_name = determine_service_name(
1080
1104
  service_mapping, stream_name, "lambda_kinesis", "kinesis"
1081
1105
  )
1106
+ kinesis = event_record.get("kinesis") or {}
1082
1107
  tags = {
1083
1108
  "operation_name": "aws.kinesis",
1084
1109
  "resource_names": stream_name,
@@ -1088,20 +1113,15 @@ def create_inferred_span_from_kinesis_event(event, context):
1088
1113
  "event_id": event_id,
1089
1114
  "event_name": event_record.get("eventName"),
1090
1115
  "event_version": event_record.get("eventVersion"),
1091
- "partition_key": event_record.get("kinesis", {}).get("partitionKey"),
1116
+ "partition_key": kinesis.get("partitionKey"),
1092
1117
  }
1093
1118
  InferredSpanInfo.set_tags(tags, tag_source="self", synchronicity="async")
1094
- request_time_epoch = event_record.get("kinesis", {}).get(
1095
- "approximateArrivalTimestamp"
1096
- )
1119
+ request_time_epoch = kinesis.get("approximateArrivalTimestamp")
1097
1120
 
1098
- args = {
1099
- "service": service_name,
1100
- "resource": stream_name,
1101
- "span_type": "web",
1102
- }
1103
- tracer.set_tags({"_dd.origin": "lambda"})
1104
- span = tracer.trace("aws.kinesis", **args)
1121
+ tracer.set_tags(_dd_origin)
1122
+ span = tracer.trace(
1123
+ "aws.kinesis", service=service_name, resource=stream_name, span_type="web"
1124
+ )
1105
1125
  if span:
1106
1126
  span.set_tags(tags)
1107
1127
  span.start = request_time_epoch
@@ -1115,7 +1135,7 @@ def create_inferred_span_from_dynamodb_event(event, context):
1115
1135
  service_name = determine_service_name(
1116
1136
  service_mapping, table_name, "lambda_dynamodb", "dynamodb"
1117
1137
  )
1118
- dynamodb_message = event_record.get("dynamodb")
1138
+ dynamodb_message = event_record.get("dynamodb") or {}
1119
1139
  tags = {
1120
1140
  "operation_name": "aws.dynamodb",
1121
1141
  "resource_names": table_name,
@@ -1128,16 +1148,11 @@ def create_inferred_span_from_dynamodb_event(event, context):
1128
1148
  "size_bytes": str(dynamodb_message.get("SizeBytes")),
1129
1149
  }
1130
1150
  InferredSpanInfo.set_tags(tags, synchronicity="async", tag_source="self")
1131
- request_time_epoch = event_record.get("dynamodb", {}).get(
1132
- "ApproximateCreationDateTime"
1151
+ request_time_epoch = dynamodb_message.get("ApproximateCreationDateTime")
1152
+ tracer.set_tags(_dd_origin)
1153
+ span = tracer.trace(
1154
+ "aws.dynamodb", service=service_name, resource=table_name, span_type="web"
1133
1155
  )
1134
- args = {
1135
- "service": service_name,
1136
- "resource": table_name,
1137
- "span_type": "web",
1138
- }
1139
- tracer.set_tags({"_dd.origin": "lambda"})
1140
- span = tracer.trace("aws.dynamodb", **args)
1141
1156
  if span:
1142
1157
  span.set_tags(tags)
1143
1158
 
@@ -1147,7 +1162,10 @@ def create_inferred_span_from_dynamodb_event(event, context):
1147
1162
 
1148
1163
  def create_inferred_span_from_s3_event(event, context):
1149
1164
  event_record = get_first_record(event)
1150
- bucket_name = event_record.get("s3", {}).get("bucket", {}).get("name")
1165
+ s3 = event_record.get("s3") or {}
1166
+ bucket = s3.get("bucket") or {}
1167
+ obj = s3.get("object") or {}
1168
+ bucket_name = bucket.get("name")
1151
1169
  service_name = determine_service_name(
1152
1170
  service_mapping, bucket_name, "lambda_s3", "s3"
1153
1171
  )
@@ -1156,23 +1174,20 @@ def create_inferred_span_from_s3_event(event, context):
1156
1174
  "resource_names": bucket_name,
1157
1175
  "event_name": event_record.get("eventName"),
1158
1176
  "bucketname": bucket_name,
1159
- "bucket_arn": event_record.get("s3", {}).get("bucket", {}).get("arn"),
1160
- "object_key": event_record.get("s3", {}).get("object", {}).get("key"),
1161
- "object_size": str(event_record.get("s3", {}).get("object", {}).get("size")),
1162
- "object_etag": event_record.get("s3", {}).get("object", {}).get("eTag"),
1177
+ "bucket_arn": bucket.get("arn"),
1178
+ "object_key": obj.get("key"),
1179
+ "object_size": str(obj.get("size")),
1180
+ "object_etag": obj.get("eTag"),
1163
1181
  }
1164
1182
  InferredSpanInfo.set_tags(tags, synchronicity="async", tag_source="self")
1165
1183
  dt_format = "%Y-%m-%dT%H:%M:%S.%fZ"
1166
1184
  timestamp = event_record.get("eventTime")
1167
1185
  dt = datetime.strptime(timestamp, dt_format)
1168
1186
 
1169
- args = {
1170
- "service": service_name,
1171
- "resource": bucket_name,
1172
- "span_type": "web",
1173
- }
1174
- tracer.set_tags({"_dd.origin": "lambda"})
1175
- span = tracer.trace("aws.s3", **args)
1187
+ tracer.set_tags(_dd_origin)
1188
+ span = tracer.trace(
1189
+ "aws.s3", service=service_name, resource=bucket_name, span_type="web"
1190
+ )
1176
1191
  if span:
1177
1192
  span.set_tags(tags)
1178
1193
  span.start = dt.replace(tzinfo=timezone.utc).timestamp()
@@ -1198,13 +1213,10 @@ def create_inferred_span_from_eventbridge_event(event, context):
1198
1213
  timestamp = event.get("time")
1199
1214
  dt = datetime.strptime(timestamp, dt_format)
1200
1215
 
1201
- args = {
1202
- "service": service_name,
1203
- "resource": source,
1204
- "span_type": "web",
1205
- }
1206
- tracer.set_tags({"_dd.origin": "lambda"})
1207
- span = tracer.trace("aws.eventbridge", **args)
1216
+ tracer.set_tags(_dd_origin)
1217
+ span = tracer.trace(
1218
+ "aws.eventbridge", service=service_name, resource=source, span_type="web"
1219
+ )
1208
1220
  if span:
1209
1221
  span.set_tags(tags)
1210
1222
  span.start = dt.replace(tzinfo=timezone.utc).timestamp()
@@ -1221,7 +1233,7 @@ def create_function_execution_span(
1221
1233
  trigger_tags,
1222
1234
  parent_span=None,
1223
1235
  ):
1224
- tags = {}
1236
+ tags = None
1225
1237
  if context:
1226
1238
  function_arn = (context.invoked_function_arn or "").lower()
1227
1239
  tk = function_arn.split(":")
@@ -1240,18 +1252,19 @@ def create_function_execution_span(
1240
1252
  "dd_trace": ddtrace_version,
1241
1253
  "span.name": "aws.lambda",
1242
1254
  }
1255
+ tags = tags or {}
1243
1256
  if is_proactive_init:
1244
1257
  tags["proactive_initialization"] = str(is_proactive_init).lower()
1245
1258
  if trace_context_source == TraceContextSource.XRAY and merge_xray_traces:
1246
1259
  tags["_dd.parent_source"] = trace_context_source
1247
1260
  tags.update(trigger_tags)
1248
- args = {
1249
- "service": "aws.lambda",
1250
- "resource": function_name,
1251
- "span_type": "serverless",
1252
- }
1253
- tracer.set_tags({"_dd.origin": "lambda"})
1254
- span = tracer.trace("aws.lambda", **args)
1261
+ tracer.set_tags(_dd_origin)
1262
+ span = tracer.trace(
1263
+ "aws.lambda",
1264
+ service="aws.lambda",
1265
+ resource=function_name,
1266
+ span_type="serverless",
1267
+ )
1255
1268
  if span:
1256
1269
  span.set_tags(tags)
1257
1270
  if parent_span: