""" Structured logging setup via structlog. Production: JSON output, machine-parseable. Development: colourised console output. Call configure_logging() once at application startup before any loggers are created. """ from __future__ import annotations import logging import sys import structlog def configure_logging(log_level: str = "INFO", env: str = "development") -> None: """Configure structlog with environment-appropriate rendering.""" 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(), ] if env == "production": processors: list[structlog.types.Processor] = [ *shared_processors, structlog.processors.dict_tracebacks, structlog.processors.JSONRenderer(), ] renderer = structlog.processors.JSONRenderer() else: processors = [ *shared_processors, structlog.dev.ConsoleRenderer(colors=True), ] renderer = structlog.dev.ConsoleRenderer(colors=True) structlog.configure( processors=processors, wrapper_class=structlog.make_filtering_bound_logger( getattr(logging, log_level.upper(), logging.INFO) ), context_class=dict, logger_factory=structlog.PrintLoggerFactory(sys.stdout), cache_logger_on_first_use=True, ) # Silence noisy third-party loggers for noisy in ("uvicorn.access", "httpx", "transformers", "datasets"): logging.getLogger(noisy).setLevel(logging.WARNING)