from __future__ import annotations import logging import sys import structlog from app.core.config import settings def setup_logging() -> None: """Configure structlog for structured JSON logging.""" shared_processors: list[structlog.types.Processor] = [ structlog.contextvars.merge_contextvars, structlog.stdlib.add_logger_name, structlog.stdlib.add_log_level, structlog.stdlib.PositionalArgumentsFormatter(), structlog.processors.TimeStamper(fmt="iso"), structlog.processors.StackInfoRenderer(), structlog.processors.UnicodeDecoder(), ] if settings.log_format == "json": renderer: structlog.types.Processor = structlog.processors.JSONRenderer() else: renderer = structlog.dev.ConsoleRenderer(colors=True) 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, ], foreign_pre_chain=shared_processors, ) handler = logging.StreamHandler(sys.stdout) handler.setFormatter(formatter) root_logger = logging.getLogger() root_logger.handlers.clear() root_logger.addHandler(handler) root_logger.setLevel(getattr(logging, settings.log_level.upper(), logging.INFO)) # Reduce noise from third-party libraries for logger_name in ("uvicorn.access", "sqlalchemy.engine", "celery"): logging.getLogger(logger_name).setLevel(logging.WARNING) def get_logger(name: str | None = None) -> structlog.stdlib.BoundLogger: """Get a structlog logger instance.""" return structlog.get_logger(name)