Logging Básico para Depuración

Lectura
15 min~4 min lectura

Concepto clave

El logging es el proceso de registrar eventos, mensajes y datos durante la ejecución de una aplicación. En APIs de Machine Learning en producción, el logging actúa como el "diario de bitácora" de tu sistema, permitiéndote rastrear qué sucede en cada petición, identificar problemas y entender el comportamiento del modelo en tiempo real.

Imagina que tu API es un restaurante de alta cocina: el logging sería como las cámaras de seguridad y los registros de pedidos que te permiten reconstruir exactamente qué pasó cuando un cliente se queja de su plato. Sin logging, estarías "a ciegas", sin poder diagnosticar por qué una predicción falló o por qué la latencia aumentó repentinamente.

Cómo funciona en la práctica

En FastAPI, el logging se integra con el módulo estándar de Python logging. El flujo básico es: configurar un logger, definir niveles de severidad (DEBUG, INFO, WARNING, ERROR, CRITICAL), y registrar mensajes en puntos estratégicos de tu aplicación.

Paso a paso:

  1. Importa el módulo logging en tu archivo principal
  2. Configura el nivel de logging y el formato de los mensajes
  3. Crea instancias de logger en diferentes módulos
  4. Agrega handlers para enviar logs a diferentes destinos (consola, archivos, servicios externos)
  5. Instrumeta tu código con llamadas a logger.info(), logger.error(), etc.

Código en acción

Veamos un ejemplo básico de configuración de logging en FastAPI:

# main.py - ANTES (sin logging estructurado)
from fastapi import FastAPI
import numpy as np

app = FastAPI()

@app.post("/predict")
async def predict(data: dict):
    # Procesamiento del modelo
    result = some_ml_model.predict(data["features"])
    return {"prediction": result.tolist()}
# main.py - DESPUÉS (con logging estructurado)
import logging
from fastapi import FastAPI, Request
import numpy as np
import time

# Configuración básica del logger
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler('api.log'),
        logging.StreamHandler()
    ]
)

logger = logging.getLogger(__name__)
app = FastAPI()

@app.middleware("http")
async def log_requests(request: Request, call_next):
    start_time = time.time()
    
    response = await call_next(request)
    
    process_time = time.time() - start_time
    logger.info(
        f"{request.method} {request.url.path} - "
        f"Status: {response.status_code} - "
        f"Time: {process_time:.4f}s"
    )
    
    return response

@app.post("/predict")
async def predict(data: dict):
    logger.info(f"Prediction request received: {data.get('model_id', 'default')}")
    
    try:
        # Procesamiento del modelo
        features = np.array(data["features"])
        logger.debug(f"Features shape: {features.shape}")
        
        result = some_ml_model.predict(features)
        logger.info(f"Prediction successful: {result[:3]}...")
        
        return {"prediction": result.tolist()}
        
    except Exception as e:
        logger.error(f"Prediction failed: {str(e)}", exc_info=True)
        return {"error": "Prediction failed"}, 500

Errores comunes

  • Loggear demasiado o muy poco: Usar nivel DEBUG en producción inunda los logs; usar solo ERROR oculta información valiosa. Solución: Configurar INFO como default, DEBUG solo en desarrollo.
  • Logs no estructurados: Mensajes como "Error occurred" sin contexto. Solución: Incluir siempre IDs de petición, timestamps y datos relevantes.
  • No loggear excepciones completas: Usar solo str(e) pierde el stack trace. Solución: Usar logger.error("mensaje", exc_info=True).
  • Bloquear el hilo principal: Escribir logs sincrónicamente a disco puede ralentizar la API. Solución: Usar handlers asíncronos o envío por lotes.
  • Exponer datos sensibles: Loggear features completas o predicciones con información personal. Solución: Anonimizar o truncar datos sensibles en los logs.

Checklist de dominio

  • ✓ Configurar logging básico con niveles apropiados para producción
  • ✓ Incluir middleware para loggear todas las peticiones HTTP
  • ✓ Loggear el inicio y cierre de la aplicación correctamente
  • ✓ Manejar excepciones con logging de stack traces completos
  • ✓ Estructurar logs con formato consistente (timestamp, nivel, mensaje)
  • ✓ Separar logs de aplicación de logs de acceso
  • ✓ Implementar rotación de logs para evitar archivos gigantes

Implementar logging estructurado en una API de clasificación de imágenes

En este ejercicio, mejorarás una API existente de clasificación de imágenes agregando logging estructurado para facilitar la depuración en producción.

  1. Configuración inicial: Descarga el archivo image_api.py que contiene una API básica sin logging. Instala las dependencias: pip install fastapi uvicorn pillow numpy
  2. Configurar logging básico: Agrega configuración de logging al inicio del archivo que:
    • Use nivel INFO como default
    • Escriba a un archivo image_api.log
    • Muestre también en consola
    • Incluya timestamp, nombre del logger, nivel y mensaje
  3. Loggear peticiones: Implementa un middleware que loggue:
    • Método HTTP y endpoint
    • Status code de respuesta
    • Tiempo de procesamiento
    • ID del modelo usado (si aplica)
  4. Instrumentar el endpoint principal: Modifica el endpoint /classify para que:
    • Loggue INFO al recibir una imagen (tamaño, formato)
    • Loggue DEBUG con el preprocesamiento aplicado
    • Loggue ERROR con stack trace completo si falla la predicción
    • Loggue WARNING si la confianza de predicción es menor al 70%
  5. Probar y validar: Ejecuta la API con uvicorn image_api:app --reload, envía algunas peticiones con imágenes de prueba, y verifica que los logs contengan información útil para depurar.
Pistas
  • Usa logging.basicConfig() para la configuración inicial
  • Considera usar un ID único por petición para correlacionar logs
  • Para el middleware, recuerda calcular el tiempo antes y después de procesar la petición

Evalua tu comprension

Completa el quiz interactivo de arriba para ganar XP.