Files
egutierrez 9fd0ca9cac feat: funciones Python infra y tipos Python (core, datascience, infra)
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>
2026-04-05 17:11:43 +02:00

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)