Description
Expected Behavior
When a lambda is invoked with an event that is not an object (e.g. an array or value type) the instrumentation should handle this case and continue to function as normal.
Actual Behavior
Datadog wrapper fails with TypeError: argument of type 'NoneType' is not iterable
(when "null" is sent as the Payload) or TypeError: argument of type 'int' is not iterable
(when an integer is sent as the Payload). No logs are sent to Datadog.
Steps to Reproduce the Problem
- Using boto3 create a new
lambda_client
with credentials lambda_client.invoke(FunctionName="foo", InvocationType="RequestResponse", Payload=json.dumps(1))
or json.dumps(None), or any other non-object json.- Review the CloudWatch logs
- Observe no logs being sent to Datadog
Specifications
- Datadog Lambda Layer version: 23
- Python version: 3.9
Stacktrace
# CloudWatch Logs from lambda_client.invoke(FunctionName="foo", InvocationType="RequestResponse", Payload=json.dumps(None)
START RequestId: 3897a891-f42d-427d-860b-b9b5ae9e0959 Version: $LATEST
--
LOGS Name: datadog-agent State: Subscribed Types: [platform,function,extension]
EXTENSION Name: datadog-agent State: Ready Events: [INVOKE,SHUTDOWN]
Traceback (most recent call last):
File "/var/lang/lib/python3.9/site-packages/datadog_lambda/wrapper.py", line 159, in _before
dd_context, trace_context_source = extract_dd_trace_context(
File "/var/lang/lib/python3.9/site-packages/datadog_lambda/tracing.py", line 335, in extract_dd_trace_context
elif "headers" in event:
TypeError: argument of type 'NoneType' is not iterable
END RequestId: 3897a891-f42d-427d-860b-b9b5ae9e0959
REPORT RequestId: 3897a891-f42d-427d-860b-b9b5ae9e0959 Duration: 7675.97 ms Billed Duration: 8237 ms Memory Size: 512 MB Max Memory Used: 101 MB Init Duration: 560.23 ms XRAY TraceId: 1-62b96915-4c10201259b4b43b7ccfa27b SegmentId: 517ac22b2d33298c Sampled: true
The reason I think this is a bug is that the AWS Lambda documentation mentions support for non-object payloads:
The first argument is the event object. An event is a JSON-formatted document that contains data for a Lambda function to process. The Lambda runtime converts the event to an object and passes it to your function code. It is usually of the Python dict type. It can also be list, str, int, float, or the NoneType type.
I think what is happening is extract_dd_trace_context
is assuming a deserialised json object at tracing.py#L335 elif "headers" in event:
.
When the event is not a deserialised object (i.e. not a dict) the in
operator will either fail, or result in strange behaviour, for example in a case where a string or array sent in the payload happens to contain the sequence headers
.
I think issue #225 stems from a similar situation.