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.
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
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? |
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:
- El mensaje sirve para buscar, no para mirar: incluye los identificadores que localizarán el suceso después (pedido, usuario, petición).
- Cero secretos: contraseñas, tokens y cabeceras de autenticación nunca entran en el log; son lo más expuesto del sistema (temas 06 y 07).
- Nivel coherente:
INFOpara los sucesos del negocio,WARNpara lo recuperable,ERRORpara lo que rompe la operación, con su traza completa.
- El contexto viaja en el
MDC: el mensaje no repite petición ni usuario; están en el contexto y el patrón los imprime.
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).