Acerca de
Curriculum Vitae
Blog
Artículos


Tutorial de Logging con Apache : Apache Commons Logging, Log4j y Log4j JDBCAppender

Introducción

Este artículo es un tutorial de como hacer Logeo ( bitacora) desde un programa en Java con las herramientas de Apache.
Como todos los tutoriales de este sitio el enfoque es práctico, instala lo necesario en tu equipo para correr el ejemplo, descárgalo, ejecútalo y después regresa a entender que es lo que hace el ejemplo.
No olvides dejar comentarios, sugerencias y dudas en la sección de comentarios.

Requisitos: Los únicos prerequisitos previos para entender y ejecutar el ejemplo son:
Descarga y Ejecución del ejemplo

Para correr el ejemplo, descarga el siguiente archivo y descomprímelo en tu disco duro, desde una ventana de ms-dos (o un shell de Unix/Linux) , cambia de directorio hacia la ruta donde hayas descomprimido el ejemplo, y ejecuta el siguiente comando (es necesario haber configurado previamente Ant ):


D:\cd LogginTutorial

D:\LogginTutorial>ant testLoging



Si configuraste correctamente las variables de entorno debes de ver la siguiente salida:


D:\LogginTutorial>ant testLoging
Buildfile: build.xml

build:

starthsql:

createtable:
[sql] Executing commands
[sql] 0 rows affected
[sql] 1 of 1 SQL statements executed successfully

runSimple:
[java] [INFO] BasicLogging - Test de logging
[java] [INFO] BasicLogging - Utilizando la implementacion:org.apache.commons.logging.impl.SimpleLog

runLog4J:
[java] 2008-06-05 15:39:14,453 INFO [com.marioalberto.tutorial.logging.Log4JLogging] - <Test de logging>
[java] 2008-06-05 15:39:14,891 INFO [com.marioalberto.tutorial.logging.Log4JLogging] - <<Utilizando la implementacion:org.apache.commons.logging.impl.Log4JLogger>
[java] 2008-06-05 15:39:14,969 DEBUG [com.marioalberto.tutorial.logging.Log4JLogging] - <Test de mensaje de DEBUG>

shutdowndb:
[sql] Executing commands
[sql] MESSAGE,TIMESTAMP
[sql] Test de logging,2008-06-05 15:39:14.782
[sql] Utilizando la implementacion:org.apache.commons.logging.impl.Log4JLogger,2008-06-05 15:39:14.891
[sql] Test de mensaje de DEBUG,2008-06-05 15:39:14.969
[sql]
[sql] 0 rows affected
[sql] 0 rows affected

BUILD FAILED
D:\mario\desarrollo\www.marioalberto.com.mx\tutorialesCode\logging\build.xml:52: java.sql.SQLException: Connection is broken

Total time: 4 seconds
D:\LogginTutorial>

Si es asi ...felicidades! acabas de Utilizar log4J y Apache Commons Logging para generar logs a Consola, a un archivo y a BD.
Recuerda que es necesario haber instalado y configurado previamente Ant

¿ Que es "Logging"?

De acuerdo a la Wikipedia , es la practica de llevar un registro de datos, generalmente se lleva a cabo de forma secuencial.
En el contexto de un programa de Java, frecuentemente es necesario mostrar en pantalla (o almacenar en un archivo) información relevante a medida que el programa se va ejecutando.

Para programas sencillos podemos utilizar un simple System.out.println pero si queremos hacerlo de forma estándar y no solo enviar la salida a Consola sino también a archivos y/o bases de datos es mejor utilizar alguno de los Frameworks de Logeo disponibles

El proceso de logeo se puede dividir en tres componentes principales, el Logger, el Formateador y el Appender.

El Logger se encarga principalmente de capturar el mensaje con información extra como el nivel del Logeo (si es un mensaje de depuración, de warning, informativo, de error, etc) y le pasa esa información al Framework de Logeo.

