Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(logger): add method to return currently configured keys #4033

Merged
6 changes: 6 additions & 0 deletions aws_lambda_powertools/logging/formatter.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ class BasePowertoolsFormatter(logging.Formatter, metaclass=ABCMeta):
def append_keys(self, **additional_keys) -> None:
raise NotImplementedError()

def get_current_keys(self) -> Dict[str, Any]:
return {}

def remove_keys(self, keys: Iterable[str]) -> None:
raise NotImplementedError()

Expand Down Expand Up @@ -231,6 +234,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 get_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)
Expand Down
6 changes: 6 additions & 0 deletions aws_lambda_powertools/logging/logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,12 @@ def debug(
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 {}
rubenfonseca marked this conversation as resolved.
Show resolved Hide resolved

def remove_keys(self, keys: Iterable[str]) -> None:
self.registered_formatter.remove_keys(keys)

Expand Down
12 changes: 11 additions & 1 deletion docs/core/logger.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -732,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 `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"

Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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)
Expand Down
14 changes: 14 additions & 0 deletions examples/logger/src/get_current_keys.py
Original file line number Diff line number Diff line change
@@ -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"
42 changes: 40 additions & 2 deletions tests/functional/test_logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -606,6 +605,45 @@ 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
current_keys = logger.get_current_keys()
assert "request_id" in current_keys
assert "context" in current_keys


def test_logger_formatter_without_get_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 the appended keys will not persist
# unless the customer implements the required methods and persists the log_format
assert logger.get_current_keys() == {}


def test_logger_custom_formatter(stdout, service_name, lambda_context):
class CustomFormatter(BasePowertoolsFormatter):
custom_format = {}
Expand Down