Decisiones de arquitectura

ADR-0006: Los logs llegan a OpenTelemetry por un appender de Logback

Estado: Aceptado · Fecha: 2026-09-25

Contexto

application.yaml configuraba desde M0 el destino OTLP de las tres señales, incluido management.opentelemetry.logging.export.otlp.endpoint. Las trazas y las métricas salían; los logs no. La propiedad es real y Spring Boot 4.1 autoconfigura con ella el SdkLoggerProvider, el BatchLogRecordProcessor y el exportador OTLP, pero nada alimenta ese exportador: SLF4J y Logback no conocen el SDK de OpenTelemetry. El starter spring-boot-starter-opentelemetry no trae el puente.

El resultado es la peor forma de fallo: una tubería completa, bien configurada y vacía. El diagnóstico ya estaba anotado en docs/diagrams/telemetria_01_flowchart_componentes.mmd como flecha discontinua con la leyenda «requiere appender», y en telemetria_05 como rama pendiente del árbol de decisión.

Esto importa aquí más que en una aplicación cualquiera. Un LIS se diagnostica correlacionando lo que pasó (log) con por dónde pasó (traza). Sin logs en el backend, la correlación se hace a mano contra la consola del proceso, que en producción no se conserva.

Decisión

Los logs llegan a OpenTelemetry mediante opentelemetry-logback-appender-1.0, declarado en src/main/resources/logback-spring.xml como appender OTEL del logger raíz junto al de consola, e instalado en el arranque por OpenTelemetryAppenderInitializer, porque Logback se configura antes que el contexto de Spring y el appender nace sin SDK.

Se descarta el agente Java de OpenTelemetry, que instrumentaría sin tocar código pero duplicaría las señales que ya produce el starter.

Regla de versionado, y es la parte operativa de esta decisión: el BOM de Spring Boot gestiona io.opentelemetry:opentelemetry-bom pero no io.opentelemetry.instrumentation, donde vive el appender. Su versión se fija a mano en el pom.xml y se elige por el opentelemetry-api que declara, que debe ser idéntico al SDK que gestiona Boot. Hoy: Boot 4.1.0 gestiona el SDK 1.62.0, y 2.28.1-alpha es la versión del appender que declara opentelemetry-api 1.62.0. Las contiguas no sirven: 2.30.0-alpha pide 1.64.0 y 2.31.1-alpha pide 1.65.0.

Al subir Spring Boot se comprueba el SDK que pasa a gestionar y se busca la versión del appender que lo declare, en lugar de subir el appender a la última.

Cómo se verifica

ExportacionLogsOtelTest afirma dos cosas sobre la aplicación arrancada: que el logger raíz tiene el appender OTEL, y que un log emitido por SLF4J dentro de un span activo llega al SDK con el mismo traceId y spanId que ese span. Sustituye el exportador OTLP por un procesador en memoria, así que no depende de la red ni de que haya un backend escuchando.

Se comprobó rompiéndolo a propósito, en los dos puntos donde este cableado puede caerse: sin el <appender-ref ref="OTEL"/> fallan los dos tests; sin el @Component del inicializador falla el de correlación. Ese era el objetivo, porque ninguno de esos dos daños rompe la compilación.

La exportación OTLP real es otra cosa y se verificó a mano contra Grafana LGTM; el procedimiento y su resultado están en wiki/TELEMETRIA.md.

Consecuencias

  • Las tres señales llegan al backend, y un log se puede saltar a su traza por traceId.
  • El fallo silencioso pasa a romper el build: el cableado tiene dueño y test. − Una dependencia con versión fijada a mano, que no se actualiza sola y que hay que revisar en cada subida de Spring Boot. La regla de arriba existe para que esa revisión no se haga a ojo: esta decisión se tomó después de que un salto a una versión inexistente del appender dejara el repositorio sin compilar. − logback-spring.xml pasa a existir. Hereda los defaults de Boot con <include>, así que el formato de consola sigue siendo el de Spring Boot, pero ahora hay un archivo más que puede desalinearse de la configuración por propiedades.

Lo que esta decisión NO resuelve

No fija el formato de los logs. Siguen siendo texto de consola con el patrón por defecto de Spring Boot; 00-agents.md pide logs estructurados con traceId y eso exige decidir logging.structured.format.console y en qué perfiles se aplica.

No hay identificador de correlación en la respuesta HTTP: de una petición fallida todavía no se puede saltar a su traza sin buscar por tiempo. Es la deuda declarada que S1.01 recoge.

No captura atributos del MDC. Cuando haga falta, se eligen por nombre: captureMdcAttributes con comodín exportaría al backend cualquier cosa que alguien meta en el MDC, y la regla 14 de 00-agents.md prohíbe PII en logs.

No genera spans de consultas SQL; eso requiere instrumentación aparte de JDBC y se evalúa en S1.22.

Alternativas descartadas

Agente Java de OpenTelemetry: instrumenta sin tocar el código, pero combinado con el starter duplica señales. Además el Dockerfile tendría que transportar y arrancar el agente.

No exportar logs y correlacionar contra la consola: es lo que había. Funciona en desarrollo y no funciona en producción, donde la consola del proceso no se conserva.

Appender propio sobre la API de logs del SDK: unas pocas decenas de líneas y ningún problema de versiones, a cambio de mantener un puente que la comunidad de OpenTelemetry ya mantiene, con los detalles de contexto, severidad y excepciones ya resueltos.