Primeros pasos con logback

De ChuWiki
Saltar a: navegación, buscar

Logback es un sistema de log similar al conocido log4j, pero que pretende mejorarlo y convertirse en su sucesor. Logback ha sido desarrollado por la misma que gente que desarrolló log4j, tratando de corregir los problemas de este último.

Ventajas de logback sobre log4j

Las ventajas de logback sobre log4j son, como dicen en su propia página web, muchas, pero vamos a ver aquí algunas de ellas.

  • Es más rápido y eficiente, consume menos recursos.
  • Es más robusto. No se cae, por ejemplo, si el fichero donde se está escribiendo el log desaparece o deja de ser escribible temporalmente.
  • slf4j es una API común para usar en nuestros programas, de forma que detrás de ella pueda estar log4j, logback, java-logging u otros muchos sistemas de log existentes. Si nuestro programa usa slf4j, podemos cambiar mas adelante de log4j a logback o java-logging sin tocar una línea de nuestro código, basta con cambiar el jar de log por el que queramos. Logback usa slf4j por defecto, por lo que usando logback, podemos cambiar a cualquier otro sistema de log sin tocar nuestro código.
  • Los ficheros de configuración de logback pueden ser modificados en caliente, sin necesidad de rearrancar nuestra aplicación. Podemos cambiar sobre la marcha, por ejemplo, el nivel de log.
  • Cuando el log va a fichero, logback tiene un 'prudent mode' o modo prudente, que permite que varias aplicaciones con logback puedan escribir simultáneamente sobre el mismo fichero de log.
  • Se pueden poner condicionales en los ficheros de configuración de logback, de forma que dependiendo por ejemplo del contenido de una variable de entorno del sistema operativo, la configuración se interpreta de distinta manera.
  • Si se usa en una aplicación web, logback tiene un Servlet que permite visualizar sobre el navegador el log.
  • Tiene un SiftingAppender capaz de dirigir el log a distintos sitios en función del valor que fijemos en código a una variable. Por ejemplo, podemos tener un log separado para cada usuario, metiendo en la variable el nombre del usuario antes de sacar el log.

Un ejemplo básico

Para una aplicación java normal, debemos usar logback-classic-x.x.x.jar, que a su vez tira de logback-core-x.x.x.jar y slf4j-api-y.y.y.jar. Si usamos maven, nos bastará con la dependencia

<dependency>
   <groupId>ch.qos.logback</groupId>
   <artifactId>logback-classic</artifactId>
   <version>1.1.7</version>
   <scope>compile</scope>
</dependency>

Una vez montado nuestro proyecto, el siguiente código funciona sin problemas

package com.chuidiang.ejemplos.logback;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class LogBackBasicExample {

   private static Logger logger = LoggerFactory.getLogger(LogBackBasicExample.class);

   public static void main(String[] args) throws InterruptedException {
         logger.debug("Hello world.");
         logger.warn("Hello world.!!");
   }

}

logback, a diferencia de log4j, se configura el solito por defecto si no encuentra un fichero de configuración, por lo que basta ejecutar este programa, sin ningún tipo de configuración, para obtener la salida

16:59:22.057 [main] DEBUG com.chuidiang.ejemplos.logback.LogBackBasicExample - Hello world.
16:59:22.062 [main] WARN com.chuidiang.ejemplos.logback.LogBackBasicExample - Hello world.!!

Simplemente hemos obtenido un Logger con LoggerFactory.getLogger(LogBackBasicExample.class) y ya está listo para usar. Podemos llamar a sus métodos trace(), debug(), info(), warn() o error(), según el nivel que queramos para el mensaje de log.

Fíjate también en los import. Son de slf4j y no de logback. Eso hace que este código sea compatible con cualquier otro sistema de log soportado por slf4j, sin necesidad de recompilar.

Finalmente, al obtener el Logger, hemos pasado un LogBackBasicExample.class. Suele ser práctica habitual en cualquier sistema de log pedir el Logger pasando el nombre completo de la clase (con su paquete) o en su defecto la clase. En nuestro caso, el nombre del Logger sería com.chuidiang.ejemplos.logback.LogBackBasicExample. Este sistema de nombres facilita, más adelante, configurar los log en función del paquete al que pertenecen de forma fácil. Por ejemplo, si configuramos el Logger de nombre "com", afectará a nuestro Logger, ya que esa configuración afecta a todos los Logger cuyo nombre "cuelgue" de "com".

