Skip to content

Commit

Permalink
Fix AWS Lambda tests (#3495)
Browse files Browse the repository at this point in the history
AWS changed their Lambda run times, so we no longer have access to the current exception during the init phase of the Lambda function.

I am trying to fix this upstream: aws/aws-lambda-python-runtime-interface-client#172

This PR adds a fall back to the errror json object provided by AWS. This has way less data than a real exception in it, but it is better than nothing.

Fixes #3464
  • Loading branch information
antonpirker authored Sep 5, 2024
1 parent e99873d commit 9fc3bd2
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 10 deletions.
62 changes: 62 additions & 0 deletions sentry_sdk/integrations/aws_lambda.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import json
import re
import sys
from copy import deepcopy
from datetime import datetime, timedelta, timezone
Expand Down Expand Up @@ -56,6 +58,11 @@ def sentry_init_error(*args, **kwargs):
)
sentry_sdk.capture_event(sentry_event, hint=hint)

else:
# Fall back to AWS lambdas JSON representation of the error
sentry_event = _event_from_error_json(json.loads(args[1]))
sentry_sdk.capture_event(sentry_event)

return init_error(*args, **kwargs)

return sentry_init_error # type: ignore
Expand Down Expand Up @@ -428,3 +435,58 @@ def _get_cloudwatch_logs_url(aws_context, start_time):
)

return url


def _parse_formatted_traceback(formatted_tb):
# type: (list[str]) -> list[dict[str, Any]]
frames = []
for frame in formatted_tb:
match = re.match(r'File "(.+)", line (\d+), in (.+)', frame.strip())
if match:
file_name, line_number, func_name = match.groups()
line_number = int(line_number)
frames.append(
{
"filename": file_name,
"function": func_name,
"lineno": line_number,
"vars": None,
"pre_context": None,
"context_line": None,
"post_context": None,
}
)
return frames


def _event_from_error_json(error_json):
# type: (dict[str, Any]) -> Event
"""
Converts the error JSON from AWS Lambda into a Sentry error event.
This is not a full fletched event, but better than nothing.
This is an example of where AWS creates the error JSON:
https://github.com/aws/aws-lambda-python-runtime-interface-client/blob/2.2.1/awslambdaric/bootstrap.py#L479
"""
event = {
"level": "error",
"exception": {
"values": [
{
"type": error_json.get("errorType"),
"value": error_json.get("errorMessage"),
"stacktrace": {
"frames": _parse_formatted_traceback(
error_json.get("stackTrace", [])
),
},
"mechanism": {
"type": "aws_lambda",
"handled": False,
},
}
],
},
} # type: Event

return event
21 changes: 11 additions & 10 deletions tests/integrations/aws_lambda/test_aws.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@

import pytest

RUNTIMES_TO_TEST = [
"python3.8",
"python3.9",
"python3.10",
"python3.11",
"python3.12",
]

LAMBDA_PRELUDE = """
from sentry_sdk.integrations.aws_lambda import AwsLambdaIntegration, get_lambda_bootstrap
Expand Down Expand Up @@ -137,15 +144,7 @@ def lambda_client():
return get_boto_client()


@pytest.fixture(
params=[
"python3.8",
"python3.9",
"python3.10",
"python3.11",
"python3.12",
]
)
@pytest.fixture(params=RUNTIMES_TO_TEST)
def lambda_runtime(request):
return request.param

Expand Down Expand Up @@ -331,7 +330,9 @@ def test_init_error(run_lambda_function, lambda_runtime):
syntax_check=False,
)

(event,) = envelope_items
# We just take the last one, because it could be that in the output of the Lambda
# invocation there is still the envelope of the previous invocation of the function.
event = envelope_items[-1]
assert event["exception"]["values"][0]["value"] == "name 'func' is not defined"


Expand Down

0 comments on commit 9fc3bd2

Please sign in to comment.