Saltar a contenido

ADR-003: Estrategia de logs y observabilidad

Fecha: 2026-06-26 Estado: Aprobado Responsable: Orchestrator


Contexto

Venuo tiene eventos críticos que requieren trazabilidad:

  • Requests con tenant incorrecto o sin tenant — posible intento de acceso entre tenants.
  • Fallos en sincronización de ventas offline — pérdida de datos potencial.
  • Errores en cierre de caja — operación crítica de negocio.
  • Fallos en importación masiva — el usuario está esperando resultado.
  • Tareas Celery fallidas con retry agotado — pérdida de datos potencial.
  • 403 por feature flag bloqueado — tenant intentando acceder fuera de su plan.

Sin logs estructurados en producción no es posible diagnosticar incidentes ni auditar operaciones críticas.


Decisión

Estrategia en dos niveles según el momento del proyecto:

Nivel 1 — MVP (Sprint 0 en adelante)

  • Logging estructurado en JSON configurado en config/settings/base.py via el módulo LOGGING de Django. Salida a stdout (capturado por Docker) y a archivo con rotación diaria en /app/logs/.
  • Sentry para captura de errores y excepciones con contexto completo (tenant activo, usuario, endpoint, stack trace). SDK integrado en Django y Celery. Plan gratuito suficiente para MVP.

Nivel 2 — Producción con clientes reales

  • Loki + Grafana (self-hosted en docker-compose) para agregación, búsqueda histórica y dashboards. Se activa cuando el volumen de logs justifique búsquedas históricas. No requiere cambios en el código — solo agregar contenedores y configurar Promtail como agente.

Alternativas consideradas

Alternativa Ventajas Desventajas
Solo stdout + grep Cero configuración Inviable en producción con múltiples contenedores
Loki + Grafana desde Sprint 0 Stack completo desde el inicio Tres contenedores extra innecesarios en MVP — recursos desperdiciados
Sentry + Papertrail (elegida parcialmente) Sin infraestructura extra, plan gratuito generoso Logs salen del servidor — consideración de privacidad
ELK Stack (Elasticsearch + Logstash + Kibana) Muy potente Pesado, costoso en recursos, excesivo para MVP

Formato de log estructurado

Todos los logs del sistema siguen este formato JSON:

{
  "level": "ERROR",
  "timestamp": "2026-06-26T02:15:33Z",
  "tenant": "tenant_empresa_1",
  "user_id": 42,
  "endpoint": "/api/caja/cerrar/",
  "message": "Cierre de caja fallido — caja ya cerrada",
  "extra": {}
}

Los campos tenant y user_id se inyectan automáticamente via middleware de logging en core_api.


Eventos críticos con nivel mínimo requerido

Evento Nivel Destino
Request sin tenant válido WARNING stdout + Sentry
Fallo en sync de venta offline ERROR stdout + Sentry
Fallo en cierre de caja ERROR stdout + Sentry
Fallo en importación masiva ERROR stdout + Sentry
Tarea Celery con retry agotado CRITICAL stdout + Sentry
403 por feature flag INFO stdout
Login / logout INFO stdout
Apertura / cierre de caja exitoso INFO stdout

Rotación de archivos de log

Archivo Retención Criterio
django.log 7 días Rotación diaria — TimedRotatingFileHandler
celery.log 7 días Rotación diaria
nginx/access.log 7 días Rotación via logrotate en el contenedor Nginx

La carpeta /app/logs/ se monta como volumen en el host para persistir logs entre reinicios del contenedor.


Consecuencias

Positivas

  • Sentry captura excepciones con contexto de tenant y usuario sin código adicional en cada endpoint.
  • El logging estructurado en JSON es compatible con cualquier agregador futuro (Loki, ELK, Datadog).
  • La migración a Loki + Grafana en producción no requiere cambios de código — solo configuración de infraestructura.

Negativas / trade-offs

  • Sentry recibe datos de errores incluyendo contexto de tenants — revisar política de privacidad antes del lanzamiento. Configurar before_send para filtrar datos sensibles si es necesario.
  • Los archivos de log en disco crecen — la rotación de 7 días los mantiene acotados.

Impacto en el sistema

  • Backend: configurar módulo LOGGING en config/settings/base.py. Agregar middleware de logging en core_api que inyecte tenant y user_id en cada log. Integrar SDK de Sentry en config/settings/base.py.
  • Celery: configurar Sentry SDK para workers — captura excepciones de tareas automáticamente.
  • DevOps: volumen /app/logs/ en docker-compose.yml. Agregar SENTRY_DSN a .env.example. Documentar cómo agregar Loki + Grafana cuando corresponda.
  • Seguridad: configurar before_send en Sentry para filtrar campos sensibles (contraseñas, tokens, montos de caja) antes de enviar a los servidores de Sentry.

Documentos actualizados

  • docs/adr/ADR_INDEX.md
  • docs/STACK.md
  • docs/ARCHITECTURE.md
  • agents/DEVOPS_AGENT.md

Revisión futura

Activar Loki + Grafana cuando haya más de 3 tenants activos en producción o cuando el diagnóstico de incidentes requiera búsquedas históricas que grep no resuelva eficientemente.