"""Configuracion de logger con rotacion de archivo y salida a consola.""" import logging import logging.handlers import os import sys from datetime import datetime def setup_logger( name: str = "app", log_dir: str = "logs", level: int = logging.DEBUG, ) -> logging.Logger: """Configura un logger con dual output: archivo rotante y consola. Crea el directorio de logs si no existe. El archivo usa nivel DEBUG con formato detallado y rotacion diaria (maxBytes=10MB, backupCount=5). La consola usa nivel INFO con formato simplificado. Es idempotente: si el logger ya tiene handlers no se duplican. Args: name: Nombre del logger (identifica la instancia en el sistema de logging). log_dir: Directorio donde se guardan los archivos de log. level: Nivel minimo del logger principal (por defecto DEBUG). Returns: Logger configurado con handler de archivo y handler de consola. """ os.makedirs(log_dir, exist_ok=True) logger = logging.getLogger(name) logger.setLevel(level) logger.propagate = False # Idempotente: si ya tiene handlers no agregar mas if logger.handlers: return logger fmt_detailed = logging.Formatter( "[%(asctime)s] %(levelname)s [%(name)s.%(funcName)s:%(lineno)d] %(message)s" ) fmt_simple = logging.Formatter( "[%(asctime)s] %(levelname)s: %(message)s" ) # File handler con rotacion por tamano log_filename = os.path.join(log_dir, f"{datetime.now():%Y-%m-%d}.log") file_handler = logging.handlers.RotatingFileHandler( log_filename, maxBytes=10 * 1024 * 1024, # 10 MB backupCount=5, encoding="utf-8", ) file_handler.setLevel(logging.DEBUG) file_handler.setFormatter(fmt_detailed) # Console handler if sys.platform == "win32": try: sys.stdout.reconfigure(encoding="utf-8", errors="replace") # type: ignore[attr-defined] except AttributeError: pass console_handler = logging.StreamHandler(sys.stdout) console_handler.setLevel(logging.INFO) console_handler.setFormatter(fmt_simple) logger.addHandler(file_handler) logger.addHandler(console_handler) return logger def get_logger(name: str = "app") -> logging.Logger: """Devuelve un logger existente o lo crea con setup_logger. Args: name: Nombre del logger. Returns: Logger configurado. """ logger = logging.getLogger(name) return logger if logger.handlers else setup_logger(name)