"""Logging configuration and setup.""" import logging import sys from pathlib import Path from logging.handlers import RotatingFileHandler import structlog from pythonjsonlogger import jsonlogger from wled_controller.config import get_config def setup_logging() -> None: """Configure structured logging for the application.""" config = get_config() # Ensure log directory exists log_path = Path(config.logging.file) log_path.parent.mkdir(parents=True, exist_ok=True) # Configure root logger root_logger = logging.getLogger() root_logger.setLevel(config.server.log_level) # Remove existing handlers root_logger.handlers.clear() # Console handler console_handler = logging.StreamHandler(sys.stdout) console_handler.setLevel(config.server.log_level) # File handler with rotation file_handler = RotatingFileHandler( filename=str(log_path), maxBytes=config.logging.max_size_mb * 1024 * 1024, backupCount=config.logging.backup_count, ) file_handler.setLevel(config.server.log_level) # Configure formatter based on format setting if config.logging.format == "json": formatter = jsonlogger.JsonFormatter( "%(asctime)s %(name)s %(levelname)s %(message)s" ) console_handler.setFormatter(formatter) file_handler.setFormatter(formatter) else: formatter = logging.Formatter( "%(asctime)s - %(name)s - %(levelname)s - %(message)s" ) console_handler.setFormatter(formatter) file_handler.setFormatter(formatter) root_logger.addHandler(console_handler) root_logger.addHandler(file_handler) # Configure structlog structlog.configure( processors=[ structlog.contextvars.merge_contextvars, structlog.processors.add_log_level, structlog.processors.StackInfoRenderer(), structlog.dev.set_exc_info, structlog.processors.TimeStamper(fmt="iso"), structlog.processors.JSONRenderer() if config.logging.format == "json" else structlog.dev.ConsoleRenderer(), ], wrapper_class=structlog.make_filtering_bound_logger(logging.NOTSET), context_class=dict, logger_factory=structlog.stdlib.LoggerFactory(), cache_logger_on_first_use=True, ) def get_logger(name: str) -> structlog.stdlib.BoundLogger: """Get a configured logger instance. Args: name: Logger name (typically __name__) Returns: Configured structlog logger """ return structlog.get_logger(name)