recipe-app / utils /structured_logging.py
ronheichman's picture
Deploy recipe web app
ed3c16c verified
Raw
History Blame Contribute Delete
2.91 kB
"""Structlog configuration for the pipeline.
Call configure_structlog() once at application startup to set up
structured logging with human-readable console output in dev
and JSON output when use_json=True is passed.
Logs always go to stderr (console/PyCharm) AND to ``logs/pipeline.log``
(rotating, 10 MB x 3 backups) so the assistant can read them without
needing the IDE console.
"""
from __future__ import annotations
import logging
import sys
from logging.handlers import RotatingFileHandler
import structlog
from common.paths import REPO_ROOT
LOG_DIR = REPO_ROOT / "logs"
LOG_FILE = LOG_DIR / "pipeline.log"
LOG_MAX_BYTES = 10 * 1024 * 1024
LOG_BACKUP_COUNT = 3
def configure_structlog(use_json: bool) -> None:
"""Configure structlog processors and stdlib integration.
Uses ConsoleRenderer for human-readable dev output.
Integrates with stdlib logging so third-party libraries
(litellm, httpx, etc.) also emit structured logs.
Also attaches a RotatingFileHandler so output lands in
``logs/pipeline.log`` regardless of how the process is launched.
"""
shared_processors: list[structlog.types.Processor] = [
structlog.contextvars.merge_contextvars,
structlog.stdlib.add_log_level,
structlog.stdlib.add_logger_name,
structlog.processors.TimeStamper(fmt="iso"),
structlog.processors.StackInfoRenderer(),
structlog.processors.UnicodeDecoder(),
]
if use_json:
renderer: structlog.types.Processor = structlog.processors.JSONRenderer()
else:
renderer = structlog.dev.ConsoleRenderer()
structlog.configure(
processors=[
*shared_processors,
structlog.stdlib.ProcessorFormatter.wrap_for_formatter,
],
logger_factory=structlog.stdlib.LoggerFactory(),
wrapper_class=structlog.stdlib.BoundLogger,
cache_logger_on_first_use=True,
)
formatter = structlog.stdlib.ProcessorFormatter(
processors=[
structlog.stdlib.ProcessorFormatter.remove_processors_meta,
renderer,
],
)
console_handler = logging.StreamHandler(sys.stderr)
console_handler.setFormatter(formatter)
LOG_DIR.mkdir(parents=True, exist_ok=True)
file_handler = RotatingFileHandler(
LOG_FILE,
maxBytes=LOG_MAX_BYTES,
backupCount=LOG_BACKUP_COUNT,
encoding="utf-8",
)
plain_renderer = structlog.dev.ConsoleRenderer(colors=False)
file_formatter = structlog.stdlib.ProcessorFormatter(
processors=[
structlog.stdlib.ProcessorFormatter.remove_processors_meta,
plain_renderer,
],
)
file_handler.setFormatter(file_formatter)
root_logger = logging.getLogger()
root_logger.handlers.clear()
root_logger.addHandler(console_handler)
root_logger.addHandler(file_handler)
root_logger.setLevel(logging.INFO)