Configuración de logback

logback busca en el classpath los siguientes ficheros de configuración en este orden:

  • logback.groovy
  • logback-test.xml
  • logback.xml
  • META-INF\services\ch.qos.logback.classic.spi.Configurator
  • Pone una configuración por defecto si no encuentra nada de lo anterior.

Los tres primeros contendrían la configuración que queramos para logback, el primero en groovy, los dos siguiente en xml. El logback-test.xml está pensado para tenerlo en desarrollo. Si usamos maven, por ejemplo, en src/test/resources podemos poner logback-test.xml y se le haría caso en los text de JUnit, o incluso si tenemos el proyecto montado en un IDE y estamos desarrollando. Al generar el jar (con maven o con el ide), este logback-test.xml no se empaqueta por defecto en el jar, sino que en su lugar se empaquetaría el logback.xml que tendríamos en src/main/resources y que sería la configuración deseada para el entorno de producción.

Si ninguno de los anteriores se encuentra, en el fichero META-INF\services\ch.qos.logback.classic.spi.Configurator podemos poner dentro el nombre completo de una clase de configuración de logback, que implemente la interfaz ch.qos.logback.classic.spi.Configurator.

El siguiente es un ejemplo de fichero logback.xml

<configuration>

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
  </appender>

  <root level="debug">
    <appender-ref ref="STDOUT" />
  </root>
</configuration>

En el fichero se ponen "appenders" que son las salidas de log. En el ejemplo, un ConsoleAppender que es para que la salida vaya por pantalla.

En "encoder" es el encargado de forma el mensaje. Por defecto (salvo que indiquemos otro en el fichero), logback usa el PatternLayoutEncoder, que forma el mensaje a partir de un patrón que ponemos luego en "pattern". En ese "pattern", cosas como $d es la fecha/hora en el formato que se indica detrás, %thread el nombre del hilo java, %-5level en nivel de log con 5 letras (DEBUG, INFO, WARN, ...), etc, etc. Es decir, podemos componer el mensaje a nuestro gusto.

Luego configuramos los logger. El logger raíz de todos (y del que todos heredan su configuración) es "root", así que basta configurar "root" para tener todos configurados. En nuestro ejemplo anterior, nuestro logger se llamaba com.chuidiang.ejemplos.logback.LogBackBasicExample, que heredará la configuración de root. En el ejemplo, configuramos root para que su nivel de log sea "debug", es decir, mostrará los mensajes de debug y mensajes más imporantes. No mostrará los mensajes "trace". Y el appender le ponemos el que hemos definido antes, que hemos llamado "STDOUT, por medio de "appender-ref", es decir, todos los log saldrán por consola usando el patrón que hemos puesto.

Si quisieramos que determinados logger de nuestra aplicación tuvieran otra configuración, podemos hacer cosas como esta

<configuration>

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
  </appender>

  <appender name="FILE" class="ch.qos.logback.core.FileAppender">
    <file>myApp.log</file>
    <encoder>
      <pattern>%date %level [%thread] %logger{10} [%file:%line] %msg%n</pattern>
    </encoder>
  </appender>

  <root level="debug">
    <appender-ref ref="STDOUT" />
  </root>

  <logger name="com.chuidiang.ejemplos.logback" additivity="false" level="info">
      <appender-ref ref="FILE" />
  </logger>
</configuration>

Hemos creado un nuevo appender que saca el log a fichero. Es similar al de consola, pero lleva otra class, y lleva el tag file, para indicar el nombre del fichero donde queremos el log.

Hemos añadido un tag logger, en el que en name hemos puesto el paquete de nuestro logger de ejemplo. Esto afectará a todas las clases del paquete, pero podríamos poner el nombre completo de la clase para solo afectar a esa clase concreta. Y le decimos que su appender es "FILE", así que el log de las clase de este paquete saldrá por fichero. Hemos cambiado además el nivel a "info", por lo que de estas clases no saldrán log de debug ni de trace.

Como hay "herencia" del logger raíz root, nuestras clases también heredan el appender de consola, así que el log de nuestras clases saldrá tanto por fichero como por consola. Si queremos evitar esta herencia de appenders, podemos poner el atributo additivity a false, como hemos hecho en el ejemplo. De esta forma no se hereda el appender del root y el log sólo saldrá por fichero.

Enlaces