Logs en Java con Java.util.logging

Cuando se está preparando un programa para un entorno de producción tener un log donde se reporten los eventos y errores puede ser la diferencia entre pasar una semana tratando de replicar un error o solo leer un archivo y saber en que linea ocurrió el error y si bien hay todo un mundo de librerias y frameworks para este propósito no hay que olvidar que el propio Java ya contiene las clases para hacer esto y que nunca esta de mas ahorrarse dependencias.

Java Logging Api

La forma en que opera el framework de logging de Java es la siguiente:

  1. Creamos un objeto estático de tipo Logger desde el cual enviaremos los mensajes a registrar en la bitácora
  2. Creamos un objeto ConsoleHandler y se lo agregamos al Logger, de modo que los mensajes aparezcan automáticamente en la consola
  3. Creamos un objeto FileHandler y se lo agregamos al Logger, este Handler en particular enviara los mensajes al archivo que le indiquemos.
  4. Creamos un objeto SimpleFormatter y lo establecemos en el FileHandler, de este modo los logs se escriban como texto plano simple, de no indicarlo se escribiran en formato XML por defecto
  5. Para registrar algo en bitácora llamamos al método log del Logger indicamos el nivel del log y el mensaje que deseamos registrar, esto automáticamente reportara la fecha, hora, el nombre completo de la clase y el método y en el caso de mensajes de nivel grave la linea de código donde se genero el reporte

Como muchas cosas del Java esto suena muy feo a sobre ingeniera, pero hay que reconocer que en cada paso del proceso puede modificar y extender el comportamiento del sistema para que se ajuste a sus necesidades.

Niveles de log

Como se mencionó en el paso 5 al momento de registrar un evento en bitácora necesitamos indicar el “nivel” de ese evento, estos nos permiten diferenciar entre meras notificaciones de que todo está bien a errores de diferente severidad esto no solo se refleja en el archivo sino que también podremos filtrar a partir de qué nivel de error queremos que se registre de modo que no se llene el archivo de meras notificaciones, este filtrado se indica en el objeto Handler.

Jerarquía de los Logger

Una de las grandes preguntas que tenía al investigar el uso del framework de logging era ¿Como envío los logs de varias clases al mismo archivo?, esto puede sonar trivial, simplemente cada clase necesita su propio FileHandler apuntando al mismo archivo ¿no?, pero esto, aparte de hacer que reusar una clase se haga difícil, solo le generará un montón de archivos .lck de los intentos de acceder al mismo archivo, la forma correcta es aprovechar la jerarquía propia de los paquetes de clases en java.

La idea de esto es la siguiente, creamos un Logger para el paquete principal de la aplicación y un Logger extra para cada subpaquete que exista escribiendo el nombre completo del paquete (esto es la ruta completa del mismo, javax.swing por ejemplo) en los métodos Logger.getLogger con los que se crea cada Logger, hecho esto al Logger del paquete principal le agregamos el Handler y Formatter que deseemos.

Lo siguiente es crear el objeto Logger adecuado en cada clase y registrar los eventos que deseemos, al correr el programa veremos que los logs de todo el programa están en el archivo del FileHandler del Logger del paquete principal.

Esto se debe a que los logs se propagan hacia arriba por toda la jerarquía de paquetes del programa hasta hallar un Handler que los maneje, como el único handler en todo el programa es el FileHandler del paquete principal (el cual está en la cima de la jerarquía), pues todos los logs acabarán en ese archivo.

La ventaja de organizar los logs de este modo es que si deseamos cambiar el archivo o formato solo hay que modificar una parte del programa en lugar de cada clase del programa y si deseamos utilizar una clase solo falta ajustar levemente la ruta en la declaración del Logger.

Imprimiendo el StackTrace en el Log.

Algo que puede serle muy útil para lidiar con los errores catastróficos capaces de tumbar el programa completo son los stackTrace, estos son están contenidos en los objetos Exception y son básicamente descripciones muy detalladas del estado del programa al momento en que ocurriera el error

Escribir los stackTrace en un log tiene truco, esto debido que los objetos Exception no tienen un método .getStackTrace() que le regrese un String, sino un método .printStackTrace() el cual envía la información directo a la salida, pero existe un método para redireccionar esa salida, el cual se detallara en el ejemplo.

Ejemplo.

Todo esto puede sonar demasiado complicado por lo cual se presentara un ejemplo que demuestre todo lo dicho, un log general para un programa compuesto de muchas clases organizadas en varios paquetes como se ve en la figura 1.

Figura 1 — Proyecto

Para comenzar cree un proyecto nuevo en NetBeans y cree los paquetes bitacora.bitacora.subnivel y bitacora.subnivel.under, como se ve en la figura.

El código de cada archivo se presenta a continuación:

Creo que esta es la entrada mas larga hasta la fecha, si algo por que el ejemplo es más elaborado de lo usual, las clases Control, Utilidades e InternalSys son bastante simples, solo crean un Logger y generan un log en la unica funcion que tienen.

Por su parte la clase Bitacora contiene el código más relevante, como es usual en este blog las líneas más importantes aparecen en rojo y los comentarios en verde detallan que ocurre en dichas líneas.

Ya que ejecute el programa verá en la consola una salida similar a la mostrará en la Figura 2

Figura 2 — Logs en Consola

Y llendo a la pestaña de archivos podra ver un archivo “bitacora.log” el cual al abrirlo le mostrara los logs del sistema a detalle, como se ve en la Figura 3

Figura 3 — Archivo de Bitacora

Como un ultimo detalle comente la linea fileHandler.setFormatter(simpleFormatter); y ejecute el programa de nuevo, notara que el archivo bitacora.log a cambiado y el log ahora se encuentra en formato xml, lo cual puede ser útil ya que este formato puede ser procesado y analizado por un programa de computadora con facilidad.

Figura 4 — Log en formato XML

Si lo desea puede bajar el proyecto completo de este ejemplo desde https://github.com/HashRaygoza/Logging

Espero que esta entrada fuera de utilidad y más comprensible que otros tutoriales que he visto en línea, si hay preguntas con gusto las respondere en los comentarios y nos vemos en la próxima entrada.