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:
- Nociones Basicas de Java
- Tener instalado el JDK 1.4+
- Conocimientos básicos de Ant (Ver tutorial de Ant)
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
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
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
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
- AvalonLogger: Utiliza el esquema de Loggeo del proyecto Apache
Avalon (Ahora Excalibur)
- Jdk14Logger: Utiliza el esquema de Loggeo del API de JDK1.4
- Jdk13LumberjackLogger: Implementación para JDK 1.3
- Log4JLogger: Utilizar Log4J
- SimpleLog: Es una de las implementaciones mas sencillas, envia
todos los mensajes a System.err.
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
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
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:
- org.apache.log4j.net.JMSAppender Manda los mensajes a un tópico de JMS (Liga)
- org.apache.log4j.nt.NTEventLogAppender Enviar la slida al log de Eventos de Windows (el cual se puede consultar con el Event Viewer)
- org.apache.log4j.net.SMTPAppender Envía un email cada vez que se genera un mensaje con el Log.
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
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=#
Los pasos a seguir y las consideraciones a tener en cuenta para hacer Loggin son:
- Tener los archivos jar de los siguientes frameworks:
- Apache Commons Log
- Log4j (o cualquier otra implementación que se desee utilizar)
-
Declarar e inicializar el logger dentro del programa a utilizar de la siguiente forma:
private static final Log LOGGER = LogFactory.getLog(BasicLogging.class);
- Configurar que implementación del Logger se desea utilizar mediante una de las opciones (en ese orden):
- Por programacion especificando un atributo de LogFactory
- Con el archivo commons-logging.properties
- Con propiedades de sistema mandando la opcion -D al interprete de Java
- Poniendo el jar de Log4j en el classpath
- Para el caso de Log4j, poner el archivo log4j.properties en alguna de las rutas del classpath, y configurar las siguientes opciones:
- Los loggers que se van a utilizar
- Que nivel de mensajes registraran los logs (INFO,WARN,DEBUG,etc)y hacia que appenders enviaran la salida
- De que tipo es cada uno de los appenders y parámetros de configuración
específicos de cada appender (es decir, si la salida será a un archivo,
cual es el nombre del archivo, si la salida es a una base de datos proporcionar
informacion especifica de la base de datos)
- Indicar que Layout deberán de tener los mensajes tomando como referencia
la clase PatternLayout
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.
© Copyright Mario Alberto Ramirez, todos los derechos reservados