Archivos por Etiqueta: log4j

Java: Upgrade log4j 1.2

Si usas log4j 1.2 y estas leyendo este articulo seguramente te planteas actualizar, vayamos al grano…

Escenario 1: codigo que usa directamente log4j 1.2, no puedes tocarlo y necesitas 100% retrocompatibilidad.

Si necesitas reemplazar sin tocar codigo o sin compilar (tienes un entorno antiguo del que no tienes acceso al codigo o es un proyecto que funciona bien y meterse a cambiar codigo es poco viable, por poner un par de ejemplos), existen opciones, una es reemplazar el log4j-1.2.17.jar por reload4j-1.2.20.jar, directamente en el filesystem o bien cambiando las dependencias en el pom.xml, haciendo algun exclude y haciendo el package de nuevo. Este escenario es 100% retro-compatible (reload4j es un fork/parches sobre el log4j original). Este es de los pocos metodos que usaria en un entorno productivo «legacy» sin opcion de pruebas.

<!-- Log4j 1.2 replacement -->
<dependency>
	<groupId>ch.qos.reload4j</groupId>
	<artifactId>reload4j</artifactId>
	<version>1.2.20</version>
</dependency>

Escenario 2: usas slf4j con el backend de log4j 1.2 y necesitas mantenerlo por algun motivo.

Si estabas usando slf4j-api y slf4j-log4j12, este es practicamente como el escenario anterior, con un par de matices, puedes usar reload4j pero te recomendaria actualizar a la ultima version de slf4j (1.7.36) y cambiar slf4j-log4j12 por slf4j-reload4j (aqui hay que vigilar las dependencias finales/excludes).

<!-- Log4j 1.2 replacement -->
<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-reload4j</artifactId>
  <version>1.7.36</version>
</dependency>

Escenario 3: tienes log4j 1.2 pero quieres pasar a log4j 2.x con pocos cambios.

Otra opcion interesante, es mediante log4j-1.2-api, con esto emularas el log4j 1.2 pero usando el backend de log4j 2.x, no requiere tocar codigo, aunque tendras que cambiar el pom.xml (asegurarte de borrar las referencias o hacer los excludes de «log4j:log4j:1.2.17») y hacer el package, la magia corre de cuenta de log4j2. Este escenario diria que cubre el 80-90% de los casos de uso. Aqui no me la jugaria sin un entorno de pruebas.

<!-- Log4j 1.x bridge -->
<dependency>
  <groupId>org.apache.logging.log4j</groupId>
  <artifactId>log4j-1.2-api</artifactId>
  <version>2.17.2</version>
</dependency>

Hay otros escenarios / combinaciones, pero diria que la mayoria rondaran slf4j, reload4j o log4j2.

Mi recomendación es que si no necesitas ninguna funcionalidad especifica del framework de logging (como por ejemplo NDC), uses slf4j-api, por debajo ya es cuestion de gustos (mi preferencia siempre será el más sencillo o en su defecto, log4j*).

Referencias:
https://reload4j.qos.ch/
https://reload4j.qos.ch/manual.html
https://www.slf4j.org/legacy.html
https://logging.apache.org/log4j/2.x/manual/migration.html
https://logging.apache.org/log4j/1.2/
https://technobcn.wordpress.com/2013/02/04/java-logs-de-aplicaciones-la-senda-del-guerrero/

Java: Logs de Aplicaciones – La senda del Guerrero

Lógica básica a la hora de poner trazas:

  • No des por hecho que tienes acceso al código/subversión:
    Si tu traza (INFO/WARN/ERROR) necesita de leer el código, es que no es una buena traza.
  • No des por hecho que tienes trazas de nivel DEBUG:
    Si tienes que tener las trazas en modo debug es que necesitas una traza bien definida en un nivel inferior, ya sea ERROR, WARN o INFO.
  • No des por hecho que solo accede 1 usuario a tu aplicación:
    Las trazas se mezclan, haz que sean identificables y se puedan correlacionar las diferentes líneas de log y así saber que líneas pertenecen a que petición o usuario.
  • Ni mucha, ni poca; pon información útil:
    No hace falta que pongas el quijote, tampoco pongas un dato que no se sepa que es.
  • No pongas acentos; si las pones en ingles, mejor:
    Los acentos no siempre son ASCII/ISO-8859-1, los ficheros de logs a veces se ven mal o los “greps” no funcionan bien.

