La Librería de Logging ECS es una biblioteca basada en Java diseñada para generar logs estructurados que cumplen con el formato del Elastic Common Schema (ECS). Soporta los paradigmas de programación imperativa y reactiva, para proyectos Java generados a partir de plantillas (scaffold). La biblioteca facilita la generación de logs con un esquema previamente definido, permitiendo un y análisis de los logs de request/response/error.
La librería está compuesta por cuatro módulos principales:
ecs-model: Define las estructuras de datos y el esquema necesario para que los logs cumplan con el estándar ECS. Este módulo es una dependencia para los demás módulos.
ecs-core: Contiene la lógica central para construir logs en formato ECS, utilizando ecs-model para las definiciones de esquema.
ecs-imperative: Proporciona funcionalidad de logging para proyectos que siguen un enfoque de programación imperativa, utilizando ecs-core.
ecs-reactive: Proporciona funcionalidad de logging para proyectos que siguen un enfoque de programación reactiva (ej. Project Reactor), también utilizando ecs-core.
Java: Versión 21 o superior.
Gradle: Para la gestión de dependencias.
Azure DevOps: Para pipelines de CI/CD.
La Librería de Logging ECS está diseñada para integrarse fácilmente en proyectos Java.
Agregar Dependencia: Incluye ecs-model en el main.gradle principal como se muestra posteriomente.
subprojects {
apply plugin: 'java'
apply plugin: 'jacoco'
apply plugin: 'io.spring.dependency-management'
apply plugin: 'info.solidsoft.pitest'
compileJava.dependsOn validateStructure
java {
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
}
//build.dependsOn 'pitest'
test {
useJUnitPlatform()
}
dependencies {
...
implementation 'co.com.bancolombia:ecs-model:<library-version>'
}
}
Configurar en el whitelistedDependencies la librería para evitar el error en la tarea de validate-structure de scaffold
validate-structure
En la clase de excepción ejemplo BusinessException o AppException debe extender de la clase BusinessExceptionECS del modelo de la librería
ejemplo:
package co.com.bancolombia.model.exception;
import co.com.bancolombia.ecs.model.management.BusinessExceptionECS;
public class BusinessException extends BusinessExceptionECS {
...
public BusinessException(ConstantBusinessException value) {
super(value);
}
...
}
En la clase de las constantes de las excepciones ejemplo ConstantBusinessException debe implementar la interfaz ErrorManagement del modelo de la librería
ejemplo:
package co.com.bancolombia.model.exception;
import co.com.bancolombia.ecs.model.management.ErrorManagement;
import static java.net.HttpURLConnection.HTTP_INTERNAL_ERROR;
public enum ConstantBusinessException implements ErrorManagement {
...
DEFAULT_EXCEPTION(HTTP_INTERNAL_ERROR,
CodeMessage.INITIAL_CODE_DETAIL,
BusinessCode.BASIC_INITIAL_CODE,
InternalMessage.TECHNICAL_ERROR,
CodeLog.LOG500_00),
...
}
Agregar Dependencia: Incluye ecs-imperative en el build.gradle del modulo de aplicación donde se encuentra el Main application como se muestra posteriomente.
dependencies {
...
implementation 'co.com.bancolombia:ecs-imperative:<library-version>'
}
Importar la configuracion de el ImperativeLogsConfiguration en la clase de main principal:
package co.com.bancolombia;
import co.com.bancolombia.ecs.application.ImperativeLogsConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
import org.springframework.context.annotation.Import;
@SpringBootApplication
@ConfigurationPropertiesScan
@Import(ImperativeLogsConfiguration.class)
public class MainApplication {
public static void main(String[] args) {
SpringApplication.run(MainApplication.class, args);
}
}
Agregar Dependencia: Incluye ecs-reactive en el build.gradle del modulo de aplicación donde se encuentra el Main application como se muestra posteriomente.
dependencies {
...
implementation 'co.com.bancolombia:ecs-reactive:<library-version>'
}
Importar la configuracion de el ReactiveLogsConfiguration en la clase de main principal:
package co.com.bancolombia;
import co.com.bancolombia.ecs.application.ReactiveLogsConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
import org.springframework.context.annotation.Import;
@SpringBootApplication
@ConfigurationPropertiesScan
@Import(ReactiveLogsConfiguration.class)
public class MainApplication {
public static void main(String[] args) {
SpringApplication.run(MainApplication.class, args);
}
}
Salida: Los logs se generan de manera transversal a todas las peticiones rest y errores del aplicativo en formato JSON ECS.
Variables por default
spring:
application:
name: "ms_ecs"
ecs:
logs:
request:
replacement: ""
patterns: ""
delimiter: ""
fields: ""
allow-headers: ""
excluded-paths: "/actuator"
show: false
response:
replacement: ""
delimiter: ""
fields: ""
patterns: ""
show: false
print-on-error:
print-req-resp:
print-req-resp-level:
message-id:
enable_auto_register_message_id:
| Variable de Entorno | Descripción | Valor por Defecto |
|---|---|---|
spring.application.name |
Nombre del servicio | "ms_ecs" |
spring.ecs.logs.request.replacement |
Reemplazos para campos en los logs de solicitudes | "" |
spring.ecs.logs.request.patterns |
Patrones a filtrar el json de las solicitudes | "" |
spring.ecs.logs.request.delimiter |
Delimitador utilizado para separar campos en las variables de solicitudes | "" |
spring.ecs.logs.request.fields |
Campos a aplicar la sanitización en los logs de solicitudes | "" |
spring.ecs.logs.request.allow-headers |
Encabezados HTTP permitidos para incluir en los logs de solicitudes | "" |
spring.ecs.logs.request.excluded-paths |
Rutas excluidas del registro de solicitudes | "/actuator" |
spring.ecs.logs.request.show |
Indica si se deben mostrar los logs de las solicitudes | false |
spring.ecs.logs.response.replacement |
Reemplazos para campos en los logs de respuestas | "" |
spring.ecs.logs.response.delimiter |
Delimitador utilizado para separar campos de las variables de respuestas | "" |
spring.ecs.logs.response.fields |
Campos a aplicar la sanitización en los logs de respuestas | "" |
spring.ecs.logs.response.patterns |
Patrones a buscar en los logs de respuestas | "" |
spring.ecs.logs.response.show |
Indica si se deben mostrar los logs de las respuestas | false |
spring.ecs.logs.print-on-error.print-req-resp |
Activa la impresión condicional de request/response solo en errores | null |
spring.ecs.logs.print-on-error.print-req-resp-level |
Nivel único de excepción que dispara la impresión (BusinessExceptionECS, Exception, Throwable) |
null |
adapter.ecs.logs.message-id.enable_auto_register_message_id |
Controla la autogeneración del message-id UUID por request. Solo acepta "true" o "false" |
(no declarada) |
Esta funcionalidad permite que la librería genere y propague automáticamente un identificador único (message-id UUID) por cada request HTTP entrante, con comportamiento configurable vía YAML sin necesidad de cambios en el código del microservicio consumidor.
adapter:
ecs:
logs:
message-id:
enable_auto_register_message_id: "true" # "true" | "false" | (omitir)| Variable | Tipo | Descripción | Valor por Defecto |
|---|---|---|---|
spring.ecs.logs.message-id.enable_auto_register_message_id |
String | Controla la autogeneración del message-id. Solo acepta "true" o "false" (valores distintos fallan al arrancar) |
(no declarada) |
Por qué el tipo es
Stringy noBoolean? Spring Boot convierte silenciosamente"yes","on","1"→truecon tipoBoolean. Con tipoString+ validador explícito, cualquier valor no permitido lanza un error al iniciar la aplicación.
| Valor YAML | Header enviado por el cliente | Header ausente en request | Excepción sin message-id previo |
|---|---|---|---|
| (no declarada) | Se propaga tal como viene | No se genera UUID (null) |
Se genera UUID siempre |
| (declarada sin valor / blanco) | Se propaga tal como viene | No se genera UUID (null) |
Se genera UUID siempre |
"false" |
Se propaga tal como viene | No se genera UUID (null) |
Se genera UUID siempre |
"true" |
Se propaga tal como viene | Se genera UUID automáticamente | Se genera UUID siempre |
El header del cliente siempre se propaga, independientemente del valor configurado. La diferencia entre modos solo aplica cuando el cliente no envía el header.
Clave declarada sin valor: si el microservicio consumidor escribe
enable_auto_register_message_id:en su YAML sin asignarle un valor, Spring Boot la enlaza como cadena vacía (""), no comonull. La librería trata ese caso igual que la propiedad no declarada: no falla al arrancar y el comportamiento por defecto es inactivo.
Las excepciones siempre tienen trazabilidad:
resolveForExceptiongenera UUID incondicionalmente cuando no hay unmessage-idprevio, sin importar el valor deenable_auto_register_message_id.
El header se reconoce en cualquiera de sus variantes: message-id, Message-Id, messageId, message_id. Internamente se normaliza a la clave canónica message-id.
Esta funcionalidad permite que el cuerpo de request y response solo se imprima cuando ocurre un error que coincida con los tipos configurados. Es útil para reducir el volumen de logs en peticiones exitosas sin perder visibilidad en los errores.
adapter:
ecs:
logs:
print-on-error:
print-req-resp: true
print-req-resp-level: "Throwable"| Variable | Tipo | Descripción | Valor por Defecto |
|---|---|---|---|
adapter.ecs.logs.print-on-error.print-req-resp |
Boolean | Activa la impresión condicional de request/response solo en errores | null (desactivado) |
adapter.ecs.logs.print-on-error.print-req-resp-level |
String | Nivel único de excepción que dispara la impresión. Valores permitidos: BusinessExceptionECS, Exception, Throwable |
null |
print-on-error.print-req-resp |
print-on-error.print-req-resp-level |
Resultado |
|---|---|---|
null / false |
— | Comportamiento estándar (usa request.show, response.show y sampling normalmente) |
true |
"Throwable" |
Imprime request/response para cualquier Throwable |
true |
"Exception" |
Imprime request/response para cualquier Exception (incluye BusinessExceptionECS) |
true |
"BusinessExceptionECS" |
Imprime request/response solo para BusinessExceptionECS y subclases |
true |
null |
"" |
Cuando print-on-error.print-req-resp: true:
- Desactiva
request.showyresponse.show: La impresión de body pasa a ser controlada exclusivamente por esta funcionalidad. - Suspende el sampling: Las reglas de muestreo (
rules20XJson,rules40XJson) no aplican. Los errores que coincidan con el nivel configurado siempre se imprimen. - Excluded paths: Siguen funcionando normalmente (se aplican antes de la lógica de
print-on-error). - Validación temprana: Si
print-req-resp-leveltiene un valor diferente aBusinessExceptionECS,ExceptionoThrowable, la aplicación falla en la inicialización de configuración con un mensaje explícito.
La coincidencia se realiza con un nivel único jerárquico definido en print-on-error.print-req-resp-level. Ejemplos:
"BusinessExceptionECS"→ coincide conBusinessExceptionECSy todas sus subclases (ej.BusinessExceptiondel MS consumidor)"Exception"→ coincide con todas las excepciones"Throwable"→ coincide con todas las implementaciones de la interfaz Throwable (ej. excepciones, errores)nullo""→ no coincide con ningún error, no se imprime nada
Para imprimir request/response solo cuando ocurra un BusinessExceptionECS:
adapter:
ecs:
logs:
print-on-error:
print-req-resp: true
print-req-resp-level: "BusinessExceptionECS"Definición de la estructura de los logs generados por la Librería de Logging ECS. A continuación, se describe la estructura de los logs para los niveles INFO y ERROR, especificando los campos, sus tipos de datos y su obligatoriedad.
| Campo | Tipo de Dato | Descripción | Obligatorio |
|---|---|---|---|
| messageId | String (UUID) | Identificador único del mensaje en formato UUID. | Sí |
| date | String | Fecha y hora del evento en formato DD/MM/YYYY HH:MM:SS:SSSS. |
Sí |
| service | String | Nombre del servicio que genera el log (ej. ms_actor). |
Sí |
| consumer | String | Identificador del consumidor o cliente que realiza la solicitud. | Sí |
| additionalInfo | Object | Información adicional sobre la solicitud (ver detalle abajo). | Sí |
| level | String | Nivel del log (INFO o ERROR). |
Sí |
| error | Object/Null | Detalles del error (presente en logs ERROR, null en INFO). |
No |
| Campo | Tipo de Dato | Descripción | Obligatorio | Notas |
|---|---|---|---|---|
| method | String | Método HTTP utilizado (ej. POST, GET). |
Sí | |
| uri | String | URI de la solicitud (ej. /actors/retrieveUser). |
Sí | |
| headers | Object | Cabeceras HTTP de la solicitud. | Sí | Ver detalle de subcampos. |
| requestBody | Object | Cuerpo de la solicitud. | Sí | Varía según el tipo de log. |
| responseBody | Object/Null | Cuerpo de la respuesta. | No | Presente en logs INFO. |
| responseResult | String | Resultado de la respuesta (ej. OK). |
Sí | |
| responseCode | String | Código de estado HTTP (ej. 200, 400). |
Sí |
| Campo | Tipo de Dato | Descripción | Obligatorio | Notas |
|---|---|---|---|---|
| code | String | Código del consumidor (ej. CAP). |
No | Coincide con consumer en la raíz. |
| message-id | String (UUID) | Identificador del mensaje. | Sí | Coincide con messageId en la raíz. |
| aid-creator | String | Identificador del creador. | No | Ej. A8A77339260DA412B8238F21BBC1CF398 |
| Campo | Tipo de Dato | Descripción | Obligatorio |
|---|---|---|---|
| raw | String | Cuerpo de la solicitud como texto plano. | No |
| Campo | Tipo de Dato | Descripción | Obligatorio |
|---|---|---|---|
| meta | Object | Metadatos de la respuesta. | No |
| meta._messageId | String (UUID) | Identificador único del mensaje. | No |
| meta._requestDateTime | String | Fecha y hora de la solicitud. | No |
| data | Object | Datos de la respuesta. | No |
| Campo | Tipo de Dato | Descripción | Obligatorio |
|---|---|---|---|
| raw | String | Cuerpo de la solicitud como texto plano. | No |
| Campo | Tipo de Dato | Descripción | Obligatorio |
|---|---|---|---|
| meta | Object | Metadatos de la respuesta. | No |
| meta._messageId | String (UUID) | Identificador único del mensaje. | No |
| meta._requestDateTime | String | Fecha y hora de la solicitud. | No |
| data | Object | Datos de la respuesta. | No |
| Campo | Tipo de Dato | Descripción | Obligatorio |
|---|---|---|---|
| type | String | Código del error (ej. SAER400-01-11). |
Sí |
| message | String | Mensaje descriptivo del error. | Sí |
| description | String | Descripción detallada del error. | Sí |
| optionalInfo | Object | Información adicional del error. | No |
| optionalInfo.OPTIONAL | String | Detalle adicional. Ej. falta de cabeceras. | No |


