Logs
Cómo hace Spring Boot los logs
En la sección de Cross-Cutting Concerns se introdujo logging como un mecanismo fundamental para entender el comportamiento de tu aplicación. Ahora, vamos a profundizar en cómo Spring Boot maneja los logs internamente.
En Spring Boot, SLF4J y Logback trabajan juntos para proveer una configuración de logging.
- SLF4J = Simple Logging Facade for Java. Pensalo como una API de logging (una capa de abstracción). Tu código de aplicación habla con SLF4J, no directamente con una implementación específica de logging. Esto es brillante porque desacopla tu código de cómo los logs se escriben.
- Logback = La implementación de logging real (el motor que hace el trabajo pesado). Es el backend de logging por defecto que SLF4J usa en una aplicación Spring Boot.
Aunque Spring Boot incluye SLF4J y Logback automáticamente y no se requiere configuración para tener logging básico funcionando, mejorar la experiencia de logging es recomendable. Logging estructurado con trace IDs, enmascaramiento centralizado de datos sensibles, y logging consistente de request/response hacen que debuggear problemas de producción sea significativamente más fácil.
Archivos modificados
- Java
- Kotlin
- Groovy
Dependencias
- Java
- Kotlin
- Groovy
implementation 'org.springframework.boot:spring-boot-starter-aspectj'
implementation 'org.springframework.boot:spring-boot-starter-opentelemetry'
implementation("io.github.oshai:kotlin-logging-jvm:8.0.01")
implementation("org.springframework.boot:spring-boot-starter-aspectj")
implementation("org.springframework.boot:spring-boot-starter-opentelemetry")
kotlin-logging is a wrapper
around SLF4J that provides a more idiomatic Kotlin way to log messages. It
does not replace SLF4J or Logback; instead, it simplifies logging calls in
Kotlin by using extension functions and lazy evaluation for messages. The
logging hierarchy remains: Kotlin code → kotlin-logging → SLF4J → Logback
(Spring Boot default)
implementation 'org.springframework.boot:spring-boot-starter-aspectj'
implementation 'org.springframework.boot:spring-boot-starter-opentelemetry'
Evitar publicación local de logs OTLP
Cuando agregás spring-boot-starter-opentelemetry a tus dependencias, Spring Boot configura automáticamente exportadores de OpenTelemetry para publicar métricas y traces vía OTLP (OpenTelemetry Protocol).
El problema es que en desarrollo local, típicamente no tenés un colector OTLP corriendo. Spring Boot va a intentar repetidamente conectarse al endpoint OTLP default y fallar, inundando tu consola con mensajes de error de conexión que ahogan tus logs reales de aplicación.
Para evitar este ruido durante desarrollo local, deshabilitá la exportación de métricas OTLP en tu perfil de dev:
- Java
- Kotlin
- Groovy
spring:
application:
name: spring_java
management:
otlp:
metrics:
export:
enabled: false
spring:
application:
name: spring_kotlin
management:
otlp:
metrics:
export:
enabled: false
spring:
application:
name: spring_groovy
management:
otlp:
metrics:
export:
enabled: false
De ahora en adelante, ejecutá la tarea Gradle bootRun con la variable de entorno SPRING_PROFILES_ACTIVE=dev para activar el perfil de dev:
SPRING_PROFILES_ACTIVE=dev ./gradlew bootRun
Log aspect
Usar un Aspect para logging es un ejemplo clásico de AOP en acción. En lugar de esparcir declaraciones de log por toda tu lógica de negocio (que ensucia el código y mezcla concerns), definís el comportamiento de logging una vez en un módulo separado. Este aspecto luego intercepta automáticamente llamadas a métodos que te importan y aplica la lógica de logging sin que el código objetivo se dé cuenta.
Para un análisis más profundo, chequeá la sección de AOP en Cross-Cutting Concerns.
- Java
- Kotlin
- Groovy
Log filter
A veces, un request puede ni siquiera llegar a tus controladores Spring MVC. Al loguear a nivel de Servlet, te da visibilidad sobre cada request entrante y respuesta saliente, independientemente de si golpea los endpoints específicos de tu aplicación.
Esto es útil para debuggear issues como fallos de autenticación, problemas de ruteo, o requests que son bloqueados más arriba en la cadena de filtros.
- Java
- Kotlin
- Groovy
Tracing
¿Alguna vez intentaste debuggear un issue en un sistema ocupado rebuscándote en un archivo de log masivo? Es como tratar de encontrar una aguja específica en un pajar de agujas.
Acá es donde entra el tracing. Al asignar un ID único (un 'Trace ID') a cada request entrante, podés etiquetar cada entrada de log generada durante el ciclo de vida de ese request. De repente, podés filtrar todo el archivo de log para ver el viaje de solo un request a través de múltiples métodos, servicios, o threads.
- Java
- Kotlin
- Groovy
Enmascarar datos sensibles en logs con Logback
Es importante enmascarar datos sensibles cuando logueamos (por ejemplo, contraseñas, SSN, etc.). Enmascará los logs de forma central configurando reglas de enmascaramiento para todas las entradas de log producidas por Logback.
Creá
MaskingPatternLayout:- Java
- Kotlin
- Groovy
Agregá patrones regex en tags
maskPatterndentro delogback.xml:- Java
- Kotlin
- Groovy
Ver cómo se ven los logs
Corré el siguiente comando curl (con valores placeholder) para ver todo funcionando: enmascaramiento de datos sensibles, el aspect interceptando invocaciones de métodos del controlador, cómo los logs contienen el trace, y el filtro imprimiendo el HTTP request y response.
curl -s --request GET --url http://localhost:8080/api/films/42 --header 'Accept: application/json' --header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c' --header 'Cookie: JSESSIONID=A1B2C3D4E5F6G7H8I9J0; auth_token=secret123token456' --header 'Proxy-Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=' --header 'User-Agent: Mozilla/5.0 (Test Client)' --header 'X-API-Key: super-secret-api-key' --header 'X-Auth-Token: super-secret-auth-token-12345' --header 'X-CSRF-Token: csrf_abc123def456ghi789' | jq
Los logs deberían verse algo así:
2026-02-18 15:28:11.600 trace_id=b8e1447340832e9b466fde0a1f172b55 span_id=a4fa1234784f7c02 trace_flags=01 INFO http-nio-8080-exec-1 --- d.p.spring_java.config.log.LogFilter >>>> Method: GET; URI: /api/films/42; QueryString: null; Headers: {Host: localhost:8080, Accept: application/json, Authorization: ****, Cookie: ****, Proxy-Authorization: ****, User-Agent: Mozilla/5.0 (Test Client), X-API-Key: ****, X-Auth-Token: ****, X-CSRF-Token: ****
2026-02-18 15:28:11.619 trace_id=b8e1447340832e9b466fde0a1f172b55 span_id=a4fa1234784f7c02 trace_flags=01 INFO http-nio-8080-exec-1 --- d.p.spring_java.config.log.LogAspect [FilmRestController.getFilm(..)] Args: [42]
2026-02-18 15:28:11.620 trace_id=b8e1447340832e9b466fde0a1f172b55 span_id=a4fa1234784f7c02 trace_flags=01 INFO http-nio-8080-exec-1 --- d.p.spring_java.config.log.LogAspect [FilmRestController.getFilm(..)] Response: FilmResponse(id=42, title=ACADEMY DINOSAUR, description=A Epic Drama of a Feminist And a Mad Scientist who must Battle a Teacher in The Canadian Rockies, releaseYear=2006, rating=PG, lengthMinutes=86, language=English)
2026-02-18 15:28:11.664 trace_id=b8e1447340832e9b466fde0a1f172b55 span_id=a4fa1234784f7c02 trace_flags=01 INFO http-nio-8080-exec-1 --- d.p.spring_java.config.log.LogFilter <<<< Response Status: 200