Despues de recibir el mensaje, el framework llama al Formatter, el cual se encargara de dar el formato correspondiente al mensaje, posteriormente se manda el mensaje formateado al Appender quien se encarga de mandar el mensaje a la salida correspondiente
¿Que hace el ejemplo?

El ejemplo ejecuta las siguientes tareas desde Ant:

build: Compila los siguientes archivos con javac
com/marioalberto/tutorial/logging/BasicLogging.java
com/marioalberto/tutorial/logging/Log4JLogging.java

starthsql: Como el ejemplo utiliza HSQL ( el cual , de acuerdo al sitio es un: "Lightweight 100% Java SQL Database Engine" ) para los ejemplos en los que se utiliza Base de Datos, con esta tarea iniciamos la BD, básicamente se ejecuta desde ant la siguiente clase: org.hsqldb.Server , con los siguientes parámetros :


-database.0 mem:logtest -dbname.0 basiclog

indicando que la BD se llama basiclog y debe ejecutarse en memoria, para mas detalles de como se configura y ejecuta HSQL consultar.

createtable: Crea una tabla llamada loggin en la BD basiclog

runSimple: Ejecuta com.marioalberto.tutorial.logging.BasicLogging, el cual es un ejemplo sencillo de Loggeo con Apache Commons Logging

runLog4J: Ejecuta com.marioalberto.tutorial.logging.Log4JLogging, el cual es un ejemplo sencillo de Loggeo con Log4J con salida a un archivo de log y a la Base de Datos de HSQL

shutdowndb: Hace un select en la tabla loggin para ver los contenidos y da de baja la BD ,todos los datos almacenados se pierden, ya que esta corriendo en memoria.

Estas instrucciones instrucciones se puede consultar mas a detalle en el archivo de ant build.xml

Logeo con Apache Commons Logging

Como podemos ver en el código del ejemplo BasicLogging.java en lugar de utilizar la instruccion System.out.println utilizamos LOGGER.info("Test de logging");

BasicLogging.java
package com.marioalberto.tutorial.logging;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class BasicLogging  {

  private static final Log LOGGER = LogFactory.getLog(BasicLogging.class);

  public void logTest() {
    LOGGER.info("Test de logging");
    LOGGER.info("Utilizando la implementacion:" +LOGGER.getClass().getName());
  }
  public static void main(String args[]) {
    BasicLogging test = new BasicLogging();
    test.logTest();
  }

}

en donde LOGGER es una variable del tipo org.apache.commons.logging.Log (la cual es una Interfase)

Apache Commons Logging (JCL) nos proporciona una Interfase cuyo propósito es ser una "abstracción" independiente de otros frameworks o toolkits de Logeo.
El propósito es usar esta interfase en el código y después conectar una implementación en especifico (Log4J,Avalon LogKit , JDK 1.4 Logging API) a través de configuración sin necesidad de modificar el código.

Configuración