Ejemplo de traza “mala” (que no aporta nada útil, ni tiene contexto de referencia) (y se repite 100 veces sin ninguna diferencia):

2012-02-18 14:04:30,471 [catalina-exec-35] INFO  com.acme.coyote.servlets.UploadServlet - Buscando caracteres no compatibles en: ESTA TARDE VA A SALIR EL SOL

Ejemplo de traza “buena” (que aporta información útil):

2012-02-18 14:42:10,504 ERROR com.acme.XXXX [10.242.1.8:ACME:PEDRO] [Login] Error en password (incorrecto) company=ACME user=PEDRO

Usando MDC/NDC (log4j) para mejorar la trazabilidad

Ejemplo de uso/implementación (si usas threads “extras” o workers hay q trabajar un poco más este ejemplo para heredar/pasar la información de contexto, ya que los datos de MDC/NDC son “ThreadLocal”):

import org.apache.log4j.Logger;
import org.apache.log4j.MDC;
import org.apache.log4j.NDC;

// log4j.appender.XXXX.layout.ConversionPattern=%d{ISO8601} %p %c [%X{IP}:%X{SESSION}:%x] %m%n

public class MyClass extends HttpServlet {

    private final static Logger l = Logger.getLogger(MyClass.class);

    // [...blablabla...]

    public void doGet(final HttpServletRequest req, final HttpServletResponse resp)
                throws IOException, ServletException {
        try {
            MDC.put("IP", req.getRemoteAddr()); // MDC IP=10.242.1.8
            MDC.put("SESSION", req.getRequestedSessionId()); // MDC SESSION=7A7B9C9D721EAAFFAA1BB7E43BDFD2F1
            //
            NDC.push("doGet"); // NDC (add entry point)
            //
            String p_user = req.getParameter("user"));

            // Check Params
            if (p_user == null) {
                l.error("Invalid parameters USER is NULL");
                // 2012-02-18 16:42:10,504 ERROR com.acme.MyClass [10.242.1.8:7A7B9C9D721EAAFFAA1BB7E43BDFD2F1:doGet] Invalid parameters USER is NULL
                // [...blablabla...]
                return;
            }

            // Remapping Parameters
            p_user = p_user.trim().toUpperCase();

            NDC.push("USER=" + p_user); // NDC (add user info)

            try {
                NDC.push("Test");
                l.info("blablabla");
                // 2012-02-18 16:42:10,801 ERROR com.acme.MyClass [10.242.1.8:7A7B9C9D721EAAFFAA1BB7E43BDFD2F1:doGet USER=PEPE Test] blablabla
                // [...blablabla...]
            }
            finally {
                NDC.pop();
            }

            // [...blablabla...]

            l.info("Request OK");
            // 2012-02-18 16:42:11,102 ERROR com.acme.MyClass [10.242.1.8:7A7B9C9D721EAAFFAA1BB7E43BDFD2F1:doGet USER=PEPE] Request OK
        }
        catch (Exception e) {
            l.error("Request ERROR (Exception=" + e.toString() + ")");
            // 2012-02-18 16:42:11,102 ERROR com.acme.MyClass [10.242.1.8:7A7B9C9D721EAAFFAA1BB7E43BDFD2F1:doGet USER=PEPE] Request ERROR (Exception=java.lang.NullPointerException)
        }
        finally {
            MDC.clear();
            NDC.clear();
            NDC.remove();
        }
    }

    // [...blablabla...]
}

Referencias:
NDC vs MDC – Which one should I use?
Log4j MDC (Mapped Diagnostic Context) : Example code
Build Flexible Logs With log4j
Effective logging practices ease enterprise development