Log4J, SMTPAppender: Envio de trazas de log por email
Una tarea fundamental en todo proyecto informático es la generación de log o trazas, generalmente para detectar y solucionar los posibles errores
que pudieran producirse.
Aunque normalmente estos logs suelen dejarse en archivos de texto, en determinadas situaciones puede ser
necesario enterarse rápidamente de los errores sin tener que esperar a que el usuario nos lo notifique.
Una posible solución podría ser que el sistema nos enviase esos errores a una cuenta de correo electrónico.
En este tutorial vamos a aprender como hacerlo, para ello nos basaremos en una sencilla aplicación que genera errores por correo electrónico cuando una simulada compra de productos falla.
BuyCard
: Simula un carrito de compra.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
package com.autentia.tutoriales.log4j; import java.util.ArrayList; import java.util.Random; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * Carrito de compra * @author Carlos García. Autentia * @see http://www.mobiletest.es */ public class BuyCard { private static Log log = LogFactory.getLog(BuyCard.class); private java.util.ArrayList<String> pedido; /** * Constructor */ public BuyCard(){ this.pedido = new ArrayList<String>(); } /** * Añade un producto al carrito de la compra * @param code Código del producto * @throws java.util.NoSuchElementException En caso de que el producto no exista o no hay unidades dispobibles en stock */ public void appendProduct(String code) throws java.util.NoSuchElementException { if (! this.isAvailable(code)){ if (log.isDebugEnabled()){ log.debug("Se ha solicitado un producto que no existe: " + code); } throw new java.util.NoSuchElementException(code); } pedido.add(code); if (log.isDebugEnabled()){ log.debug("El producto ha sido agregado al carrito correctamente: " + code); } } /** * @param code Código del producto * @return Indica un producto está en stock */ public boolean isAvailable(String code){ Random rnd = new Random(); int nRnd = rnd.nextInt(100); // Generamos número aleatorio entre 0-100 boolean exists = (nRnd >= 5); // Probabilidad baja de que el producto no exista. if (log.isDebugEnabled()){ log.debug("Producto " + code + ", dispobibilidad: " + exists); } return exists; } /** * Realiza el proceso de compra */ public boolean processBuy() { if (log.isDebugEnabled()){ log.debug("Realizando la compra"); } Random rnd = new Random(); int nRnd = rnd.nextInt(100); // Generamos número aleatorio entre 0-100 boolean ok = (nRnd >= 50); // 50% de probabilidad de que el proceso de compra compra falle. if (log.isDebugEnabled()){ log.debug("compra realizada correctamente: " + ok); } return ok; } } |
La siguiente aplicación de consola simula un proceso de compra de varios productos.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
package com.autentia.tutoriales.log4j; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.log4j.xml.DOMConfigurator; /** * Ejemplo del tutorial * @author Carlos García. Autentia. * @see http://www.mobiletest.es */ public class EjemploLog { /** * Punto de entrada a la aplicación */ public static void main(String[] args) { // Cargamos la configuración de Log4J desde un archivo accesible via CLASSPATH DOMConfigurator.configure(EjemploLog.class.getClassLoader().getResource("autentiaLog4jSmtp.xml")); // Obtenemos el Log para esta clase. Log log = LogFactory.getLog(EjemploLog.class); BuyCard compra = new BuyCard(); try { // Solicitamos unos productos compra.appendProduct("98HG"); compra.appendProduct("73DD"); compra.appendProduct("671A"); if (compra.processBuy()){ log.debug("La compra se ha realizado correctamente"); } else { Log smtp = LogFactory.getLog("soporteCompras"); smtp.info("No saldrá en el email por por el valor LevelMin del filtro LevelRangeFilter"); smtp.error("Se ha producido un error grave al realizar el proceso de compra"); } } catch (Exception ex){ log.warn("Error en la compra:", ex); } } } |
Si observa, en las líneas 35 y 36 se ve como la aplicación selecciona el Logger soporteCompras
para notificar el error por correo.
Dependencias necesarias para ejecutar la aplicación:
1 2 3 4 5 6 7 |
// Para el sistema de logs necesitamos: log4j-X.Y.jar commons-logging-X.Y.jar // Para el envio de correos necesitamos: mail-X.Y.jar activation-X.Y.jar |
Archivo de configuración de Log4j: autentiaLog4jSmtp.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE log4j:configuration SYSTEM "dtds/log4j.dtd"> <log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="false"> <appender name="file" class="org.apache.log4j.RollingFileAppender"> <param name="File" value="autentiaLog4jApp.log" /> <param name="MaxFileSize" value="2000000" /> <param name="MaxBackupIndex" value="5" /> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="%n%d{yyyy-MM-dd HH:mm:ss} [%-5p] [%l] %n%m%n" /> </layout> </appender> <appender name="email" class="org.apache.log4j.net.SMTPAppender"> <param name="BufferSize" value="10"/> <param name="SMTPHost" value="smtp_servidor.com" /> <param name="SMTPUsername" value="tu_usuario" /> <param name="SMTPPassword" value="tu_password_usuario" /> <param name="From" value="noreply@tu_servidor.com" /> <param name="To" value="cuenta_soporte@tu_servidor.com" /> <param name="Subject" value="Notificación de la aplicación" /> <filter class="org.apache.log4j.varia.LevelRangeFilter"> <param name="LevelMin" value="ERROR"/> <param name="LevelMax" value="FATAL"/> </filter> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="%n%d{yyyy-MM-dd HH:mm:ss} [%-5p] [%l] %n%m%n" /> </layout> </appender> <root> <appender-ref ref="file" /> </root> <logger name="soporteCompras"> <appender-ref ref="email" /> </logger> </log4j:configuration> |
BufferSize
: Número de eventos de log que serán enviados por email, (por motivos de rendimiento).- Si observa en las líneas 16-20 debemos poner la configuración necesaria para realizar la conexión al servidor SMTP y enviar el correo electrónico.
- Por defecto
SMTPAppender
almacenará en memoria todos eventos y los enviará cuando se reporte algún evento de nivel ERROR o superrior.
En este ejemplo debido al valor deLevelMin
sólo saldrán las trazas de error o superior.
En el archivo autentiaLog4jApp.log
la aplicación deja todas las trazas que genera la aplicación.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
2008-04-18 16:18:01 [DEBUG] [com.autentia.tutoriales.log4j.BuyCard.isAvailable(BuyCard.java:58)] Producto 98HG, dispobibilidad: true 2008-04-18 16:18:01 [DEBUG] [com.autentia.tutoriales.log4j.BuyCard.appendProduct(BuyCard.java:44)] El producto ha sido agregado al carrito correctamente: 98HG 2008-04-18 16:18:01 [DEBUG] [com.autentia.tutoriales.log4j.BuyCard.isAvailable(BuyCard.java:58)] Producto 73DD, dispobibilidad: true 2008-04-18 16:18:01 [DEBUG] [com.autentia.tutoriales.log4j.BuyCard.appendProduct(BuyCard.java:44)] El producto ha sido agregado al carrito correctamente: 73DD 2008-04-18 16:18:01 [DEBUG] [com.autentia.tutoriales.log4j.BuyCard.isAvailable(BuyCard.java:58)] Producto 671A, dispobibilidad: true 2008-04-18 16:18:01 [DEBUG] [com.autentia.tutoriales.log4j.BuyCard.appendProduct(BuyCard.java:44)] El producto ha sido agregado al carrito correctamente: 671A 2008-04-18 16:18:01 [DEBUG] [com.autentia.tutoriales.log4j.BuyCard.processBuy(BuyCard.java:69)] Realizando la compra 2008-04-18 16:18:01 [DEBUG] [com.autentia.tutoriales.log4j.BuyCard.processBuy(BuyCard.java:77)] compra realizada correctamente: false 2008-04-18 16:18:01 [ERROR] [com.autentia.tutoriales.log4j.EjemploLog.main(EjemploLog.java:36)] Se ha producido un error grave al realizar el proceso de compra |
Además de la salida que se observa en el log, la aplicación habrá enviado un correo electrónico con la información que aparecen en las líneas 25 y 26.
Tenga en cuenta que registrar un evento por correo es mucho más costoso en términos de rendimiento que hacerlo a un archivo, por lo que sería bueno realizar esta tarea en de forma asíncrona.
Conclusiones
La gestión de trazas es una parte fundamental de toda aplicación, por lo que es recomendable establecer alguna política o pautas al respecto dentro del proyecto.
Recordar que la mayor parte del dinero de un sistema informático se va en mantenimientos, así que un buen sistema de logs nos hará la vida más fácil y nos permitirá ahorrar muchos esfuerzos (dinerito).
Referencias y enlaces de interés
- Sitio Official del proyecto Log4J
- Log4J JavaDocs
- Tutorial: Introducción a Log4J
- Tutorial: Cómo crear distintos logs en función de su naturaleza y nivel
Carlos García Pérez. Creador de MobileTest, un complemento educativo para los profesores y sus alumnos.
cgpcosmad@gmail.com
no funciona, sale error
me podrías ayudar Carlos Garcia