5a324f6554
Infra: cache_to_file, cache_to_sqlite, http_download_file, http_get_json, http_post_json, read_file_with_encoding, safe_extract_zip, scan_directory, setup_logger, normalize_zip_filenames. Tipos: 30+ tipos core (agent_action, context, task, message, parse_result...), 6 tipos datascience (entity_candidate, extraction_result...), 2 tipos infra. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
86 lines
2.5 KiB
Python
86 lines
2.5 KiB
Python
"""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)
|