El Módulo logging
El paquete logging de la biblioteca estándar dirige los registros de log desde los loggers a través de handlers hacia salidas (consola, archivos, syslog). Configure niveles, formatters y la jerarquía de loggers una vez al inicio; las bibliotecas registran en loggers con nombre sin configurar handlers.
Receta
import logging
logging.basicConfig(level=logging.INFO, format="%(levelname)s %(name)s: %(message)s")
log = logging.getLogger(__name__)
log.info("iniciando servidor")
log.warning("disco al %s%% lleno", 92)Cuándo usar esto:
- Los servicios de producción necesitan diagnósticos nivelados y filtrables.
- Las bibliotecas emiten logs sin elegir el destino de salida.
- Múltiples handlers (stderr + archivo rotatorio) por logger.
- Los campos estructurados se mapean posteriormente desde los atributos de LogRecord.
- Reemplazar la depuración con
printpor rastros operables.
Ejemplo de Trabajo
import logging
from logging.handlers import RotatingFileHandler
def configure_logging() -> None:
root = logging.getLogger()
root.setLevel(logging.INFO)
fmt = logging.Formatter("%(asctime)s %(levelname)s %(name)s %(message)s")
console
Lo que esto demuestra:
- Alternativa a
basicConfig: adjuntar handlers explícitamente a la raíz. - Los loggers con nombre heredan el nivel y los handlers de la raíz.
- Formateo perezoso con
%: los argumentos se posponen si el nivel los filtra. log.exceptionincluye el traceback cuando se llama dentro de unexcept.
Inmersión Profunda
Cómo Funciona
- Los loggers forman una jerarquía por nombres con puntos (
app.api,app.db). - Los registros fluyen: logger -> filtros -> handlers -> formatters -> IO.
- Niveles: DEBUG < INFO < WARNING < ERROR < CRITICAL.
propagatecontrola la propagación a los loggers padres.- Las bibliotecas solo llaman a
getLogger(__name__); no configuran handlers.
Componentes
| Pieza | Rol |
|---|---|
| Logger | Punto de entrada log.info(...) |
| Handler | Receptor de salida |
| Formatter | Cadena de formato |
| Filter | Puerta de enlace adicional |
Errores Comunes
basicConfigno hace nada si la raíz ya está configurada: la segunda llamada se ignora. Solución: configurar una vez en elmaino usardictConfig.- Logs duplicados por
propagate: el hijo y la raíz tienen handlers. Solución: establecerlogger.propagate = Falseo usar handlers solo en la raíz. - f-strings en llamadas de log: evaluación anticipada incluso cuando se filtra. Solución:
log.info("x=%s", x). printen bibliotecas: omite niveles y agregación. Solución: usar solologging.- DEBUG en producción siempre activado: volumen y fuga de secretos. Solución: nivel controlado por entorno, muestrear
debug.
Alternativas
| Alternativa | Usar Cuando | No Usar Cuando |
|---|---|---|
| structlog | Pipelines JSON, variables de contexto | Scripts pequeños |
| OpenTelemetry logs | Trazas/métricas/logs unificados | Aún no hay colector |
| Exploración de REPL | Servicios |
Preguntas Frecuentes
¿Dónde configuro el logging?
En el punto de entrada de la aplicación (main, ciclo de vida de FastAPI, diccionario LOGGING de Django) - nunca en módulos de biblioteca.
¿Qué nombre de logger debo usar?
logging.getLogger(__name__) para que los nombres reflejen la estructura del paquete.
¿Cómo registro excepciones?
log.exception("mensaje") dentro de un bloque except, o log.error("mensaje", exc_info=True).
¿`dictConfig` vs `basicConfig`?
dictConfig escala para múltiples handlers/formatters y anulaciones de pruebas.
¿Cómo silencio bibliotecas ruidosas?
logging.getLogger("urllib3").setLevel(logging.WARNING).
¿Son seguros los loggers para hilos (thread-safe)?
Sí - el módulo de logging serializa la emisión de handlers; aun así, evite el contexto mutable sin bloqueos.
¿Cómo funcionan los archivos rotatorios?
RotatingFileHandler por tamaño; TimedRotatingFileHandler por intervalo - emparejar con envío de logs.
¿Debo usar `LoggerAdapter`?
Para contexto estático (prefijo de ID de solicitud) antes de las bibliotecas de logging estructurado.
¿Qué pasa con los niveles de log en las pruebas?
La fixture caplog en pytest captura registros por nombre de logger y nivel.
¿Cómo se compara esto con el logging de Django?
Django usa dictConfig internamente - mismos conceptos, valores predeterminados proporcionados por el framework.
Relacionado
- Logging Estructurado y Contextual - JSON y IDs de correlación
- Advertencias y Deprecaciones - separado del logging
- Conceptos Básicos de Excepciones - registrar y lanzar
- Mejores Prácticas de Errores y Logging - políticas
Versiones de Stack: Esta página fue escrita para Python 3.14.0 (estable 3.14, mantenimiento 3.13), FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, y uv 0.6+.