Skip to content

Commit f471c36

Browse files
fix external logs
1 parent 440eafe commit f471c36

3 files changed

Lines changed: 56 additions & 7 deletions

File tree

aws_lambda_powertools/logging/buffer/handler.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import logging
44
from typing import TYPE_CHECKING
55

6+
from aws_lambda_powertools.logging.buffer.functions import _check_minimum_buffer_log_level
7+
68
if TYPE_CHECKING:
79
from aws_lambda_powertools.logging.buffer.cache import LoggerBufferCache
810
from aws_lambda_powertools.logging.buffer.config import LoggerBufferConfig
@@ -15,7 +17,8 @@ class BufferingHandler(logging.Handler):
1517
1618
The handler intercepts log records from external libraries and
1719
stores them in the source logger's buffer using the same tracer_id mechanism.
18-
Logs are flushed when an error occurs or when explicitly requested.
20+
Logs above the buffer verbosity threshold are emitted directly through the source logger.
21+
Logs at or below the threshold are buffered and flushed together with application logs.
1922
"""
2023

2124
def __init__(
@@ -43,14 +46,23 @@ def __init__(
4346

4447
def emit(self, record: logging.LogRecord) -> None:
4548
"""
46-
Buffer the log record by delegating to source logger's buffering logic.
47-
Call source logger to add a structured record to the buffer.
49+
Buffer or emit the log record based on the buffer verbosity threshold.
50+
51+
Logs above the configured buffer_at_verbosity are emitted directly
52+
through the source logger. Logs at or below the threshold are buffered.
4853
4954
Parameters
5055
----------
5156
record : logging.LogRecord
5257
The log record from an external logger
5358
"""
59+
level_name = logging.getLevelName(record.levelno)
60+
61+
# If log level exceeds buffer threshold, emit directly through source logger
62+
if _check_minimum_buffer_log_level(self.buffer_config.buffer_at_verbosity, level_name):
63+
self.source_logger._logger.handle(record)
64+
return
65+
5466
self.source_logger._add_log_record_to_buffer(
5567
level=record.levelno,
5668
msg=record.getMessage(),

tests/functional/logger/required_dependencies/test_logger_utils.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,8 +322,9 @@ def test_copy_config_to_ext_loggers_but_preserve_log_levels(stdout, logger, log_
322322

323323
def test_copy_config_with_buffering_uses_buffering_handler(stdout, logger, log_level):
324324
# GIVEN an external logger and Powertools logger with buffer_config
325+
# Using buffer_at_verbosity="WARNING" so INFO logs are buffered (not emitted directly)
325326
ext_logger = logger()
326-
buffer_config = LoggerBufferConfig(max_bytes=10240)
327+
buffer_config = LoggerBufferConfig(max_bytes=10240, buffer_at_verbosity="WARNING")
327328
powertools_logger = Logger(
328329
service=service_name(),
329330
level=log_level.INFO.value,

tests/unit/logger/required_dependencies/test_logger_buffer_handler.py

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,10 @@ def test_buffering_handler_init_stores_dependencies():
3232

3333
def test_buffering_handler_emit_calls_add_log_record_to_buffer(monkeypatch):
3434
# GIVEN a real Logger with buffer and a BufferingHandler (tracer id set so records are buffered)
35+
# Using buffer_at_verbosity="WARNING" so INFO logs are buffered (below threshold)
3536
monkeypatch.setenv(constants.XRAY_TRACE_ID_ENV, "1-67c39786-5908a82a246fb67f3089263f")
3637
stream = io.StringIO()
37-
buffer_config = LoggerBufferConfig(max_bytes=10240)
38+
buffer_config = LoggerBufferConfig(max_bytes=10240, buffer_at_verbosity="WARNING")
3839
source_logger = Logger(service="test2", buffer_config=buffer_config, stream=stream)
3940
handler = BufferingHandler(
4041
buffer_cache=source_logger._buffer_cache,
@@ -55,9 +56,44 @@ def test_buffering_handler_emit_calls_add_log_record_to_buffer(monkeypatch):
5556

5657
# WHEN the handler emits the record and the buffer is flushed
5758
handler.emit(record)
58-
source_logger.flush_buffer()
5959

60-
# THEN the buffered message appears in the logger output
60+
# THEN the message is NOT in output yet (it's buffered)
61+
assert "test arg" not in stream.getvalue()
62+
63+
# AND when buffer is flushed, the buffered message appears in the logger output
64+
source_logger.flush_buffer()
6165
output = stream.getvalue()
6266
log_line = json.loads(output.strip())
6367
assert log_line["message"] == "test arg"
68+
69+
70+
def test_buffering_handler_emit_above_threshold_emits_directly(monkeypatch):
71+
# GIVEN a real Logger with buffer_at_verbosity="DEBUG" (default)
72+
# INFO logs should be emitted directly since INFO > DEBUG
73+
monkeypatch.setenv(constants.XRAY_TRACE_ID_ENV, "1-67c39786-5908a82a246fb67f3089263f")
74+
stream = io.StringIO()
75+
buffer_config = LoggerBufferConfig(max_bytes=10240, buffer_at_verbosity="DEBUG")
76+
source_logger = Logger(service="test3", buffer_config=buffer_config, stream=stream)
77+
handler = BufferingHandler(
78+
buffer_cache=source_logger._buffer_cache,
79+
buffer_config=source_logger._buffer_config,
80+
source_logger=source_logger,
81+
)
82+
record = logging.LogRecord(
83+
name="external",
84+
level=logging.INFO,
85+
pathname="",
86+
lineno=0,
87+
msg="direct message",
88+
args=(),
89+
exc_info=None,
90+
func=None,
91+
)
92+
record.stack_info = None
93+
94+
# WHEN the handler emits the record
95+
handler.emit(record)
96+
97+
# THEN the message appears immediately (not buffered)
98+
output = stream.getvalue()
99+
assert "direct message" in output

0 commit comments

Comments
 (0)