Saltar a contenido

ADR-0004 · Los logs del backend van a GlitchTip, no a un stack de logs propio

  • Estado: aceptado
  • Decisores: equipo OMS (iniciativa de observabilidad)
  • Fecha de la decisión: agosto 2026
  • Relacionado: ADR-0003 (no lo reemplaza: lo complementa)

Contexto y problema

Con ADR-0003 un error de producción ya se rastrea end-to-end, pero el evento llegaba "seco": los logger.log/warn/error del backend —incluidos los del sap-client, que registran cada llamada al Service Layer con su duración y su body— morían en la consola del contenedor, sin búsqueda ni retención. Para entender qué venía pasando antes de un error había que reproducirlo.

El plan original (fase 4 de analisis/observabilidad-errores/) era montar Loki + Grafana con nestjs-pino: dos servicios nuevos en el compose, un shipper de logs y un dashboard que mantener.

Al verificar la instancia se encontró que GlitchTip 6.1.5 ya trae la feature logs habilitada (enabledFeatures: ["logs","uptime","mcp"]): acepta logs estructurados por el mismo DSN, con retención de 90 días (7 en hot storage PostgreSQL, luego cold).

Opciones consideradas

  1. Logs nativos de GlitchTip (enableLogs) — elegida
  2. Loki + Grafana + nestjs-pino (plan original)
  3. Solo breadcrumbs, sin log persistente

Decisión

Un único GlitchtipLogger (registrado en NestFactory.create) reemplaza al logger de Nest y, sin tocar los ~200 new Logger(X) existentes, hace tres cosas con cada log/warn/error:

  1. Lo imprime por consola igual que antes.
  2. Lo agrega como breadcrumb, que viaja adjunto al evento si el request termina en error (responde "¿qué pasó antes de este error?").
  3. Lo envía como log estructurado a la sección Logs, con atributos request_id y context (responde "¿qué pasó en el request abc-123?", haya o no error).

Dos restricciones descubiertas contra la instancia real y que condicionan la implementación:

  • La búsqueda de logs de GlitchTip 6.1 es full-text sobre el cuerpo del mensaje: no filtra por atributos. Por eso el request_id se antepone también al texto ([rid:…]), no alcanza con mandarlo como atributo. Si alguien quita ese prefijo, se rompe la búsqueda por request.
  • Los contextos de arranque del framework (RouterExplorer, RoutesResolver, InstanceLoader) generan 200+ líneas por reinicio sin valor diagnóstico y consumían slots de los 50 breadcrumbs: se excluyen de GlitchTip.

El volumen se gobierna por entorno, sin redeploy: GLITCHTIP_LOGS_ENABLED (corta el envío; los breadcrumbs siguen) y GLITCHTIP_LOGS_MIN_LEVEL (log|warn|error).

Un log nunca genera un issue por sí solo. Para las degradaciones silenciosas (catch que continúa con un fallback) existe captureWarning(flujo, mensaje), explícito y de lista corta.

Consecuencias

Positivas

  • Se cancela la fase 4: cero infraestructura nueva — ni Loki, ni Grafana, ni shipper, ni dos servicios más que mantener y monitorear.
  • Los logs quedan correlacionados con los errores en la misma herramienta: del issue al request completo sin cambiar de pestaña.
  • Volumen medido en producción: ~43.000 logs/día ≈ 30 MB/día ≈ 2,7 GB con la retención de 90 días. Asumible.

Negativas / deuda asumida

  • Se resignan los dashboards de Grafana (tasa de errores por endpoint, p95 de duración de llamadas SAP). Si esa necesidad aparece, se replantea con un ADR nuevo.
  • La búsqueda es full-text: no hay filtros por atributo ni agregaciones.
  • Los logs quedan atados a la disponibilidad y al disco de la instancia de GlitchTip; conviene vigilar el crecimiento de su PostgreSQL.