Páginas

jueves, 26 de abril de 2012

Librerías de logging para Java (slf4j, log4j, java.util.logging, logback, MentaLog)

Java
Prácticamente toda aplicación incluye un sistema para emitir trazas con información relevate de lo que está realizando a un fichero o a la salida estándar. Dependiendo de la información y detalle de la misma esta puede ser muy útil para determinar la causa de un posible fallo o algún mal funcionamiento de la aplicación ya que el log es un registro no solo de la excepción producida sino también de su contexto con las acciones anteriores realizadas. También puede servir para monitorizar en tiempo real las acciones que está llevando a cabo el sistema que de otra forma no podríamos realizar o sería utilizando herramientas más complejas.

slf4j
Generar trazas en una aplicación es muy barato y simple comparado con la información que puede proporcionar, su utilidad y tiempo que puede ahorrar a la hora de descubrir problemas. Básicamente, las trazas que queremos generar las insertamos dentro del propio código de la aplicación que están compuestas del mensaje que queremos emitir, el nivel de la traza y la categoría a la misma. El mensaje puede ser cualquier cadena que queramos que creamos que nos puede ser útil pudiendo contener datos procedentes de variables o cualquier otra cosa de la que pueda disponer el programa. Los niveles de las trazas en muchos sistemas son las siguientes por orden de relevancia de mayor a menor: fatal, error, warn, info, debug. Estos niveles de las trazas nos pemitirán utilizalos posteriormente para en tiempo de ejecución y mendiante la configuración del sistema de logging filtrar las mismas por el nivel que queramos pudiendo obtener únicamente las trazas de nivel warn o superior. Las categorías representan la pieza dentro de nuestro sistema que emite la traza que también podremos utilizar para filtrar los mensajes que queramos. Por supuesto, podremos utilizar las combinaciones de nivel y categoría que queramos teniendo en nuestras manos un sistema muy flexible para obtener la información que queramos y que genere la aplicación.

Veamos un pequeño programa para hacernos una idea de que es todo esto:

package es.com.blogspot.elblogdepicodev.slf4j;

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

public class HelloWorld {
  public static void main(String[] args) {
    // logger es un referncia a la categoria
    Logger logger = LoggerFactory.getLogger(getClass().getName());
    // debug es el nivel de la traza
    // Hello world! es el mensaje que se emite
    logger.debug("Hello world!");
  }
}

Como normalmente ocurre en el mundo del software libre y de código abierto y muchas veces en java hay más de una librería a nuestra disposición para cada funcionalidad que queramos incorporar en una aplicción. En el caso del sistema de trazas por supuesto esto no es menos. Dos de las más conocidas son log4j y la propia que incorpora el JDK de Java, java.util.logging. Aunque ambas librerías/api son prácticamente iguales salvo por llamar de forma diferente a los mismos conceptos normalmete la elección se decanta por log4j por ser más flexible y poseer algunas más opciones más (log4j vs java.util.logging). Salvo que no queramos o no podamos añadir una nueva dependencia a nuestro proyecto posiblemente elijamos log4j u otra como logback.

Logback es otra librería para logging denominandose a si misma la sucesora de log4j y siendo sus autores los mismos, con lo que han plasmado en esta nueva librería la experiencia aprendida en log4j. Hay varias razones por las que podríamos querer usar logback en vez de log4j, entre ellas es más rápido (según dicen ellos unas 10 veces más rápido), configuración usando Groovy, recarga automática de la configuración, compresión y eliminación automática de archivos históricos y trazas con información de empaquetado entre otras.

El ejemplo de código anterior hace uso de otra librería llamada slf4j que permite abstraernos de la libreria que decidamos utilizar ya sea lof4j, java.util.logging o logback. Usando slf4j si posteriormente queremos cambiar de una librería a otra será tan fácil como realizar un nuevo archivo de configuración y sustituir su jar por el de la otra librería, no tendremos que tocar ninguna linea de nuestro código. Por tanto podemos tener las siguientes combinaciones:
  • slf4j + log4j
  • slf4j + java.util.logging
  • slf4j + logback
Posibles bindings para slf4j
Como se puede ver en la imagen logback no necesita un adaptador que penaliza el rendimiento como en el caso de log4j o java.util.logging.

Por si estas opciones no fueran suficientes existen más, otra con interesantes características es MentaLog. Por citar algunas de sus propiedades:
  • Configuración programática (sin archivos de configuración)
  • Mejor rendimiento incluso por encima de logback
  • Varargs y placeholders en los métodos de logging
  • Sin reserva de memoria y por tanto sin sobrecarga para el garbage collector
  • Soporta colores para la consola
  • Los loggers son enumerados
Tenemos a nuestra disposición varias posibilidades y si tenemos la posibilidad de elegir quizás una opción recomendable sea la combinación slf4j + logback, ya que con ella siempre podremos pasar a utilizar cualquiera de log4j, java.util.logging o logback de forma sencilla y sin ningún cambio en el código. Y aunque MentaLog parece que no tiene todas las opciones de la anterior combinación si cumple con nuestras necesidades tambien sea otra opción a considerar por sus características.


Referencia:
Logging usando marcadores con slf4j y logback
http://logging.apache.org/log4j/1.2/
http://docs.oracle.com/javase/7/docs/api/java/util/logging/package-summary.html
http://logback.qos.ch/
http://logback.qos.ch/reasonsToSwitch.html
http://mentalog.soliveirajr.com/posts/list/26.page

viernes, 13 de abril de 2012

Ejemplo del patrón de diseño Command y programación concurrente en Java

