Tabla de Contenidos

09 - Logs e instrumentación

En 1.º DAW ya se usa el sistema de logs de Spring Boot: el evento de log con su nivel, la fachada SLF4J, la implementación Logback de serie y el nivel por paquete en application.properties. Esas bases bastaban con la aplicación en el equipo local: la consola lo mostraba todo y cada fallo se reproducía a mano. Una vez desplegada, la misma aplicación corre en un servidor o contenedor donde la consola es efímera, el acceso es remoto y las peticiones se solapan: el registro pasa a ser la principal herramienta para saber qué ocurre y para reconstruir después qué le sucedió a una petición concreta.

1. El log en producción

El cambio de contexto cambia la función del log: ya no es una versión mejorada del printf de desarrollo, sino una fuente de información sobre un proceso que nadie está mirando en directo. El diagnóstico se hace después de los hechos, leyendo lo que la aplicación registró por el camino.

Condición del despliegue Qué exige al registro
El proceso muere o se reinicia y la consola desaparece Persistencia: fichero con retención limitada (rotación)
Peticiones concurrentes comparten la misma salida Correlación: cada suceso lleva el contexto de la petición que lo originó
El accidente depende del entorno (carga, datos, hora) Sucesos cronológicos en formato estable: buscar, filtrar, comparar

Las preguntas que el log de producción debe responder no se conocen por adelantado: ¿qué falló esta noche?, ¿qué hizo esta petición?, ¿cómo se comportó tras el despliegue?. Responderlas exige conservar los sucesos con contexto suficiente y poder localizarlos.

2. Configuración de la salida

2.1 Fichero y rotación

Con una propiedad el registro se guarda en fichero, pero uno que crece sin control acabaría llenando el disco. La rotación trocea el historial, comprime los ficheros cerrados y limita cuántos se conservan:

logging.file.name=logs/bookstore.log
logging.logback.rollingpolicy.max-file-size=10MB
logging.logback.rollingpolicy.max-history=30

Al llegar al tamaño máximo, el fichero se archiva comprimido y se abre uno nuevo; solo se conservan los últimos 30. El nombre del fichero activo no cambia, así que las herramientas de lectura siempre consultan la misma ruta.

Estas propiedades son de Spring Boot: las traduce a la configuración de la implementación (Logback), por eso viven bajo logging.logback.rollingpolicy. Con otra implementación los equivalentes cambian de nombre; el concepto de rotación es el mismo.

2.2 El nivel según el entorno

El nivel de cada logger se configura por paquete; los perfiles de Spring permiten que la misma aplicación registre de manera distinta según dónde corra:

# application.properties — desarrollo: consola y máximo detalle propio
logging.level.com.bookstore=DEBUG

# application-prod.properties — producción: fichero y solo lo relevante
logging.file.name=logs/bookstore.log
logging.logback.rollingpolicy.max-file-size=10MB
logging.logback.rollingpolicy.max-history=30
logging.level.com.bookstore=INFO
logging.level.org.springframework=WARN

Activar el perfil con spring.profiles.active=prod cambia destino y detalle sin recompilar. El detalle amplio (DEBUG) es una herramienta de desarrollo; en producción se reserva para ventanas de diagnóstico puntuales.

3. Correlación de peticiones con MDC

En desarrollo las peticiones llegan una a una y se distinguen solas; en producción una ráfaga entrelaza en la salida los eventos de decenas de flujos distintos. Un mensaje Error al procesar el pedido aportaría bien poco contexto: ¿qué pedido, qué petición, qué cliente? La correlación consiste en que cada evento del flujo de una petición quede marcado con el identificador de esa petición.

Tomcat atiende cada petición en un hilo de un pool (visto en 1.º DAW), así que durante la petición todos los logs del flujo salen del mismo hilo. Aprovechando esa correspondencia, SLF4J ofrece el MDC (*Mapped Diagnostic Context*): un mapa de contexto por hilo que el patrón de formato puede incluir en cada mensaje con %X{clave}.

El filtro situado delante del DispatcherServlet abre el flujo y prepara el contexto:

@Component
public class RequestTrackingFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain chain) throws ServletException, IOException {

        String requestId = UUID.randomUUID().toString().substring(0, 8);
        MDC.put("requestId", requestId);
        try {
            chain.doFilter(request, response);
        } finally {
            MDC.clear();
        }
    }
}

Los eventos que el hilo emita durante la petición llevan el identificador, pero solo si el patrón lo declara:

logging.pattern.level=%5p [%X{requestId}]

2026-10-21 14:07:21  INFO  [3e9f2c8a] BookService - Pedido 42 cancelado

RequestTrackingFilterControladorServicioFichero de logRequestTrackingFilterControladorServicioFichero de logRequestTrackingFilterControladorServicioFichero de logpetición HTTP(requestId en el MDC)coordina el caso de usoevento [3e9f2c8a]evento [3e9f2c8a]resultadorespuesta HTTPel hilo vuelve al pool(finally limpia el MDC)

El hilo no muere al terminar la petición: vuelve al pool y atenderá otra. Si el MDC no se limpia en el finally, la segunda petición heredaría el identificador de la primera y la correlación se corrompería en silencio.

El filtro de autenticación del tema 07 es el otro punto natural de correlación: una vez validado el token, el principal identificado entra también en el MDC y cada suceso del flujo lleva petición y usuario. Un límite que tener presente: el contexto pertenece al hilo, y en el procesamiento asíncrono del tema 08 el hilo cambia — el contexto no viaja solo; quien lanza el trabajo asíncrono debe propagarlo explícitamente si quiere mantener la correlación.

4. Instrumentación: Actuator

Los logs solo dicen lo que el código escribió en cada mensaje. Quedan preguntas que el registro no cubre: ¿está sano el proceso?, ¿cuánta memoria consume?, ¿cuántas peticiones por segundo atiende? La observabilidad completa combina las tres ventanas:

Ventana Pregunta que responde
Logs ¿Qué pasó, suceso a suceso?
Métricas ¿Cómo se comporta el sistema, agregado y a lo largo del tiempo?
Health ¿Está vivo y listo para recibir tráfico?

Spring Boot Actuator expone las ventanas de métricas y salud como endpoints operativos. La dependencia spring-boot-starter-actuator y la exposición se declaran en propiedades:

management.endpoints.web.exposure.include=health,info,metrics

{
  "status": "UP"
}

Endpoint Pregunta que responde
/actuator/health ¿Está operativa cada dependencia: base de datos, broker, disco?
/actuator/info ¿Qué versión está desplegada?
/actuator/metrics ¿Cuánta memoria, peticiones HTTP por código de estado, pool de conexiones?
Estos endpoints son operativos: exponen incluso el estado interno del sistema. En producción se mantienen al mínimo necesario y se quedan tras la misma autenticación que construye el tema 07, nunca abiertos al anonimato.

5. La disciplina del registro

La configuración decide qué se guarda y dónde; la calidad del contenido distingue un log diagnóstico de una cascada de frases. Unas reglas de partida suficientes:

En el tema siguiente la función en la nube cierra el círculo: en una Lambda el registro es prácticamente la única ventana de observación; el context que la plataforma entrega sirve de logger y su salida llega al servicio de logs de la nube (CloudWatch).