JCL utiliza principalmente dos abstracciones Log (el logger basico ) y LogFactory (que sabe como crear instancias de Log en base a la configuración proporcionada). Es por esto que cuando queremos utilizar un logger no lo instanciamos directamente con el metodo new, lo que hacemos es solicitarle una instancia al Factory de esta forma:

 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 public class BasicLogging  {
    private static final Log LOGGER = LogFactory.getLog(BasicLogging.class);


la cual puede ser utilizada de la siguiente forma en cualquier parte del codigo:

LOGGER.info("Test de logging");


Dependiendo de la configuración que se detecte el LogFactory regresara una implementación en particular de la interface org.apache.commons.logging.Log, en específico, se tienen las siguientes posibles implementaciones Para saber que tipo de Logger instanciar, el LogFactory utiliza el siguiente proceso de descubrimiento (es decir, el proceso termina cuando alguna de las opciones - en el orden en que se muestran - es encontrada):

1.- Buscar un atributo de configuración del LogFactory llamado org.apache.commons.logging.Log, en el cual se le indica que implementación de Log en especifico se debe utilizar, este atributo se puede especificar mediante código (para usuarios avanzados), pero lo mas común es colocar un archivo llamado commons-logging.properties en el classpath. Cuando se detecta la existencia de ese archivo, cada línea en el archivo se convierte en un atributo de configuración del LogFactory

2.- Buscar una propiedad de sistema llamada org.apache.commons.logging.Log , la cual se le especifica a java desde linea de comando con la opción :

-Dorg.apache.commons.logging.Log=com.paquete.AlgunaImplementacion

en nuestro ejemplo esta opción se especifica desde Ant en el task: runSimple y runLog4j

3.- Si el framework Log4J se encuentra en el classpath (es decir, si en el classpath se detecta el jar que contiene el framework de Log4J) utilizar la clase correspondiente (Log4JLogger).

4.- Si la aplicación se esta ejecutando con JDK 1.4 , usar la clase correspondiente del API (Jdk14Logger)

5. Utilizar la clase por default proporcionada por JCL (SimpleLog).

Una vez que se ha configurado el esquema de Loggeo en base a lo anterior y se ha obtenido una instancia a través del Factory, se puede utilizar el Logger (en sustitución del System.out.println) como se muestra a continuación

public void logTest() {
 LOGGER.info("Test de logging");
 LOGGER.info(LOGGER.getClass().getName());

}


En nuestro ejemplo BasicLogging queremos utilizar la clase por default commons.logging.impl.SimpleLog y enviamos la propiedad del sistema:

-Dorg.apache.commons.logging.Log=org.apache.commons.logging.impl.SimpleLog, desde la linea de comandos (con Ant).

Y obtenemos la salida:

[INFO] BasicLogging - Test de logging
[INFO] BasicLogging - org.apache.commons.logging.impl.SimpleLog


Con la cual podemos comprobar que efectivamente la clase que se esta utilizando es SimpleLog, notese que si esta configurado Ant correctamente podemos ejecutar este ejemplo indivualmente tecleando el comando:


D:\LogginTutorial>ant runSimple

el cual se encarga de enviar el parametro desde Ant

Logeo con Apache log4j

Como se menciono en la sección anterior si queremos utilizar Log4j no es necesario modificar el código lo único que tenemos que hacer es configurar explícitamente la propiedad de sistema org.apache.commons.logging.Log especificandole el valor org.apache.commons.logging.impl.Log4JLogger.

De la misma forma si no especificamos ninguna propiedad, debido a que el archivo jar de Log4J se encuentra en el Classpath se utilizara esa implementación del Logger (como se expuso en el paso 3 del proceso de descubrimiento)

De esta forma el LogFactory sabra que debe utilizar la clase Log4JLogger para crear las instancias del Logger

Aunque no es necesario modificar el código del ejemplo original, para probar el Loggeo con Log4j utilizaremos la siguiente clase para poder ejecutarlos indivualmente:


Log4JLogging.java
package com.marioalberto.tutorial.logging;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class Log4JLogging  {

private static final Log LOGGER = LogFactory.getLog(Log4JLogging.class);
public void logTest() {

LOGGER.info("Test de logging");
LOGGER.info("Utilizando la implementacion:" + LOGGER.getClass().getName());
LOGGER.debug("Test de mensaje usando Nivel de logeo DEBUG");
}

public static void main(String args[]) {
  Log4JLogging test = new Log4JLogging();
  test.logTest();
}

}



Nuevamente, si esta configurado Ant correctamente podemos ejecutar este ejemplo indivualmente tecleando el comando:


D:\LogginTutorial>ant runL4JSample

el cual se encarga de ejecutar unicamente el ejemplo para Log4J desde Ant

Configuración de log4j

Log4J también esta conformado por los tres componentes principales que se mencionaron anteriormente loggers, appenders y layouts (formatter).

Para configurar correctamente Log4j es necesario proporcionar el archivo log4j.properties en el classpath, en cual se configuraran estos tres componentes.

LOGGERS

En un mismo archivo log4j.properties se pueden configurar distintos Loggers lo cual nos permitiría activar o desactivar los mensajes en base al paquete (package) o clase en la que se utilice el logger.
De esta forma si especificamos en el archivo de configuración las siguientes opciones:

log4j.rootLogger=INFO,archivo
log4j.logger.com.marioalberto.tutorial.logging.Log4JLogging=DEBUG, consola

Estamos indicando que tendremos un Logger "genérico" (el rootLogger) el cual funcionara para todas las clases y guardara los mensajes con el nivel INFO, mandando la salida al appender archivo. Adicionalmente tendremos un appender para la clase com.marioalberto.tutorial.logging.Log4JLogging (nuestro programa de ejemplo) la cual mandara todos los mensajes con Nivel de DEBUG al appender consola"

APPENDERS

Tambien es necesario configurar cada uno de los Appenders , en este caso "archivo" y "consola", primero se especifica que tipo de appender se utiliza indicando el nombre de la clase a utilizar, en nuestro ejemplo:

log4j.appender.consola=org.apache.log4j.ConsoleAppender
log4j.appender.archivo=org.apache.log4j.RollingFileAppender


Con lo cual indicamos que el appender "consola" es del tipo org.apache.log4j.ConsoleAppender, el cual se encarga de mandar la salida a System.out , y el appender "archivo" es del tipo org.apache.log4j.RollingFileAppender, el cual manda la salida a un archivo y despues de que el archivo alcanza un tamaño determinado, se crea un respaldo de ese archivo y se genera uno nuevo.

Cada appender puede tener opciones de configuración especificas, por ejemplo el RollingFileAppender necesita saber entre otras cosas la ruta y el nombre del archivo donde almacenara los logs, así como el tamaño máximo del archivo antes de hacer un backup.
Lo cual se especifica con las siguientes opciones:

log4j.appender.archivo.File=logs/agregadores.log
log4j.appender.archivo.MaxFileSize=512KB


Cabe mencionar que se tienen varios appenders adicionales, los cuales no se verán en este tutorial. A continuación algunos de ellos: LAYOUTS

Por ultimo, debemos especificar que formato o "layout" deberán llevar cada una de las líneas que se generan con el Logger, para ambos Logers utilizamos el PatternLayout lo cual se indica con:

log4j.appender.consola.layout=org.apache.log4j.PatternLayout
log4j.appender.consola.layout.ConversionPattern=%d %p [%c] - <%m>%n


log4j.appender.archivo.layout=org.apache.log4j.PatternLayout
log4j.appender.archivo.layout.ConversionPattern=%d %p [%c] - <%m>%n


El PatternLayout nos permite especificar un patron determinado en base a las reglas especificadas aqui:

Para nuestro caso indicamos que:

ConversionPattern=%d %p [%c] - <%m>%n

en el cual:

%d = fecha
%p = Nivel (INFO,WARN,DEBUG)
%c = la categoria o clase del evento
%m - El Mensaje
%n - Separador de Linea

Utilizando JDBCAppender desde Log4J para mandar la salida a una BD

Si en lugar de mandar los Logs a Consola o a un Archivo ,quisieramos mandar la salida de los Logs a una BD podemos utilizar el JDBCAppender para lo cual unicamente tenemos que configurar un appender adicional:

log4j.appender.JDBC=org.apache.log4j.jdbcplus.JDBCAppender
log4j.appender.JDBC.url= jdbc:hsqldb:hsql://localhost/basiclog
log4j.appender.JDBC.dbclass=org.hsqldb.jdbcDriver
log4j.appender.JDBC.username=sa
log4j.appender.JDBC.password=
log4j.appender.JDBC.sql=INSERT INTO loggin(message,timestamp) VALUES ('@MSG@', '@TIMESTAMP@')
log4j.appender.JDBC.layout=org.apache.log4j.PatternLayout
log4j.appender.JDBC.layout.ConversionPattern=%d %p [%c] - <%m>%n
log4j.appender.JDBC.layoutPartsDelimiter=#


Para configurar este appender es necesario proporcionar el URL de la Base de Datos , el Driver de Java, un username y una contraseña.

En este ejemplo utilizamos una base de datos de HSQL con una sola tabla con dos campos, la cual puede ser creada con el siguiente comando:

CREATE TABLE loggin (
  idTable  varchar(3) default NULL,
  idGenero varchar(3) default NULL
);
En este caso la tabla fue creada con el task "createtable" desde Ant

Adicionalmente tenemos que indicar una sentencia de SQL con la cual se hara el INSERT en la tabla correspondiente:

log4j.appender.JDBC.sql=INSERT INTO loggin(message,timestamp) VALUES ('@MSG@', '@TIMESTAMP@')

El Listado completo de log4j.properties con la configuración para este tutorial es el siguiente:
log4j.rootLogger=INFO, consola
log4j.logger.com.marioalberto.tutorial.logging.Log4JLogging=DEBUG, archivo,JDBC

log4j.appender.consola=org.apache.log4j.ConsoleAppender
log4j.appender.consola.layout=org.apache.log4j.PatternLayout
log4j.appender.consola.layout.ConversionPattern=%d %p [%c] - <%m>%n

log4j.appender.archivo=org.apache.log4j.RollingFileAppender
log4j.appender.archivo.File=logs/agregadores.log
log4j.appender.archivo.MaxFileSize=512KB
log4j.appender.archivo.MaxBackupIndex=3
log4j.appender.archivo.layout=org.apache.log4j.PatternLayout
log4j.appender.archivo.layout.ConversionPattern=%d %p [%c] - %m%n

log4j.appender.JDBC=org.apache.log4j.jdbcplus.JDBCAppender
log4j.appender.JDBC.url= jdbc:hsqldb:hsql://localhost/basiclog
log4j.appender.JDBC.dbclass=org.hsqldb.jdbcDriver
log4j.appender.JDBC.username=sa
log4j.appender.JDBC.password=
log4j.appender.JDBC.sql=INSERT INTO loggin(message,timestamp) VALUES ('@MSG@', '@TIMESTAMP@')
log4j.appender.JDBC.layout=org.apache.log4j.PatternLayout
log4j.appender.JDBC.layout.ConversionPattern=%d %p [%c] - <%m>%n log4j.appender.JDBC.layoutPartsDelimiter=#


Resumen


Los pasos a seguir y las consideraciones a tener en cuenta para hacer Loggin son:
  1. Tener los archivos jar de los siguientes frameworks:

  2. Declarar e inicializar el logger dentro del programa a utilizar de la siguiente forma:

    private static final Log LOGGER = LogFactory.getLog(BasicLogging.class);


  3. Configurar que implementación del Logger se desea utilizar mediante una de las opciones (en ese orden):
  4. Para el caso de Log4j, poner el archivo log4j.properties en alguna de las rutas del classpath, y configurar las siguientes opciones:
Espero que este articulo te sea de utilidad, recuerda que puedes poner tus dudas en la sección de comentarios donde seran respondidas a la brevedad.



4 Comentario(s)
killko-2009-08-31 18:06:13
Me parece un manual exelente, lleno de informacion valioza, gracias por compartirla

Mario-2009-09-01 15:20:55
Que bueno que te sirvio.

Saludos

Magui-2010-03-04 14:01:57
Muchas gracias, a mi también me sirvio de mucho... saludos y mas de estos manuales :D

g0h4n-2010-05-28 06:11:35
Mil gracias, me ha servido mucho porque estaba teniendo problemas con la implementación que estaba utilizando el LogFactory y sin lo que he encontrado aquí hubiera tardado una eternidad. saludos


Agregar Comentarios:
Nombre/Alias:
Email:
Url:
Comentarios:
Escribe las palabras (es para evitar spam):
© Copyright Mario Alberto Ramirez, todos los derechos reservados