Skip to content

Commit aa6da21

Browse files
fix(logger): improve behavior with exc_info=True to prevent errors (#6417)
fix exception logger
1 parent f608b21 commit aa6da21

File tree

4 files changed

+34
-12
lines changed

4 files changed

+34
-12
lines changed

aws_lambda_powertools/logging/formatter.py

+14-8
Original file line numberDiff line numberDiff line change
@@ -357,19 +357,25 @@ def _extract_log_message(self, log_record: logging.LogRecord) -> dict[str, Any]
357357
return message
358358

359359
def _serialize_stacktrace(self, log_record: logging.LogRecord) -> LogStackTrace | None:
360-
if log_record.exc_info:
360+
# Check if the first element of exc_info has the __name__ attribute,
361+
# which indicates it is likely an exception class or object.
362+
# See: https://github.com/aws-powertools/powertools-lambda-python/issues/6358
363+
if isinstance(log_record.exc_info, tuple) and hasattr(log_record.exc_info[0], "__name__"):
361364
exception_info: LogStackTrace = {
362365
"type": log_record.exc_info[0].__name__, # type: ignore
363366
"value": log_record.exc_info[1], # type: ignore
364367
"module": log_record.exc_info[1].__class__.__module__,
365-
"frames": [],
368+
"frames": [
369+
{
370+
"file": fs.filename,
371+
"line": fs.lineno,
372+
"function": fs.name,
373+
"statement": fs.line,
374+
}
375+
for fs in traceback.extract_tb(log_record.exc_info[2])
376+
],
366377
}
367378

368-
exception_info["frames"] = [
369-
{"file": fs.filename, "line": fs.lineno, "function": fs.name, "statement": fs.line}
370-
for fs in traceback.extract_tb(log_record.exc_info[2])
371-
]
372-
373379
return exception_info
374380

375381
return None
@@ -387,7 +393,7 @@ def _extract_log_exception(self, log_record: logging.LogRecord) -> tuple[str, st
387393
log_record: tuple[str, str] | tuple[None, None]
388394
Log record with constant traceback info and exception name
389395
"""
390-
if log_record.exc_info:
396+
if isinstance(log_record.exc_info, tuple) and hasattr(log_record.exc_info[0], "__name__"):
391397
return self.formatException(log_record.exc_info), log_record.exc_info[0].__name__ # type: ignore
392398

393399
return None, None

aws_lambda_powertools/utilities/parser/models/appsync.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
from typing import Optional, List, Dict, Union, Any
1+
from typing import Any, Dict, List, Optional, Union
2+
23
from pydantic import BaseModel
34

5+
46
class AppSyncIamIdentity(BaseModel):
57
accountId: str
68
cognitoIdentityPoolId: Optional[str]
@@ -67,4 +69,4 @@ class AppSyncResolverEventModel(BaseModel):
6769
stash: Dict[str, Any]
6870

6971

70-
AppSyncBatchResolverEventModel = List[AppSyncResolverEventModel]
72+
AppSyncBatchResolverEventModel = List[AppSyncResolverEventModel]

tests/functional/logger/required_dependencies/test_logger.py

+13
Original file line numberDiff line numberDiff line change
@@ -638,6 +638,19 @@ def test_logger_exception_extract_exception_name(stdout, service_name):
638638
assert "ValueError" == log["exception_name"]
639639

640640

641+
def test_logger_exception_should_not_fail_with_exception_block(stdout, service_name):
642+
# GIVEN Logger is initialized
643+
logger = Logger(service=service_name, stream=stdout)
644+
645+
# WHEN calling a logger.exception with a ValueError and outside of a try/except block
646+
logger.exception("Received an exception")
647+
648+
# THEN the log output should not contain "exception_name" or "exception" and not fail
649+
log = capture_logging_output(stdout)
650+
assert "exception_name" not in log
651+
assert "exception" not in log
652+
653+
641654
def test_logger_set_correlation_id(lambda_context, stdout, service_name):
642655
# GIVEN
643656
logger = Logger(service=service_name, stream=stdout)

tests/unit/parser/_pydantic/test_appsync.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import pytest
22

3-
from aws_lambda_powertools.utilities.parser import parse, ValidationError
3+
from aws_lambda_powertools.utilities.parser import ValidationError, parse
44
from aws_lambda_powertools.utilities.parser.models import AppSyncResolverEventModel
55
from tests.functional.utils import load_event
66

7+
78
def test_appsync_event_model_parses_successfully():
89
"""
910
Validate that a valid AppSync resolver event is correctly parsed by the model.
@@ -24,4 +25,4 @@ def test_appsync_event_model_invalid_payload_raises():
2425
"""
2526
invalid_event = {"invalid": "event"}
2627
with pytest.raises(ValidationError):
27-
parse(event=invalid_event, model=AppSyncResolverEventModel)
28+
parse(event=invalid_event, model=AppSyncResolverEventModel)

0 commit comments

Comments
 (0)