Skip to content

Commit 456846a

Browse files
committed
refactor: add append/remove keys, common properties
1 parent 754a2a2 commit 456846a

File tree

3 files changed

+58
-18
lines changed

3 files changed

+58
-18
lines changed

aws_lambda_powertools/logging/formatter.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def __init__(
5151
json_deserializer: Optional[Callable[[Any], Any]] = None,
5252
json_default: Optional[Callable[[Any], Any]] = None,
5353
datefmt: str = None,
54-
log_record_order: List = None,
54+
log_record_order: List[str] = None,
5555
utc: bool = False,
5656
**kwargs
5757
):
@@ -99,6 +99,7 @@ def __init__(
9999
self.utc = utc
100100
self.log_record_order = log_record_order or ["level", "location", "message", "timestamp"]
101101
self.log_format = dict.fromkeys(self.log_record_order) # Set the insertion order for the log messages
102+
self.update_formatter = self.append_keys # alias to previous method
102103

103104
if self.utc:
104105
self.converter = time.gmtime
@@ -108,6 +109,13 @@ def __init__(
108109
keys_combined = {**self._build_default_keys(), **kwargs}
109110
self.log_format.update(**keys_combined)
110111

112+
def append_keys(self, **additional_keys):
113+
self.log_format.update(additional_keys)
114+
115+
def remove_keys(self, keys: Iterable[str]):
116+
for key in keys:
117+
self.log_format.pop(key, None)
118+
111119
@staticmethod
112120
def _build_default_keys():
113121
return {
@@ -121,9 +129,6 @@ def _get_latest_trace_id():
121129
xray_trace_id = os.getenv(constants.XRAY_TRACE_ID_ENV)
122130
return xray_trace_id.split(";")[0].replace("Root=", "") if xray_trace_id else None
123131

124-
def update_formatter(self, **kwargs):
125-
self.log_format.update(kwargs)
126-
127132
def _extract_log_message(self, log_record: logging.LogRecord) -> Union[Dict[str, Any], str, bool, Iterable]:
128133
"""Extract message from log record and attempt to JSON decode it if str
129134

aws_lambda_powertools/logging/logger.py

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import os
55
import random
66
import sys
7-
from typing import Any, Callable, Dict, Optional, Union
7+
from typing import Any, Callable, Dict, Iterable, Optional, Union
88

99
import jmespath
1010

@@ -42,6 +42,7 @@ def _is_cold_start() -> bool:
4242
# so we need to return to subclassing removed in #97
4343
# All methods/properties continue to be proxied to inner logger
4444
# https://github.com/awslabs/aws-lambda-powertools-python/issues/107
45+
# noinspection PyRedeclaration
4546
class Logger(logging.Logger): # lgtm [py/missing-call-to-init]
4647
"""Creates and setups a logger to format statements in JSON.
4748
@@ -96,7 +97,7 @@ class Logger(logging.Logger): # lgtm [py/missing-call-to-init]
9697
>>> logger = Logger(service="payment")
9798
>>>
9899
>>> def handler(event, context):
99-
logger.structure_logs(append=True, payment_id=event["payment_id"])
100+
logger.append_keys(payment_id=event["payment_id"])
100101
logger.info("Hello")
101102
102103
**Create child Logger using logging inheritance via child param**
@@ -139,6 +140,7 @@ def __init__(
139140
self._handler = logging.StreamHandler(stream) or logging.StreamHandler(sys.stdout)
140141
self._default_log_keys = {"service": self.service, "sampling_rate": self.sampling_rate}
141142
self._logger = self._get_logger()
143+
142144
self._init_logger(**kwargs)
143145

144146
def __getattr__(self, name):
@@ -282,12 +284,28 @@ def decorate(event, context):
282284

283285
return decorate
284286

285-
def structure_logs(self, append: bool = False, **kwargs):
287+
def append_keys(self, **additional_keys):
288+
self.registered_formatter.append_keys(**additional_keys)
289+
290+
def remove_keys(self, keys: Iterable[str]):
291+
self.registered_formatter.remove_keys(keys)
292+
293+
@property
294+
def registered_handler(self) -> logging.Handler:
295+
"""Registered Logger handler"""
296+
handlers = self._logger.parent.handlers if self.child else self._logger.handlers
297+
return handlers[0]
298+
299+
@property
300+
def registered_formatter(self) -> Optional[LambdaPowertoolsFormatter]:
301+
"""Registered Logger formatter"""
302+
return self.registered_handler.formatter
303+
304+
def structure_logs(self, append: bool = False, **keys):
286305
"""Sets logging formatting to JSON.
287306
288307
Optionally, it can append keyword arguments
289-
to an existing logger so it is available
290-
across future log statements.
308+
to an existing logger so it is available across future log statements.
291309
292310
Last keyword argument and value wins if duplicated.
293311
@@ -297,15 +315,12 @@ def structure_logs(self, append: bool = False, **kwargs):
297315
[description], by default False
298316
"""
299317

300-
# Child loggers don't have handlers attached, use its parent handlers
301-
handlers = self._logger.parent.handlers if self.child else self._logger.handlers
302-
for handler in handlers:
303-
if append:
304-
# Update existing formatter in an existing logger handler
305-
handler.formatter.update_formatter(**kwargs)
306-
else:
307-
# Set a new formatter for a logger handler
308-
handler.setFormatter(LambdaPowertoolsFormatter(**self._default_log_keys, **kwargs))
318+
if append:
319+
# Maintenance: Add deprecation warning for major version, refer to append_keys() when docs are updated
320+
self.append_keys(**keys)
321+
else:
322+
# Set a new formatter for a logger handler
323+
self.registered_handler.setFormatter(LambdaPowertoolsFormatter(**self._default_log_keys, **keys))
309324

310325
def set_correlation_id(self, value: str):
311326
"""Sets the correlation_id in the logging json

tests/functional/test_logger.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,3 +474,23 @@ def handler(event, context):
474474
# THEN
475475
log = capture_logging_output(stdout)
476476
assert request_id == log["correlation_id"]
477+
478+
479+
def test_logger_append_remove_keys(stdout, service_name):
480+
# GIVEN a Logger is initialized
481+
logger = Logger(service=service_name, stream=stdout)
482+
extra_keys = {"request_id": "id", "context": "value"}
483+
484+
# WHEN keys are updated
485+
logger.append_keys(**extra_keys)
486+
logger.info("message with new keys")
487+
488+
# And removed
489+
logger.remove_keys(extra_keys.keys())
490+
logger.info("message after keys being removed")
491+
492+
# THEN additional keys should only be present in the first log statement
493+
extra_keys_log, keys_removed_log = capture_multiple_logging_statements_output(stdout)
494+
495+
assert extra_keys.items() <= extra_keys_log.items()
496+
assert (extra_keys.items() <= keys_removed_log.items()) is False

0 commit comments

Comments
 (0)