Este tutorial versa sobre cómo la conocida herramienta de logging para Java Log4j2 puede ser controlada «en caliente» (en tiempo de ejecución) mediante otra herramienta de gestión de Java, la JMX
0. Índice de contenidos
- 1. Introducción
- 2. Entorno
- 3. Creando la aplicación
- 4. Activando JMX
- 5. Monitorizando Log4j2
- 6. Conclusiones
- 7. Referencias
1. Introducción
A estas alturas, casi todo desarrllador usa (o debería usar) alguna herramienta de logging que permita conocer las trazas de las aplicaciones que implementa y gestiona. Una de las más conocidas sin duda es Apache Log4j.
La versión 2 de esta herramienta (a la que nos referiremos en el futuro como Log4j2) dispone (entre otras novedades) de soporte para la conocida JMX, el conjunto de herramientas de Java que permite, entre otras cosas, administrar las aplicaciones Java.
A lo largo de este tutorial veremos que dicho soporte permite modificar la configuración de Log4j en tiempo de ejecución, ya que algunos componentes de éste se instrumentan con MBeans, y por tanto, pueden ser controlados y monitorizados remotamente.
Para ello, se dispone de una interfaz gráfica (GUI) que permite ver la salida del componente StatusLogger y reconfigurar Log4j de forma remota mediante la edición del fichero de propiedades o incluso cambiar dicho fichero en tiempo de ejecución.
2. Entorno
El tutorial está escrito usando el siguiente entorno:
- Hardware: Portátil MacBook Pro 15′ (2.4 Ghz Intel Core I5, 8GB DDR3).
- Sistema Operativo: Mac OS Yosemite 10.10.5
- Entorno de desarrollo:
- Eclipse Mars
- Versión de Java: JDK 1.8.0_51
- Maven 3
- Log4j
3. Creando la aplicación
Para ilustrar lo que se ha dicho en la introducción, vamos a crear una aplicación Java gestionada con Maven que incluya un componente LOG, el cual será una instancia de Log4j.
3.1. Requisitos previos
En este tutorial se ha creado un proyecto Maven sencillo para gestionar la aplicación, ya que es más cómodo para la gestión de dependencias y los import de Java.
Se supone que el lector posee los siguientes conocimientos previos:
- Creación de proyectos Maven y Java con Eclipse
- Gestión de dependencias con Maven
- Uso de la herramienta Log4j2
- Uso de threads (hilos) en Java
- Bases sobre el uso de la terminal de comandos
¿Listos? Vamos a ello…
3.2. Configuración de Maven
Lo primero que debemos hacer es añadir en nuestro fichero pom.xml las dependencias necesarias para poder usar Log4j2:
<dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> <version>2.3</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.3</version> </dependency>
3.3. Configuración de Log4j2
Una vez tenemos las dependencias necesarias, debemos crear el fichero de configuración para Log4j2. Éste se localizará (en nuestro caso) en la carpera src/main/resources. Para nuestro ejemplo, usaremos la configuración por defecto (sacada de la página de configuración de Log4j2):
log4j2.xml
<?xml version="1.0" encoding="UTF-8"?> <Configuration status="WARN"> <Appenders> <Console name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/> </Console> </Appenders> <Loggers> <Root level="error"> <AppenderRef ref="Console"/> </Root> </Loggers> </Configuration>
Por defecto, el nivel mínimo para sacar un mensaje por consola (SYSTEM_OUT) es error. Esto dice que sólo los mensajes con nivel superior o igual a éste (error y fatal) serán los que salgan por pantalla.
3.4. Aplicación Java
Ya tenemos todo lo que tendremos que usar para crear un componente Logger y empezar a utilizarlo.
Creamos una clase que tenga un miembro de clase Logger y construimos un método main que muestre los distintos niveles de aviso de Log4j2:
NOTA: No usamos el nivel fatal.
Log4j2Main.java
package com.autentia.tutoriales.log4j2.main; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; public class Log4j2Main { private static final int NUM_OF_ITERATIONS = 50; private static final int MILLISECONDS_TO_SLEEP = 2000; private static final Logger LOG = LogManager.getLogger(Log4j2Main.class); public static void main(String[] args) throws InterruptedException { for (int i = 0; i < NUM_OF_ITERATIONS; i++) { LOG.trace("Mensaje de trace"); LOG.debug("Mensaje de debug"); LOG.info("Mensaje de info"); LOG.warn("Mensaje de warn"); LOG.error("Mensaje de error"); // para separar las iteraciones System.out.println(); Thread.sleep(MILLISECONDS_TO_SLEEP); } } }
El funcionamiento es bastante simple: cada dos segundos enviar todos los niveles de log que disponemos, y repetir el procedimiento cincuenta veces. Estas constantes pueden variar en función de nuestros deseos. Si la lanzamos, veremos que sólo salen por pantalla los mensajes de error. Eso es porque el nivel de traza está configurado como «ERROR».
4. Activando JMX
El soporte de JMX está activado por defecto. Cuando Log4j se inicializa, sus componentes (StatusLogger, ContextSelector, y todos los LoggerContexts, LoggerConfigs y Appenders) se instrumentan con MBeans (componentes que JMX monitoriza y gestiona).
Si se desea evitar la creación de estos MBeans, hay que especificar en los argumentos de la JVM la siguiente propiedad:
log4j2.disable.jmx=true
Hay más información sobre JMX en este enlace.
Se puede monitorizar el Log tanto en local como en remoto. Para hacerlo de forma local, no hace falta especificar a la JVM la propiedad arriba mencionada. Este ejemplo es el que veremos durante el tutorial.
Por su parte, la monitorización remota necesita la siguiente propiedad para la JVM:
com.sun.management.jmxremote.port=portNum
donde portNum indica el puerto que activará las conexiones RMI de JMX.
5. Monitorizando Log4j2
Ya está lista la aplicación; ahora toca monitorizar el miembro LOG. Esto lo podemos hacer de dos formas.
Primero vamos a utilizar una herramienta llamada JConsole, que nos permitirá ver la información que deseamos y al mismo tiempo cambiar la configuración de nuestro Logger.
Más tarde probaremos a usar la propia interfaz gráfica que nos brinda Log4j2, como un plugin de JConsole.
5.1. JConsole
La mejor forma de averiguar qué métodos y/o atributos de los componentes de Log4j2 son accesibles mediante JMX es explorarlos directamente mediante JConsole. Para lanzarla, escribimos el siguiente comando:
$JAVA_HOME/bin/jconsole
Esto nos abrirá la siguiente pantalla:
NOTA: la aplicación debe estar corriendo para que aparezca en la lista de procesos. Si no, la arrancamos, pulsamos en Connection -> New Connection y debería aparecer.
Pulsamos en nuestra aplicación, y tras pulsar en la pestaña MBeans y desplegar el directorio de org.apache.logging.log4j2 -> XXXXXXX (donde XXXXXXX es un número), obtendremos esta otra vista:
Pulsando en Loggers y seleccionando el único atributo que tiene, veremos en la parte derecha la propiedad Level. Ésta indica el nivel de traza configurado para nuestro LOG. Vemos que su valor es «ERROR»:
Si cambiamos ese valor por «INFO», la salida de nuestra aplicación cambiará, mostrando más niveles de traza (info y warn):
En esta última imagen se ve la iteración exacta en la que se refleja el cambio de nivel.
5.2. GUI de Log4j2 como plugin de JConsole
Log4j2 provee una GUI que puede ejecutarse como un plugin de JConsole. Para lanzar ésta con el plugin de Log4j2 ejecutamos el comando:
$JAVA_HOME/bin/jconsole -pluginpath /ruta/a/log4j-api-2.3.jar:/ruta/a/log4j-core-2.3.jar:/ruta/a/log4j-jmx-gui-2.3.jar
NOTA: Hay que descargarse los .jar previamente. Pueden descargarse desde este enlace.
Por defecto, el nivel del StatusLogger (el componente que se muestra al seleccionar la pestaña del plugin) es WARN, por lo que no muestra nada salvo que ocurra algo importante. Para cambiar este nivel, cambiamos su nivel a TRACE. De esta forma aparecerán los mensajes internos de estado. Se puede cambiar el nivel en la propia JConsole o en el fichero de configuración de Log4j2, en la línea:
<Configuration status="TRACE">
Este plugin también permite la edición «en caliente» del fichero de configuración mediante un editor de texto:é
Hay dos maneras de cambiar el fichero de configuración:
- Especificando un nuevo archivo desde otra localización.
- Editando el fichero XML actual desde el editor.
Especificando un nuevo fichero desde otra localización
Si cambiamos el fichero especificando una nueva localización y pulsamos el botón «Reconfigure from Location» se cargará esa nueva configuración. Para este método, el fichero debe existir y debe poder ser leído desde la aplicación (por ejemplo estando en el directorio src/main/resources de nuestra aplicación). En caso contrario ocurrirá un error y la configuración no cambiará (se mantendrá la configuración por defecto); sin embargo, el editor sí cambiar´ y mostrará el nuevo archivo.á
Vamos a cambiar con este método la configuración actual. Nuestro fichero original es el siguiente:
log4j2.xml
<?xml version="1.0" encoding="UTF-8"?> <Configuration status="TRACE"> <Appenders> <Console name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/> </Console> </Appenders> <Loggers> <Root level="error"> <AppenderRef ref="Console"/> </Root> </Loggers> </Configuration>
Creamos un nuevo fichero xml y lo llamamos log4j2-other.xml. Luego, escribimos el siguiente código:
log4j2-other.xml
<?xml version="1.0" encoding="UTF-8"?> <Configuration status="WARN"> <Appenders> <Console name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/> </Console> </Appenders> <Loggers> <Root level="info"> <AppenderRef ref="Console"/> </Root> </Loggers> </Configuration>
Hemos cambiado el nivel del Status Logger y el nivel de trazas del log (de error a info).
Ejecutamos la aplicación, abrimos JConsole con el plugin de Log4j2, vamos a la pestaña de configuración de Log4j2 y cambiamos el fichero:
Al pulsar el botón «Reconfigure from Location«, la salida de nuestro programa cambiará:
Editando el fichero desde el editor
Para cambiar la configuración mediante este método, sólo tenemos realizar los cambios que queramos y pulsar el otro botón, «Reconfigure with XML Below«. Esto mandará la configuración a la aplicación en tiempo de ejecución. Sin embargo, esto no sobre-escribirá el fichero de configuración, si no que la reconfiguración sólo ocurre en memoria y el texto no se almacena en ningún sitio.
Probamos el método: lanzamos la aplicación, abrimos JConsole con el plugin de Log4j2, vamos a la pestaña de configuración de Log4j2 y editamos el fichero:
Sólo cambiamos el nivel de traza de error a info.
Al pulsar el botón «Reconfigure with XML Below«, la salida de la aplicación mostrará una cantidad considerable de mensajes notificando el cambio y pasará a mostrar los nuevos mensajes:
6. Conclusiones
Log4j2 ofrece múltiples novedades respecto a su predecesor, pero sin duda una de las más útiles es el soporte a JMX, ya que permite monitorizar perfectamente nuestro sistema de logging y sus configuraciones de manera sencilla y eficaz.
Espero que el tutorial os haya animado a usar (o al menos probar) esta potente herramienta. El código del ejemplo (como siempre) se encuentra disponible en
Github.
¡Nos vemos!
Rodrigo de Blas
7. Referencias
- Log4j – JMX. Apache Logging
- Using JConsole. Oracle Documentation.