Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion tests/pytest/logger/test_logger_buffer.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,38 @@ def test_multiple_logs_stored(test_logger):
assert msg in logs[i]["message"]

def test_log_levels_stored(test_logger):
"""Debug messages are filtered when print_debug is False (default)."""
logger, buffer = test_logger
logger.debug("Debug message")
logger.info("Info message")
logger.warning("Warning message")
logger.error("Error message")
logs = buffer.get_logs()
levels = [log["level"] for log in logs]
assert levels == ["DEBUG", "INFO", "WARNING", "ERROR"]
assert levels == ["INFO", "WARNING", "ERROR"]


def test_log_levels_stored_with_print_debug(test_logger):
"""Debug messages pass through when print_debug is enabled."""
from webserver.logger.config import LoggerConfig
from webserver.logger import get_logger

LoggerConfig.print_debug = True
try:
# Re-fetch logger to apply new level to handlers
logger, buffer = get_logger("test_logger", use_buffer=True)
buffer.clear()
logger.debug("Debug message")
logger.info("Info message")
logger.warning("Warning message")
logger.error("Error message")
logs = buffer.get_logs()
levels = [log["level"] for log in logs]
assert levels == ["DEBUG", "INFO", "WARNING", "ERROR"]
finally:
LoggerConfig.print_debug = False
# Re-fetch to restore INFO level on handlers
get_logger("test_logger", use_buffer=True)
Comment on lines +41 to +61
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test mutates global configuration (LoggerConfig.print_debug) and relies on cleanup in finally. To improve isolation (especially if the suite ever runs tests concurrently), prefer using pytest’s monkeypatch fixture (or an equivalent config fixture) to scope the config change to this test and automatically restore the original value.

Copilot uses AI. Check for mistakes.

def test_normalize_no_microseconds(test_logger):
logger, buffer = test_logger
Expand Down
4 changes: 1 addition & 3 deletions webserver/logger/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import logging
import sys

from .logger import get_logger
from .parser import LogParser
from .bufferhandler import BufferHandler
from .formatter import JsonFormatter, HumanReadableFormatter
Expand Down Expand Up @@ -49,7 +48,6 @@ def get_logger(name="runtime", use_buffer: bool = False):

# Always update all handler levels to reflect current config
for h in logger.handlers:
if isinstance(h, logging.StreamHandler):
h.setLevel(effective_level)
h.setLevel(effective_level)

return logger, shared_buffer_handler
28 changes: 0 additions & 28 deletions webserver/logger/logger.py

This file was deleted.

9 changes: 9 additions & 0 deletions webserver/logger/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,17 @@
LEVEL_MAP = {
"DEBUG": logging.DEBUG,
"INFO": logging.INFO,
"WARN": logging.WARNING,
"WARNING": logging.WARNING,
"ERROR": logging.ERROR,
"CRITICAL": logging.CRITICAL,
}

# Normalize non-standard level names to Python conventions
LEVEL_NORMALIZE = {
"WARN": "WARNING",
}
Comment on lines 9 to +21
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LEVEL_NORMALIZE maps "WARN" to "WARNING", so the "WARN" entry in LEVEL_MAP becomes redundant (and can mislead future readers into thinking both forms are expected post-normalization). Consider removing "WARN" from LEVEL_MAP (or, alternatively, removing the normalization and relying solely on LEVEL_MAP if you don’t need to rewrite the emitted "level" field to "WARNING").

Copilot uses AI. Check for mistakes.


class LogParser:
def __init__(self, collector_logger: logging.Logger):
Expand All @@ -37,7 +43,9 @@ def parse_and_log(self, line: str):
# Preserve incoming JSON fields, but ensure timestamp is present
parsed.setdefault("timestamp", str(timestamp))
level_name = parsed.get("level", "INFO")
level_name = LEVEL_NORMALIZE.get(level_name, level_name)
level = LEVEL_MAP.get(level_name, logging.INFO)
parsed["level"] = level_name
Comment on lines 45 to +48
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The normalization is currently case-sensitive. If any producers emit "warn" / "info" / etc., they won’t be normalized or mapped as intended. Consider normalizing case (e.g., converting to upper() before applying LEVEL_NORMALIZE/LEVEL_MAP) to make parsing more robust.

Copilot uses AI. Check for mistakes.
log_entry = parsed
else:
raise ValueError("Not a valid log JSON dict")
Expand All @@ -46,6 +54,7 @@ def parse_and_log(self, line: str):
match = LOG_PATTERN.match(sline)
if match:
level_name = match["level"]
level_name = LEVEL_NORMALIZE.get(level_name, level_name)
level = LEVEL_MAP.get(level_name, logging.INFO)
message = match["message"]
else:
Expand Down
Loading