Skip to content

Commit daf6e03

Browse files
committed
* 'develop' of https://github.com/awslabs/aws-lambda-powertools-python: fix(logger): preserve std keys when using custom formatters (#1264)
2 parents ab07664 + 6c1a4d2 commit daf6e03

File tree

3 files changed

+51
-4
lines changed

3 files changed

+51
-4
lines changed

Diff for: aws_lambda_powertools/logging/formatter.py

+5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import inspect
12
import json
23
import logging
34
import os
@@ -286,3 +287,7 @@ def _strip_none_records(records: Dict[str, Any]) -> Dict[str, Any]:
286287

287288

288289
JsonFormatter = LambdaPowertoolsFormatter # alias to previous formatter
290+
291+
292+
# Fetch current and future parameters from PowertoolsFormatter that should be reserved
293+
RESERVED_FORMATTER_CUSTOM_KEYS: List[str] = inspect.getfullargspec(LambdaPowertoolsFormatter).args[1:]

Diff for: aws_lambda_powertools/logging/logger.py

+12-4
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from ..shared.functions import resolve_env_var_choice, resolve_truthy_env_var_choice
1313
from .exceptions import InvalidLoggerSamplingRateError
1414
from .filters import SuppressFilter
15-
from .formatter import BasePowertoolsFormatter, LambdaPowertoolsFormatter
15+
from .formatter import RESERVED_FORMATTER_CUSTOM_KEYS, BasePowertoolsFormatter, LambdaPowertoolsFormatter
1616
from .lambda_context import build_lambda_context_model
1717

1818
logger = logging.getLogger(__name__)
@@ -82,7 +82,7 @@ class Logger(logging.Logger): # lgtm [py/missing-call-to-init]
8282
datefmt: str, optional
8383
String directives (strftime) to format log timestamp using `time`, by default it uses RFC
8484
3339.
85-
use_datetime_directive: str, optional
85+
use_datetime_directive: bool, optional
8686
Interpret `datefmt` as a format string for `datetime.datetime.strftime`, rather than
8787
`time.strftime`.
8888
@@ -368,7 +368,7 @@ def registered_handler(self) -> logging.Handler:
368368
return handlers[0]
369369

370370
@property
371-
def registered_formatter(self) -> PowertoolsFormatter:
371+
def registered_formatter(self) -> BasePowertoolsFormatter:
372372
"""Convenience property to access logger formatter"""
373373
return self.registered_handler.formatter # type: ignore
374374

@@ -395,7 +395,15 @@ def structure_logs(self, append: bool = False, **keys):
395395
is_logger_preconfigured = getattr(self._logger, "init", False)
396396
if not is_logger_preconfigured:
397397
formatter = self.logger_formatter or LambdaPowertoolsFormatter(**log_keys) # type: ignore
398-
return self.registered_handler.setFormatter(formatter)
398+
self.registered_handler.setFormatter(formatter)
399+
400+
# when using a custom Lambda Powertools Formatter
401+
# standard and custom keys that are not Powertools Formatter parameters should be appended
402+
# and custom keys that might happen to be Powertools Formatter parameters should be discarded
403+
# this prevents adding them as custom keys, for example, `json_default=<callable>`
404+
# see https://github.com/awslabs/aws-lambda-powertools-python/issues/1263
405+
custom_keys = {k: v for k, v in log_keys.items() if k not in RESERVED_FORMATTER_CUSTOM_KEYS}
406+
return self.registered_formatter.append_keys(**custom_keys)
399407

400408
# Mode 2 (legacy)
401409
if append:

Diff for: tests/functional/test_logger.py

+34
Original file line numberDiff line numberDiff line change
@@ -625,6 +625,40 @@ def handler(event, context):
625625
assert all(k in second_log for k in lambda_context_keys)
626626

627627

628+
def test_logger_custom_formatter_has_standard_and_custom_keys(stdout, service_name, lambda_context):
629+
class CustomFormatter(LambdaPowertoolsFormatter):
630+
...
631+
632+
# GIVEN a Logger is initialized with a custom formatter
633+
logger = Logger(service=service_name, stream=stdout, logger_formatter=CustomFormatter(), my_key="value")
634+
635+
# WHEN a lambda function is decorated with logger
636+
@logger.inject_lambda_context
637+
def handler(event, context):
638+
logger.info("Hello")
639+
640+
handler({}, lambda_context)
641+
642+
standard_keys = (
643+
"level",
644+
"location",
645+
"message",
646+
"timestamp",
647+
"service",
648+
"cold_start",
649+
"function_name",
650+
"function_memory_size",
651+
"function_arn",
652+
"function_request_id",
653+
)
654+
655+
log = capture_logging_output(stdout)
656+
657+
# THEN all standard keys should be available
658+
assert all(k in log for k in standard_keys)
659+
assert "my_key" in log
660+
661+
628662
def test_logger_custom_handler(lambda_context, service_name, tmp_path):
629663
# GIVEN a Logger is initialized with a FileHandler
630664
log_file = tmp_path / "log.json"

0 commit comments

Comments
 (0)