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/__init__.py +2 -6
- datadog_lambda/cold_start.py +6 -2
- datadog_lambda/extension.py +2 -4
- datadog_lambda/handler.py +1 -1
- datadog_lambda/metric.py +11 -15
- datadog_lambda/patch.py +3 -3
- datadog_lambda/tag_object.py +4 -4
- datadog_lambda/tags.py +19 -48
- datadog_lambda/tracing.py +189 -176
- datadog_lambda/trigger.py +39 -35
- datadog_lambda/version.py +1 -0
- datadog_lambda/wrapper.py +8 -13
- datadog_lambda/xray.py +45 -34
- datadog_lambda-5.93.0.dist-info/LICENSE-3rdparty.csv +13 -0
- {datadog_lambda-5.91.0.dist-info → datadog_lambda-5.93.0.dist-info}/METADATA +3 -3
- datadog_lambda-5.93.0.dist-info/RECORD +27 -0
- datadog_lambda-5.91.0.dist-info/LICENSE-3rdparty.csv +0 -3
- datadog_lambda-5.91.0.dist-info/RECORD +0 -26
- {datadog_lambda-5.91.0.dist-info → datadog_lambda-5.93.0.dist-info}/LICENSE +0 -0
- {datadog_lambda-5.91.0.dist-info → datadog_lambda-5.93.0.dist-info}/NOTICE +0 -0
- {datadog_lambda-5.91.0.dist-info → datadog_lambda-5.93.0.dist-info}/WHEEL +0 -0
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:
|
|
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
|
-
|
|
248
|
-
first_record.get("Sns"
|
|
249
|
-
|
|
250
|
-
dd_payload = msg_attributes.get("_datadog"
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
dd_payload.get("
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
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
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
408
|
-
authorizer_headers.get("lambda"
|
|
409
|
-
if
|
|
410
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
613
|
-
|
|
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
|
-
|
|
710
|
-
|
|
711
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
951
|
+
http = request_context.get("http") or {}
|
|
952
|
+
method = http.get("method")
|
|
923
953
|
path = event.get("rawPath")
|
|
924
|
-
|
|
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":
|
|
930
|
-
"http.protocol":
|
|
931
|
-
"http.source_ip":
|
|
932
|
-
"http.user_agent":
|
|
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
|
-
|
|
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(
|
|
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":
|
|
1008
|
+
"sender_id": attrs.get("SenderId"),
|
|
980
1009
|
}
|
|
981
1010
|
InferredSpanInfo.set_tags(tags, tag_source="self", synchronicity="async")
|
|
982
|
-
request_time_epoch =
|
|
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"
|
|
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(
|
|
1024
|
-
span = tracer.trace(
|
|
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 =
|
|
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
|
-
|
|
1053
|
-
|
|
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 =
|
|
1084
|
+
timestamp = sns_message.get("Timestamp")
|
|
1058
1085
|
dt = datetime.strptime(timestamp, sns_dt_format)
|
|
1059
1086
|
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
"
|
|
1063
|
-
|
|
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":
|
|
1116
|
+
"partition_key": kinesis.get("partitionKey"),
|
|
1092
1117
|
}
|
|
1093
1118
|
InferredSpanInfo.set_tags(tags, tag_source="self", synchronicity="async")
|
|
1094
|
-
request_time_epoch =
|
|
1095
|
-
"approximateArrivalTimestamp"
|
|
1096
|
-
)
|
|
1119
|
+
request_time_epoch = kinesis.get("approximateArrivalTimestamp")
|
|
1097
1120
|
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
"
|
|
1101
|
-
|
|
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 =
|
|
1132
|
-
|
|
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
|
-
|
|
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":
|
|
1160
|
-
"object_key":
|
|
1161
|
-
"object_size": str(
|
|
1162
|
-
"object_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
|
-
|
|
1170
|
-
|
|
1171
|
-
"
|
|
1172
|
-
|
|
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
|
-
|
|
1202
|
-
|
|
1203
|
-
"
|
|
1204
|
-
|
|
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
|
-
|
|
1249
|
-
|
|
1250
|
-
"
|
|
1251
|
-
"
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
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:
|