From 1d0923758b1f2272f7ef74eda009215620ae5e75 Mon Sep 17 00:00:00 2001 From: Leandro Damascena Date: Wed, 27 Mar 2024 22:51:13 +0000 Subject: [PATCH 1/9] Initial commit --- aws_lambda_powertools/logging/formatter.py | 7 +++++++ aws_lambda_powertools/logging/logger.py | 3 +++ docs/core/logger.md | 2 +- tests/functional/test_logger.py | 13 +++++++++++++ 4 files changed, 24 insertions(+), 1 deletion(-) diff --git a/aws_lambda_powertools/logging/formatter.py b/aws_lambda_powertools/logging/formatter.py index 8b34b326435..67fa764b4e1 100644 --- a/aws_lambda_powertools/logging/formatter.py +++ b/aws_lambda_powertools/logging/formatter.py @@ -48,6 +48,10 @@ class BasePowertoolsFormatter(logging.Formatter, metaclass=ABCMeta): def append_keys(self, **additional_keys) -> None: raise NotImplementedError() + @abstractmethod + def current_keys(self) -> Dict[str, Any]: + raise NotImplementedError() + def remove_keys(self, keys: Iterable[str]) -> None: raise NotImplementedError() @@ -231,6 +235,9 @@ def formatTime(self, record: logging.LogRecord, datefmt: Optional[str] = None) - def append_keys(self, **additional_keys) -> None: self.log_format.update(additional_keys) + def current_keys(self) -> Dict[str, Any]: + return self.log_format + def remove_keys(self, keys: Iterable[str]) -> None: for key in keys: self.log_format.pop(key, None) diff --git a/aws_lambda_powertools/logging/logger.py b/aws_lambda_powertools/logging/logger.py index f86833a7851..bc68b25893a 100644 --- a/aws_lambda_powertools/logging/logger.py +++ b/aws_lambda_powertools/logging/logger.py @@ -583,6 +583,9 @@ def debug( def append_keys(self, **additional_keys: object) -> None: self.registered_formatter.append_keys(**additional_keys) + def current_keys(self) -> Dict[str, Any]: + return self.registered_formatter.current_keys() + def remove_keys(self, keys: Iterable[str]) -> None: self.registered_formatter.remove_keys(keys) diff --git a/docs/core/logger.md b/docs/core/logger.md index 8c915fcd589..79c55e8021f 100644 --- a/docs/core/logger.md +++ b/docs/core/logger.md @@ -732,7 +732,7 @@ The `log` argument is the final log record containing [our standard keys](#stand For exceptional cases where you want to completely replace our formatter logic, you can subclass `BasePowertoolsFormatter`. ???+ warning - You will need to implement `append_keys`, `clear_state`, override `format`, and optionally `remove_keys` to keep the same feature set Powertools for AWS Lambda (Python) Logger provides. This also means keeping state of logging keys added. + You will need to implement `append_keys`, `current_keys`, `clear_state`, override `format`, and optionally `remove_keys` to keep the same feature set Powertools for AWS Lambda (Python) Logger provides. This also means keeping state of logging keys added. === "bring_your_own_formatter_from_scratch.py" diff --git a/tests/functional/test_logger.py b/tests/functional/test_logger.py index d9e3b8d4e37..24aefe13357 100644 --- a/tests/functional/test_logger.py +++ b/tests/functional/test_logger.py @@ -606,6 +606,19 @@ def test_logger_append_remove_keys(stdout, service_name): assert (extra_keys.items() <= keys_removed_log.items()) is False +def test_logger_append_and_show_current_keys(stdout, service_name): + # GIVEN a Logger is initialized + logger = Logger(service=service_name, stream=stdout) + extra_keys = {"request_id": "id", "context": "value"} + + # WHEN keys are updated + logger.append_keys(**extra_keys) + + # THEN appended keys must be present in logger + assert "request_id" in logger.current_keys() + assert "context" in logger.current_keys() + + def test_logger_custom_formatter(stdout, service_name, lambda_context): class CustomFormatter(BasePowertoolsFormatter): custom_format = {} From 6491a6c2c55ee65dcc94d86eabb626d9e48128d0 Mon Sep 17 00:00:00 2001 From: Leandro Damascena Date: Wed, 27 Mar 2024 23:07:33 +0000 Subject: [PATCH 2/9] mypy + tests --- .../logger/src/bring_your_own_formatter_from_scratch.py | 5 ++++- tests/functional/test_logger.py | 6 ++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/examples/logger/src/bring_your_own_formatter_from_scratch.py b/examples/logger/src/bring_your_own_formatter_from_scratch.py index b7e7761b562..425f00b0d28 100644 --- a/examples/logger/src/bring_your_own_formatter_from_scratch.py +++ b/examples/logger/src/bring_your_own_formatter_from_scratch.py @@ -1,6 +1,6 @@ import json import logging -from typing import Iterable, List, Optional +from typing import Any, Dict, Iterable, List, Optional from aws_lambda_powertools import Logger from aws_lambda_powertools.logging.formatter import BasePowertoolsFormatter @@ -16,6 +16,9 @@ def append_keys(self, **additional_keys): # also used by `inject_lambda_context` decorator self.log_format.update(additional_keys) + def current_keys(self) -> Dict[str, Any]: + return self.log_format + def remove_keys(self, keys: Iterable[str]): for key in keys: self.log_format.pop(key, None) diff --git a/tests/functional/test_logger.py b/tests/functional/test_logger.py index 24aefe13357..384db76236c 100644 --- a/tests/functional/test_logger.py +++ b/tests/functional/test_logger.py @@ -9,10 +9,9 @@ import string import sys import warnings -from ast import Dict from collections import namedtuple from datetime import datetime, timezone -from typing import Any, Callable, Iterable, List, Optional, Union +from typing import Any, Callable, Dict, Iterable, List, Optional, Union import pytest @@ -626,6 +625,9 @@ class CustomFormatter(BasePowertoolsFormatter): def append_keys(self, **additional_keys): self.custom_format.update(additional_keys) + def current_keys(self) -> Dict[str, Any]: + return self.custom_format + def remove_keys(self, keys: Iterable[str]): for key in keys: self.custom_format.pop(key, None) From 4d392457033f0007ce3d809366dd0256d1b0c1c9 Mon Sep 17 00:00:00 2001 From: Leandro Damascena Date: Mon, 1 Apr 2024 13:14:14 +0100 Subject: [PATCH 3/9] Using get_current_keys instead of current_keys + error handling --- aws_lambda_powertools/logging/formatter.py | 5 ++--- aws_lambda_powertools/logging/logger.py | 7 +++++-- tests/functional/test_logger.py | 6 +++--- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/aws_lambda_powertools/logging/formatter.py b/aws_lambda_powertools/logging/formatter.py index 67fa764b4e1..22da8f61b5b 100644 --- a/aws_lambda_powertools/logging/formatter.py +++ b/aws_lambda_powertools/logging/formatter.py @@ -48,8 +48,7 @@ class BasePowertoolsFormatter(logging.Formatter, metaclass=ABCMeta): def append_keys(self, **additional_keys) -> None: raise NotImplementedError() - @abstractmethod - def current_keys(self) -> Dict[str, Any]: + def get_current_keys(self) -> Dict[str, Any]: raise NotImplementedError() def remove_keys(self, keys: Iterable[str]) -> None: @@ -235,7 +234,7 @@ def formatTime(self, record: logging.LogRecord, datefmt: Optional[str] = None) - def append_keys(self, **additional_keys) -> None: self.log_format.update(additional_keys) - def current_keys(self) -> Dict[str, Any]: + def get_current_keys(self) -> Dict[str, Any]: return self.log_format def remove_keys(self, keys: Iterable[str]) -> None: diff --git a/aws_lambda_powertools/logging/logger.py b/aws_lambda_powertools/logging/logger.py index bc68b25893a..27c79685bcd 100644 --- a/aws_lambda_powertools/logging/logger.py +++ b/aws_lambda_powertools/logging/logger.py @@ -583,8 +583,11 @@ def debug( def append_keys(self, **additional_keys: object) -> None: self.registered_formatter.append_keys(**additional_keys) - def current_keys(self) -> Dict[str, Any]: - return self.registered_formatter.current_keys() + def get_current_keys(self) -> Dict[str, Any]: + if hasattr(self.registered_formatter, "get_current_keys"): + return self.registered_formatter.get_current_keys() + + return {} def remove_keys(self, keys: Iterable[str]) -> None: self.registered_formatter.remove_keys(keys) diff --git a/tests/functional/test_logger.py b/tests/functional/test_logger.py index 384db76236c..2c248ce681c 100644 --- a/tests/functional/test_logger.py +++ b/tests/functional/test_logger.py @@ -614,8 +614,8 @@ def test_logger_append_and_show_current_keys(stdout, service_name): logger.append_keys(**extra_keys) # THEN appended keys must be present in logger - assert "request_id" in logger.current_keys() - assert "context" in logger.current_keys() + assert "request_id" in logger.get_current_keys() + assert "context" in logger.get_current_keys() def test_logger_custom_formatter(stdout, service_name, lambda_context): @@ -625,7 +625,7 @@ class CustomFormatter(BasePowertoolsFormatter): def append_keys(self, **additional_keys): self.custom_format.update(additional_keys) - def current_keys(self) -> Dict[str, Any]: + def get_current_keys(self) -> Dict[str, Any]: return self.custom_format def remove_keys(self, keys: Iterable[str]): From f1715f3d9b346f6cb667979619b62d3243b4b614 Mon Sep 17 00:00:00 2001 From: Leandro Damascena Date: Mon, 1 Apr 2024 13:43:55 +0100 Subject: [PATCH 4/9] Tests --- aws_lambda_powertools/logging/logger.py | 2 +- tests/functional/test_logger.py | 27 ++++++++++++++++++++++--- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/aws_lambda_powertools/logging/logger.py b/aws_lambda_powertools/logging/logger.py index 27c79685bcd..2f6422b8102 100644 --- a/aws_lambda_powertools/logging/logger.py +++ b/aws_lambda_powertools/logging/logger.py @@ -584,7 +584,7 @@ def append_keys(self, **additional_keys: object) -> None: self.registered_formatter.append_keys(**additional_keys) def get_current_keys(self) -> Dict[str, Any]: - if hasattr(self.registered_formatter, "get_current_keys"): + if hasattr(self.registered_formatter, "log_format"): return self.registered_formatter.get_current_keys() return {} diff --git a/tests/functional/test_logger.py b/tests/functional/test_logger.py index 2c248ce681c..564c1e7f333 100644 --- a/tests/functional/test_logger.py +++ b/tests/functional/test_logger.py @@ -618,6 +618,30 @@ def test_logger_append_and_show_current_keys(stdout, service_name): assert "context" in logger.get_current_keys() +def test_logger_formatter_without_current_keys_method(stdout, service_name): + class CustomFormatter(BasePowertoolsFormatter): + def append_keys(self, **additional_keys): + # Fake method + pass + + def clear_state(self) -> None: + # Fake method + pass + + custom_formater = CustomFormatter() + + # GIVEN a Logger is initialized with a Logger Formatter from scratch + + logger = Logger(service=service_name, stream=stdout, logger_formatter=custom_formater) + extra_keys = {"request_id": "id", "context": "value"} + + # WHEN keys are updated + logger.append_keys(**extra_keys) + + # THEN appended keys will not persist because customer must implement methods and persists log_format + assert logger.get_current_keys() == {} + + def test_logger_custom_formatter(stdout, service_name, lambda_context): class CustomFormatter(BasePowertoolsFormatter): custom_format = {} @@ -625,9 +649,6 @@ class CustomFormatter(BasePowertoolsFormatter): def append_keys(self, **additional_keys): self.custom_format.update(additional_keys) - def get_current_keys(self) -> Dict[str, Any]: - return self.custom_format - def remove_keys(self, keys: Iterable[str]): for key in keys: self.custom_format.pop(key, None) From d2c362090753fd9ed1c5cd6c84d5a2d379236e12 Mon Sep 17 00:00:00 2001 From: Leandro Damascena Date: Mon, 1 Apr 2024 13:52:17 +0100 Subject: [PATCH 5/9] Docs --- docs/core/logger.md | 2 +- tests/functional/test_logger.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/core/logger.md b/docs/core/logger.md index 79c55e8021f..dd622a9dd42 100644 --- a/docs/core/logger.md +++ b/docs/core/logger.md @@ -732,7 +732,7 @@ The `log` argument is the final log record containing [our standard keys](#stand For exceptional cases where you want to completely replace our formatter logic, you can subclass `BasePowertoolsFormatter`. ???+ warning - You will need to implement `append_keys`, `current_keys`, `clear_state`, override `format`, and optionally `remove_keys` to keep the same feature set Powertools for AWS Lambda (Python) Logger provides. This also means keeping state of logging keys added. + You will need to implement `append_keys`, `clear_state`, override `format`, and optionally `get_current_keys`, and `remove_keys` to keep the same feature set Powertools for AWS Lambda (Python) Logger provides. This also means keeping state of logging keys added. === "bring_your_own_formatter_from_scratch.py" diff --git a/tests/functional/test_logger.py b/tests/functional/test_logger.py index 564c1e7f333..98ec2dec33d 100644 --- a/tests/functional/test_logger.py +++ b/tests/functional/test_logger.py @@ -638,7 +638,8 @@ def clear_state(self) -> None: # WHEN keys are updated logger.append_keys(**extra_keys) - # THEN appended keys will not persist because customer must implement methods and persists log_format + # THEN the appended keys will not persist + # unless the customer implements the required methods and persists the log_format assert logger.get_current_keys() == {} From f07091c1f02588afd5e019943b9206d1e40f85ce Mon Sep 17 00:00:00 2001 From: Leandro Damascena Date: Wed, 10 Apr 2024 10:41:17 +0100 Subject: [PATCH 6/9] Adding documentation --- docs/core/logger.md | 10 ++++++++++ examples/logger/src/get_current_keys.py | 14 ++++++++++++++ tests/functional/test_logger.py | 7 ++++--- 3 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 examples/logger/src/get_current_keys.py diff --git a/docs/core/logger.md b/docs/core/logger.md index dd622a9dd42..5bfbd4acff5 100644 --- a/docs/core/logger.md +++ b/docs/core/logger.md @@ -274,6 +274,16 @@ Logger is commonly initialized in the global scope. Due to [Lambda Execution Con --8<-- "examples/logger/src/clear_state_event_two.json" ``` +### Accessing currently configured keys + +You can view all currently configured keys from the Logger state using the `get_current_keys()` method. This method is useful when you need to avoid overwriting keys that are already configured. + +=== "get_current_keys.py" + + ```python hl_lines="4 11" + --8<-- "examples/logger/src/get_current_keys.py" + ``` + ### Log levels The default log level is `INFO`. It can be set using the `level` constructor option, `setLevel()` method or by using the `POWERTOOLS_LOG_LEVEL` environment variable. diff --git a/examples/logger/src/get_current_keys.py b/examples/logger/src/get_current_keys.py new file mode 100644 index 00000000000..c0ae49165b7 --- /dev/null +++ b/examples/logger/src/get_current_keys.py @@ -0,0 +1,14 @@ +from aws_lambda_powertools import Logger +from aws_lambda_powertools.utilities.typing import LambdaContext + +logger = Logger() + + +@logger.inject_lambda_context +def lambda_handler(event: dict, context: LambdaContext) -> str: + logger.info("Collecting payment") + + if "order" not in logger.get_current_keys(): + logger.append_keys(order=event.get("order")) + + return "hello world" diff --git a/tests/functional/test_logger.py b/tests/functional/test_logger.py index 98ec2dec33d..7aa4037cb9c 100644 --- a/tests/functional/test_logger.py +++ b/tests/functional/test_logger.py @@ -614,11 +614,12 @@ def test_logger_append_and_show_current_keys(stdout, service_name): logger.append_keys(**extra_keys) # THEN appended keys must be present in logger - assert "request_id" in logger.get_current_keys() - assert "context" in logger.get_current_keys() + current_keys = logger.get_current_keys() + assert "request_id" in current_keys + assert "context" in current_keys -def test_logger_formatter_without_current_keys_method(stdout, service_name): +def test_logger_formatter_without_get_current_keys_method(stdout, service_name): class CustomFormatter(BasePowertoolsFormatter): def append_keys(self, **additional_keys): # Fake method From fe50c1283159462eb1c50a6259ec81f014c7a0d5 Mon Sep 17 00:00:00 2001 From: Leandro Damascena Date: Wed, 10 Apr 2024 14:18:46 +0100 Subject: [PATCH 7/9] Addressing Ruben's feedback --- aws_lambda_powertools/logging/logger.py | 2 +- docs/core/logger.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/aws_lambda_powertools/logging/logger.py b/aws_lambda_powertools/logging/logger.py index 2f6422b8102..3389f260762 100644 --- a/aws_lambda_powertools/logging/logger.py +++ b/aws_lambda_powertools/logging/logger.py @@ -584,7 +584,7 @@ def append_keys(self, **additional_keys: object) -> None: self.registered_formatter.append_keys(**additional_keys) def get_current_keys(self) -> Dict[str, Any]: - if hasattr(self.registered_formatter, "log_format"): + if isinstance(self.registered_formatter, LambdaPowertoolsFormatter): return self.registered_formatter.get_current_keys() return {} diff --git a/docs/core/logger.md b/docs/core/logger.md index 5bfbd4acff5..bf600e285ca 100644 --- a/docs/core/logger.md +++ b/docs/core/logger.md @@ -742,7 +742,7 @@ The `log` argument is the final log record containing [our standard keys](#stand For exceptional cases where you want to completely replace our formatter logic, you can subclass `BasePowertoolsFormatter`. ???+ warning - You will need to implement `append_keys`, `clear_state`, override `format`, and optionally `get_current_keys`, and `remove_keys` to keep the same feature set Powertools for AWS Lambda (Python) Logger provides. This also means keeping state of logging keys added. + You will need to implement `append_keys`, `clear_state`, override `format`, and optionally `get_current_keys`, and `remove_keys` to keep the same feature set Powertools for AWS Lambda (Python) Logger provides. This also means tracking the added logging keys. === "bring_your_own_formatter_from_scratch.py" From a28520b77f9eef42390115d9008e26d37c5ce646 Mon Sep 17 00:00:00 2001 From: Leandro Damascena Date: Wed, 10 Apr 2024 14:48:51 +0100 Subject: [PATCH 8/9] Addressing Ruben's feedback --- aws_lambda_powertools/logging/formatter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws_lambda_powertools/logging/formatter.py b/aws_lambda_powertools/logging/formatter.py index 22da8f61b5b..ac623303ab1 100644 --- a/aws_lambda_powertools/logging/formatter.py +++ b/aws_lambda_powertools/logging/formatter.py @@ -49,7 +49,7 @@ def append_keys(self, **additional_keys) -> None: raise NotImplementedError() def get_current_keys(self) -> Dict[str, Any]: - raise NotImplementedError() + return {} def remove_keys(self, keys: Iterable[str]) -> None: raise NotImplementedError() From 7c3ffd0678a2f9914abe5881eb73c1970a8f4c0a Mon Sep 17 00:00:00 2001 From: Leandro Damascena Date: Wed, 10 Apr 2024 23:30:16 +0100 Subject: [PATCH 9/9] Addressing Ruben's feedback --- aws_lambda_powertools/logging/logger.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/aws_lambda_powertools/logging/logger.py b/aws_lambda_powertools/logging/logger.py index 3389f260762..dc03e1af8eb 100644 --- a/aws_lambda_powertools/logging/logger.py +++ b/aws_lambda_powertools/logging/logger.py @@ -584,10 +584,7 @@ def append_keys(self, **additional_keys: object) -> None: self.registered_formatter.append_keys(**additional_keys) def get_current_keys(self) -> Dict[str, Any]: - if isinstance(self.registered_formatter, LambdaPowertoolsFormatter): - return self.registered_formatter.get_current_keys() - - return {} + return self.registered_formatter.get_current_keys() def remove_keys(self, keys: Iterable[str]) -> None: self.registered_formatter.remove_keys(keys)