|
14 | 14 | import sys |
15 | 15 | from abc import ABC, abstractmethod |
16 | 16 | from enum import Enum |
17 | | -from typing import TYPE_CHECKING, Any, Tuple, Union, overload |
| 17 | +from typing import TYPE_CHECKING, Any, Tuple, Union, cast, overload |
18 | 18 |
|
19 | 19 | from aws_lambda_powertools.shared import constants |
20 | 20 | from aws_lambda_powertools.utilities.batch.exceptions import ( |
|
35 | 35 |
|
36 | 36 | if TYPE_CHECKING: |
37 | 37 | from collections.abc import Callable |
| 38 | + from types import TracebackType |
38 | 39 |
|
39 | 40 | from aws_lambda_powertools.logging import Logger |
40 | 41 | from aws_lambda_powertools.utilities.batch.types import ( |
@@ -240,10 +241,19 @@ def failure_handler(self, record, exception: ExceptionInfo) -> FailureResponse: |
240 | 241 | entry = ("fail", exception_string, record) |
241 | 242 | logger.debug(f"Record processing exception: {exception_string}") |
242 | 243 |
|
243 | | - if getattr(self, "logger", None) and exception[2] is not None: |
244 | | - self.logger.warning( |
| 244 | + # Log with full traceback when a customer-provided logger is present |
| 245 | + # and the exception carries a real traceback (e.g. not a synthetic FIFO circuit-breaker) |
| 246 | + batch_logger = self.logger |
| 247 | + if batch_logger is not None and exception[2] is not None: |
| 248 | + # ExceptionInfo allows None on every slot, but logging.warning's exc_info |
| 249 | + # requires a fully populated tuple. We already excluded synthetic exceptions |
| 250 | + # (no traceback) above, so the type and value are guaranteed to be set. |
| 251 | + assert exception[0] is not None |
| 252 | + assert exception[1] is not None |
| 253 | + exc_info = cast("tuple[type[BaseException], BaseException, TracebackType]", exception) |
| 254 | + batch_logger.warning( |
245 | 255 | "Record processing exception; skipping this record", |
246 | | - exc_info=exception, |
| 256 | + exc_info=exc_info, |
247 | 257 | ) |
248 | 258 |
|
249 | 259 | self.exceptions.append(exception) |
|
0 commit comments