Skip to content

Commit 22c9927

Browse files
committed
feat(logger): add clear_state method to Logger
1 parent 37f7e6d commit 22c9927

File tree

6 files changed

+107
-1
lines changed

6 files changed

+107
-1
lines changed

aws_lambda_powertools/logging/logger.py

+10
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,7 @@ def __init__(
222222
choice=sampling_rate,
223223
env=os.getenv(constants.LOGGER_LOG_SAMPLING_RATE),
224224
)
225+
self._default_log_keys: dict[str, Any] = {"service": self.service, "sampling_rate": self.sampling_rate}
225226
self.child = child
226227
self.logger_formatter = logger_formatter
227228
self._stream = stream or sys.stdout
@@ -605,6 +606,15 @@ def append_context_keys(self, **additional_keys: Any) -> Generator[None, None, N
605606
with self.registered_formatter.append_context_keys(**additional_keys):
606607
yield
607608

609+
def clear_state(self) -> None:
610+
"""Removes all custom keys that were appended to the Logger."""
611+
# Clear all custom keys from the formatter
612+
self.registered_formatter.clear_state()
613+
614+
# Reset to default keys
615+
default_keys: dict[Any, Any] = dict(self._default_log_keys)
616+
self.structure_logs(**default_keys)
617+
608618
# These specific thread-safe methods are necessary to manage shared context in concurrent environments.
609619
# They prevent race conditions and ensure data consistency across multiple threads.
610620
def thread_safe_append_keys(self, **additional_keys: object) -> None:

docs/core/logger.md

+26-1
Original file line numberDiff line numberDiff line change
@@ -274,13 +274,15 @@ You can remove any additional key from Logger state using `remove_keys`.
274274

275275
#### Clearing all state
276276

277+
##### Decorator with clear_state
278+
277279
Logger is commonly initialized in the global scope. Due to [Lambda Execution Context reuse](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-context.html){target="_blank"}, this means that custom keys can be persisted across invocations. If you want all custom keys to be deleted, you can use `clear_state=True` param in `inject_lambda_context` decorator.
278280

279281
???+ tip "Tip: When is this useful?"
280282
It is useful when you add multiple custom keys conditionally, instead of setting a default `None` value if not present. Any key with `None` value is automatically removed by Logger.
281283

282284
???+ danger "Danger: This can have unintended side effects if you use Layers"
283-
Lambda Layers code is imported before the Lambda handler.
285+
Lambda Layers code is imported before the Lambda handler. When a Lambda function starts, it first imports and executes all code in the Layers (including any global scope code) before proceeding to the function's own code.
284286

285287
This means that `clear_state=True` will instruct Logger to remove any keys previously added before Lambda handler execution proceeds.
286288

@@ -304,6 +306,29 @@ Logger is commonly initialized in the global scope. Due to [Lambda Execution Con
304306
--8<-- "examples/logger/src/clear_state_event_two.json"
305307
```
306308

309+
##### clear_state method
310+
311+
`clear_state()` is a method you can call explicitly within your code to clear appended keys at any point during the execution of a single Lambda invocation.
312+
313+
This allows for more granular control over the logger's state within a single function execution, enabling you to reset the logger to its initial state before specific logging operations or at the end of certain processes within the same Lambda run.
314+
315+
=== "clear_state_method.py"
316+
317+
```python hl_lines="12"
318+
--8<-- "examples/logger/src/clear_state_method.py"
319+
```
320+
=== "Output before clear_state()"
321+
322+
```json hl_lines="9 17"
323+
--8<-- "examples/logger/src/before_clear_state.json"
324+
```
325+
326+
=== "Output after clear_state()"
327+
328+
```json hl_lines="4"
329+
--8<-- "examples/logger/src/after_clear_state.json"
330+
```
331+
307332
### Accessing currently configured keys
308333

309334
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.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"level": "INFO",
3+
"location": "lambda_handler:126",
4+
"message": "State after clearing - only show default keys",
5+
"timestamp": "2025-01-30 13:56:03,158-0300",
6+
"service": "payment"
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"logs": [
3+
{
4+
"level": "INFO",
5+
"location": "lambda_handler:122",
6+
"message": "Starting order processing",
7+
"timestamp": "2025-01-30 13:56:03,157-0300",
8+
"service": "payment",
9+
"order_id": "12345"
10+
},
11+
{
12+
"level": "INFO",
13+
"location": "lambda_handler:124",
14+
"message": "Final state before clearing",
15+
"timestamp": "2025-01-30 13:56:03,157-0300",
16+
"service": "payment",
17+
"order_id": "12345"
18+
}
19+
]
20+
}
+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from aws_lambda_powertools import Logger
2+
from aws_lambda_powertools.utilities.typing import LambdaContext
3+
4+
logger = Logger(service="payment", level="DEBUG")
5+
6+
7+
def lambda_handler(event: dict, context: LambdaContext) -> str:
8+
try:
9+
logger.append_keys(order_id="12345")
10+
logger.info("Starting order processing")
11+
finally:
12+
logger.info("Final state before clearing")
13+
logger.clear_state()
14+
logger.info("State after clearing - only show default keys")
15+
return "Completed"

tests/functional/logger/required_dependencies/test_logger.py

+29
Original file line numberDiff line numberDiff line change
@@ -1232,3 +1232,32 @@ def test_logger_change_level_child_logger(stdout, service_name):
12321232
logs = list(stdout.getvalue().strip().split("\n"))
12331233
assert len(logs) == 1
12341234
assert "service" in logs[0]
1235+
1236+
1237+
def test_clear_state_with_append_keys():
1238+
# GIVEN a Logger is initialized
1239+
logger = Logger(service="service_name", stream=stdout)
1240+
1241+
# WHEN append keys are added
1242+
logger.append_keys(custom_key="custom_key")
1243+
logger.info("message with appended keys")
1244+
logger.clear_state()
1245+
1246+
# THEN context keys should be cleared
1247+
assert "custom_key" not in logger.get_current_keys()
1248+
1249+
1250+
def test_clear_state(stdout, service_name):
1251+
logger = Logger(service=service_name, stream=stdout)
1252+
logger.info("message for the user")
1253+
logger.clear_state()
1254+
1255+
expected_keys = {
1256+
"level": "%(levelname)s",
1257+
"location": "%(funcName)s:%(lineno)d",
1258+
"message": None,
1259+
"timestamp": "%(asctime)s",
1260+
"service": service_name,
1261+
"sampling_rate": None,
1262+
}
1263+
assert logger.get_current_keys() == expected_keys

0 commit comments

Comments
 (0)