El logging en Python permite registrar lo que ocurre durante la ejecución de un programa. En lugar de depender de mensajes temporales con print(), puedes clasificar eventos por gravedad, añadir fechas, guardar registros en archivos y conservar tracebacks completos cuando ocurre un error.
Esta herramienta resulta especialmente útil en automatizaciones, aplicaciones web, tareas programadas y proyectos que se ejecutan sin una persona observando el terminal. El módulo forma parte de la biblioteca estándar, por lo que no requiere instalación.
Logging frente a print()
print() es adecuado para mostrar información al usuario o hacer una comprobación rápida. Logging está diseñado para diagnóstico y operación:
- incluye niveles como DEBUG, INFO y ERROR;
- añade fechas y nombres de módulos;
- puede escribir en varios destinos;
- filtra mensajes según el entorno;
- registra excepciones con traceback;
- rota archivos para controlar su tamaño.
La guía de print() en Python explica cuándo la salida normal sigue siendo la mejor opción.
Los niveles de logging
Python define cinco niveles habituales:
- DEBUG: detalles técnicos para investigar el estado interno.
- INFO: operaciones normales completadas correctamente.
- WARNING: una situación inesperada que no detuvo el programa.
- ERROR: una operación concreta no pudo completarse.
- CRITICAL: un fallo grave puede impedir que la aplicación continúe.
Por defecto, el logger raíz muestra WARNING y niveles superiores. Por eso un mensaje INFO puede no aparecer hasta configurar el sistema.
Primer ejemplo
import logging
logging.basicConfig(level=logging.DEBUG)
logging.debug("Información detallada")
logging.info("La aplicación comenzó")
logging.warning("Queda poco espacio")
logging.error("No se pudo procesar el archivo")
logging.critical("La aplicación no puede continuar")El nivel configurado funciona como umbral. Con logging.ERROR, solamente se emitirían ERROR y CRITICAL.
El Logging HOWTO oficial de Python presenta la configuración básica y patrones recomendados.
Formatear los mensajes
import logging
logging.basicConfig(
level=logging.INFO,
format=(
"%(asctime)s | %(levelname)s | "
"%(name)s | %(message)s"
),
)
logging.info("Aplicación preparada")Campos frecuentes:
%(asctime)s: fecha y hora;%(levelname)s: nivel;%(name)s: nombre del logger;%(module)s: módulo que creó el mensaje;%(lineno)d: línea del código;%(message)s: texto final.
Añadir valores sin f-strings
La API de logging admite formato diferido:
user_id = 417
filename = "report.csv"
logging.info(
"El usuario %s comenzó a procesar %s",
user_id,
filename,
)El mensaje se formatea solamente si supera los filtros. Esto evita trabajo innecesario en mensajes DEBUG desactivados.
Guardar logs en un archivo
import logging
logging.basicConfig(
filename="application.log",
level=logging.INFO,
format="%(asctime)s | %(levelname)s | %(message)s",
encoding="utf-8",
)
logging.info("Importación iniciada")
logging.error("No se pudo guardar el registro 208")Una ruta relativa se resuelve desde el directorio actual, que puede diferir de la carpeta del script. La guía de pathlib en Python explica cómo construir una ruta explícita y portable.
El modo predeterminado añade mensajes al archivo. Utiliza filemode="w" solo cuando realmente quieras reemplazar el historial en cada ejecución.
Registrar excepciones y tracebacks
import logging
try:
result = 10 / 0
except ZeroDivisionError:
logging.exception("Error durante el cálculo")logging.exception() emite un ERROR e incluye automáticamente el traceback actual. Debe utilizarse normalmente dentro de un bloque except.
Logging no sustituye el manejo de errores. Primero decide si debes recuperar, reintentar, omitir o propagar. La guía de try y except en Python explica ese control de flujo.
Crear un logger por módulo
import logging
logger = logging.getLogger(__name__)
def load_configuration():
logger.info("Cargando configuración")__name__ identifica el módulo, lo que permite distinguir mensajes de shop.payments y shop.inventory. Una práctica habitual es que los módulos creen loggers y que el punto de entrada configure handlers y niveles.
Handlers y formatters
El logger crea registros. Un handler decide el destino y un formatter define la apariencia. El siguiente ejemplo muestra INFO en la consola y guarda DEBUG en un archivo:
import logging
logger = logging.getLogger("inventory")
logger.setLevel(logging.DEBUG)
console = logging.StreamHandler()
console.setLevel(logging.INFO)
file_handler = logging.FileHandler(
"inventory.log",
encoding="utf-8",
)
file_handler.setLevel(logging.DEBUG)
formatter = logging.Formatter(
"%(asctime)s | %(levelname)s | "
"%(name)s | %(message)s"
)
console.setFormatter(formatter)
file_handler.setFormatter(formatter)
logger.addHandler(console)
logger.addHandler(file_handler)
logger.debug("Cálculo detallado")
logger.info("Inventario actualizado")La consola recibe el evento INFO. El archivo recibe INFO y DEBUG.
Evitar mensajes duplicados
Los duplicados suelen aparecer cuando se añaden handlers varias veces o cuando un registro se propaga a un logger padre que también tiene handlers.
Configura el sistema una sola vez, durante el inicio. Para un logger completamente aislado, puedes utilizar:
logger.propagate = FalseNo desactives la propagación sin entender la jerarquía, porque la configuración central depende de ella en muchos proyectos.
Rotar archivos de log
import logging
from logging.handlers import RotatingFileHandler
logger = logging.getLogger("service")
logger.setLevel(logging.INFO)
handler = RotatingFileHandler(
"service.log",
maxBytes=1_000_000,
backupCount=3,
encoding="utf-8",
)
handler.setFormatter(
logging.Formatter(
"%(asctime)s | %(levelname)s | %(message)s"
)
)
logger.addHandler(handler)
logger.info("Servicio iniciado")Cuando el archivo alcanza aproximadamente un megabyte, comienza uno nuevo y conserva hasta tres copias anteriores. Para rotación por tiempo existe TimedRotatingFileHandler.
Ejemplo práctico de automatización
import logging
from pathlib import Path
logging.basicConfig(
filename="organizer.log",
level=logging.INFO,
format="%(asctime)s %(levelname)s %(message)s",
encoding="utf-8",
)
source = Path("incoming")
processed = 0
failed = 0
logging.info("Proceso iniciado")
for path in source.iterdir():
if not path.is_file():
continue
try:
size = path.stat().st_size
logging.debug(
"Archivo detectado: %s, %s bytes",
path.name,
size,
)
processed += 1
except OSError:
failed += 1
logging.exception(
"No se pudo procesar %s",
path,
)
logging.info(
"Proceso terminado: %s correctos, %s fallos",
processed,
failed,
)La guía de automatización con Python presenta scripts donde un historial como este resulta indispensable.
Configuración mediante diccionarios
Para aplicaciones grandes, logging.config.dictConfig() permite declarar loggers, handlers y formatos en una estructura central:
import logging.config
CONFIG = {
"version": 1,
"formatters": {
"standard": {
"format": "%(asctime)s %(levelname)s %(name)s %(message)s",
}
},
"handlers": {
"console": {
"class": "logging.StreamHandler",
"formatter": "standard",
"level": "INFO",
}
},
"root": {
"handlers": ["console"],
"level": "INFO",
},
}
logging.config.dictConfig(CONFIG)Seguridad y privacidad
No registres contraseñas, tokens, claves privadas, cookies de sesión, números completos de tarjetas ni datos personales innecesarios. Los logs suelen copiarse, archivarse y enviarse a servicios externos.
Utiliza identificadores internos, enmascara valores y define políticas de retención. Un mensaje útil describe el evento sin exponer secretos.
Depuración y logging
Logging conserva un historial, mientras que un depurador permite detener el programa e inspeccionar variables. Son herramientas complementarias. La guía de depuración de Python en VS Code explica breakpoints y seguimiento paso a paso.
Errores frecuentes
- Llamar a
basicConfig()después de emitir mensajes. - Registrar todo con el mismo nivel.
- Usar
logger.error()sin traceback en errores inesperados. - Añadir handlers repetidamente.
- Guardar datos sensibles.
- No rotar archivos de procesos permanentes.
- Escribir mensajes vagos sin identificadores útiles.
Referencia oficial
La referencia oficial del módulo logging documenta loggers, handlers, filtros, formatters y configuración.
Conclusión
Logging convierte mensajes aislados en un registro operativo. Empieza con niveles y basicConfig(), añade fechas y contexto, guarda los eventos importantes en archivos y utiliza logger.exception() para fallos inesperados. En proyectos grandes, configura handlers una sola vez y crea un logger por módulo.