Java
En la actualizad los procesadores están aumentando su capacidad de proceso principalmente a base de añadir más núcleos, a medida que la tecnología progrese la programación concurrente tomará más importancia para aumentar el rendimiento y para poder soportar más usuarios en las aplicaciones en el caso de la web. Hay ciertas tareas de las aplicaciones que se prestan a ello y que pueden ejecutarse de manera concurrente, por ejemplo, dos tareas que podrían ser son el envío de correos electrónicos y el precalculado de ciertos datos de una base de datos para mejorar los tiempos de acceso en posteriores consultas.

Dependiendo de la aplicación estas dos tareas probablemente no hace falta que sean inmediatas y es interesante que se produzcan fuera del thread que procesa la petición del usuario, más si se trata de una aplicación web. Hay que tener en cuenta que el enviar un correo electrónico, precalcular datos u otras tareas pueden ser algo que lleve una cantidad de tiempo notable, a partir de unos cientos de milisegundos a más de unos segundos. Si tenemos muchos usuarios en la aplicación y realizamos tareas como estas en el mismo thread de la petición el tiempo de respuesta percibido por el usuario será bajo, el número de usuarios concurrentes posibles será menor y escalar en número de usuarios será más dificil. Vamos a ver como solucionar tareas como estas utilizando la programación concurrente que ofrece Java desde la versión 1.5 y aplicando el patrón de diseño Command, patrón que se presta muy bien a ello.

Java en la versión 1.5 añadió el paquete java.util.concurrent para mejorar el soporte que ofrecía java para la programación concurrente con los Threads. La forma más sencilla de empezar a aprovecharlo es a través de la clase Executors que nos permite obtener referencias a objetos ExecutorService que será el que utilicemos para encolar las tareas. La clase Executors tiene varíos métodos que podemos aprovechar, entre ellos:
newFixedThreadPool nos permite obtener un pool de threads de tamaño fijo al que enviar tareas para ejecutarse. Si se envían más tareas que threads hay disponibles en el pool la tarea se encola esperando a que se libere algún thread del pool. newScheduledThreadPool permite programar la ejecución de las tareas a intervalos regulares, es una versión simple de lo que puede ofrecer Quartz ya que no soporta expresiones cron. Si necesitamos que las tareas se ejecuten de forma serializada y no de forma concurrente pero en otro momento de donde se crean podemos usar el ExecutorService devuelto por newSingleThreadExecutor.

Las tareas que se envían a los ExecutorService son clases que implementan la interfaz Runnable o Callable, esta última tiene la ventaja de que puede devolver un resultado y que puede lanzar una excepción. La interfaz Callable tiene un único método, call, y básicamente es una interfaz que sigue el patrón de diseño Command. El patrón Command encapsula los datos de una operación a realizar y desacopla el que crea el objeto del que realmente lo ejecuta.

Dicho todo esto la idea es crear tantas clases que implementen la interfaz Callable como tareas queramos ejecutar fuera del lugar de donde se crean y de forma concurrente, también necesitaremos un ExecutorService que en los siguientes ejemplos está en la clase CallableServiceImpl.

Esta podría ser la interfaz de un servicio que se encarga de ejecutar las tareas que se le envían a través de los métodos submit:

Esta implementación del servicio se encarga de ejecutar de forma concurrente las tareas según el número de threads de un ExecutorService:

El Callable para las tareas que envían correos electrónicos:

La clase mensaje de utilidad que contiene los datos para poder enviar el correo electrónico en EnviarEmailCallable:

La clase Callable que precalcula una serie de datos:

Y utilizando una factoría podemos evitar tener una dependencia sobre estas clases Callable en el código que las usen:

Finalmente el servicio y la implementación del servicio que hace uso de la factoria CallableFactory y el servicio CallableService:

Referencia:
http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/package-summary.html
http://en.wikipedia.org/wiki/Command_pattern
Patrones de diseño en la programación orientada a objetos
Ejemplo del patrón de diseño State
Ejemplo del patrón de diseño No Operation

viernes, 6 de abril de 2012

Usar Gradle mediante Gradle wrapper

Gradle
Después de ver que es la herramienta de construcción Gradle, cuales son sus características y que ventajas nos puede aportar en comparación sobre ant o maven en esta entrada vamos a ver como usarla en los proyectos de la forma más sencilla posible y sin necesidad de instalar manualmente sus binarios en nuestra máquina.

El no tener que instalar los binarios antes de poder hacer uso de gradle facilita el trabajo a una nueva persona que vaya a comenzar a colaborar en el proyecto ya que únicamente necesitará obtener el código fuente del mismo. También nos será útil si no tenemos control sobre el entorno donde lo vayamos a ejecutar como podría ser un entorno de integración continua. Para ello existe la herramienta gradle wrapper que una vez instalado en el proyecto nos permite usarlo sin necesidad de que descarguemos manualmente sus binarios, los descomprimamos en alguna carpeta de nuestro sistema y creemos una variable de entorno, de ello se encargará el envoltorio de gradle descargando todas las cosas necesarias a través de internet.

Pero antes de usar gradle wrapper hay que añadir su soporte en el proyecto, para ello añadiremos la siguiente tarea «wrapper» y la ejecutaremos (esta tarea hace uso de gradle con lo que al inicio de proyecto al menos una persona deberá instalar manualmente gradle). Esto añadirá el soporte al proyecto para usar gradle wrapper creando una serie de archivos, entre ellos están los archivos de lotes que usaremos a partir de este momento, gradlew para entorno linux y gradlew.bat para windows, también se añadirá una carpeta gradle/wrapper con una librería y un archivo de configuración.

task wrapper(type: Wrapper) {
 gradleVersion = '1.0-milestone-9'
}

$ ./gradle wrapper

A partir de este momento usaremos el archivo de lotes adecuado para nuestro sistema:

$ ./gradlew [tarea]

Referencia:
Herramienta de construcción Gradle
http://www.gradle.org/