Personalizar el mensaje emitido de un objeto en Log4j

Escrito por el .
java planeta-codigo programacion
Comentarios

Java

Usar una librería como Log4j es probablemente indispensable en una aplicación para obtener información de su funcionamiento, mensajes informativos, advertencias, errores o excepciones que se están produciendo o se han producido. Los mensajes que se emiten contienen información útil para monitorización en tiempo real o su inspección pasado un tiempo.

En cada punto de la aplicación en la que se desea emitir una traza es necesario proporcionar el formato del mensaje y sus parámetros extraídos de las propiedades de los objetos con la intención obtener una traza con los valores de determinadas propiedades. Si es habitual emitir una traza de ciertas clases para evitar poner la misma traza en diferentes puntos de la aplicación Log4j posee una funcionalidad para transformar en objeto en un mensaje personalizado como se comenta en su documentación para mensajes.

El método toString() muy posiblemente no es la mejor solución para transformar un objeto a un String de modo que sea emitido por Log4j, quizá su valor no sea lo desedeado, se use este método para otro tipo de funcionalidad y no sirve en el caso de querer diferentes mensajes en diferentes lugares de la aplicación.

En Log4j implementando una clase de tipo Message se transforman esas instancias en el mensaje de información personalizado. En el caso de la implementación de Message en SimpleProductMessage genera una traza con solo su identificativo y su nombre para la clase Product. En el caso de ProductMessage genera una traza más completa con su identificativo, nombre y color.

El método heredado de getFormattedMessage() es el encargado de generar la traza en este caso utilizando como apoyo los métodos también heredados getFormat() que contiene el patrón del mensaje y getParameters() que devuelve como parámetros para el patrón del mensaje los valores de las propiedades.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package io.github.picodotdev.blogbitix.log4j;

import org.apache.logging.log4j.message.Message;

public class SimpleProductMessage implements Message {

    private Product product;

    public SimpleProductMessage(Product product) {
        this.product = product;
    }

    @Override
    public String getFormat() {
        return "Product(%d, %s)";
    }

    @Override
    public Object[] getParameters() {
        return new Object[] { product.getId(), product.getNombre() };
    }

    @Override
    public String getFormattedMessage() {
        return String.format(getFormat(), getParameters());
    }

    @Override
    public Throwable getThrowable() {
        return null;
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package io.github.picodotdev.blogbitix.log4j;

import org.apache.logging.log4j.message.Message;

public class ProductMessage implements Message {

    private Product product;

    public ProductMessage(Product product) {
        this.product = product;
    }

    @Override
    public String getFormat() {
        return "Product(%d, %s, %s)";
    }

    @Override
    public Object[] getParameters() {
        return new Object[] { product.getId(), product.getNombre(), product.getColor() };
    }

    @Override
    public String getFormattedMessage() {
        return String.format(getFormat(), getParameters());
    }

    @Override
    public Throwable getThrowable() {
        return null;
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package io.github.picodotdev.blogbitix.log4j;

public class Product {

    private Long id;
    private String nombre;
    private String color;

    public Product(Long id, String nombre, String color) {
        this.id = id;
        this.nombre = nombre;
        this.color = color;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getNombre() {
        return nombre;
    }

    public void setNombre(String nombre) {
        this.nombre = nombre;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public String toString() {
        return String.format("Producto(%d, %s)", id, nombre);
    }
}

En los siguientes casos se utilizan diferentes formas para emitir la traza. En el primer caso se utiliza la forma habitual de proporcionar el patrón y sus parámetros, el segundo caso utiliza el método toString() del objeto. En el tercer y cuarto caso se utilizan las clases que implementan la interfaz Message emitiendo un mensaje diferente cada una de ellas sin tener que proporcionar el patrón ni extraer las propiedades de la clase Producto ya que son estas implementaciones en las que se delega esto.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
package io.github.picodotdev.blogbitix.log4j;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Main {

    private static final Logger logger = LogManager.getLogger(Main.class);

    public static void main(String[] args) {
        Product product = new Product(1l, "Intel NUC", "Negro");

        logger.info("Product({}, {})", product.getId(), product.getName());
        logger.info(product);
        logger.info(new SimpleProductMessage(product));
        logger.info(new ProductMessage(product));
    }
}

En todos estos casos las trazas emitidas son las mismas salvo en el caso de ProductMessage que muestra una traza con más información.

1
2
3
4
2018-08-10 20:38:35,138  INFO                     io.github.picodotdev.blogbitix.log4j.Main Product(1, Intel NUC)
2018-08-10 20:38:35,142  INFO                     io.github.picodotdev.blogbitix.log4j.Main Product(1, Intel NUC)
2018-08-10 20:38:35,144  INFO                     io.github.picodotdev.blogbitix.log4j.Main Product(1, Intel NUC)
2018-08-10 20:38:35,145  INFO                     io.github.picodotdev.blogbitix.log4j.Main Product(1, Intel NUC, Negro)

En la API de Log4j hay multitud de clases Message ya implementadas, por ejemplo, MapMessage para objetos del tipo Map entre otros muchos.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
plugins {
    id 'java'
    id 'application'
}

mainClassName = 'io.github.picodotdev.blogbitix.log4j.Main'

dependencies {
    compile 'org.apache.logging.log4j:log4j-api:2.11.1'
    runtime 'org.apache.logging.log4j:log4j:2.11.1'
    runtime 'org.apache.logging.log4j:log4j-core:2.11.1'
    runtime 'com.fasterxml.jackson.core:jackson-databind:2.9.6'
    runtime 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.9.6'
}

repositories {
    jcenter()
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
configuration:
  status: warn

  appenders:
    console:
      name: STDOUT
      patternLayout:
        Pattern: "%d{DEFAULT} %X{uuid} %-5level %60.60logger %msg%n"

  loggers:
    root:
      level: info
      appenderRef:
        ref: STDOUT

El código fuente completo del ejemplo puedes descargarlo del repositorio de ejemplos de Blog Bitix alojado en GitHub y probarlo en tu equipo ejecutando el comando ./gradlew run.