2012-02-03 16 views
11

Nuestra aplicación de producción registra un error cuando no puede establecer una conexión TCP/IP. Como está constantemente reintentando la conexión, registra el mismo mensaje de error una y otra vez. Y de manera similar, otros componentes en ejecución en la aplicación pueden entrar en un bucle de error si algún recurso en tiempo real no está disponible durante un período de tiempo.log4j: ¿Forma estándar de evitar mensajes de registro repetitivos?

¿Existe algún método estándar para controlar el número de veces que se registra el mismo error? (Estamos usando log4j, entonces si hay alguna extensión para log4j para manejar esto, sería perfecto.)

+0

Comprobar este enlace http://stackoverflow.com/questions/8359839/how-to-log-repeated-warnings-only-once –

+1

@SajanChandran: pensé que podía "rodar mi propia", pero Esperaba que este fuera un problema bastante común, que ya había una solución estándar/mejor práctica. Si codigo esto, lo más probable es que extienda una clase log4j para que sea una tarea de configuración, en vez de una codificación. –

+0

Este podría ser un buen primer paso: http://logging.apache.org/log4j/2.x/manual/filters.html#BurstFilter/ - Tal vez puedas escribir tu propio filtro de manera similar a este integrando el código de este respuesta: https://stackoverflow.com/a/37619797/1520422 –

Respuesta

2

Sería bastante simple controlar esto registrando una marca de tiempo cada vez que registra el error, y luego solo registrándolo la próxima vez si ha transcurrido un cierto período.

ideal sería que esto sería una característica dentro log4j, pero la codificación dentro de su aplicación no es tan malo, y usted puede encapsular dentro de una clase de ayuda para evitar repetitivo a lo largo de su código.

Claramente, cada instrucción de registro repetitivo necesitaría algún tipo de ID única para que pueda fusionar declaraciones de la misma fuente.

3

Acabo de crear una clase de Java que resuelva este problema exacto usando log4j. Cuando quiero registrar un mensaje, acabo de hacer algo como esto:

LogConsolidated.log(logger, Level.WARN, 5000, "File: " + f + " not found.", e); 

En lugar de:

logger.warn("File: " + f + " not found.", e); 

que hace que sea ingrese un máximo de 1 vez en la historia de 5 segundos, e imprime el número de veces debería haberse registrado (por ejemplo | x53 |). Obviamente, puede hacerlo para que no tenga tantos parámetros, o eliminar el nivel haciendo log.warn o algo así, pero esto funciona para mi caso de uso.

import java.util.HashMap; 

import org.apache.log4j.Level; 
import org.apache.log4j.Logger; 

public class LogConsolidated { 

    private static HashMap<String, TimeAndCount> lastLoggedTime = new HashMap<>(); 

    /** 
    * Logs given <code>message</code> to given <code>logger</code> as long as: 
    * <ul> 
    * <li>A message (from same class and line number) has not already been logged within the past <code>timeBetweenLogs</code>.</li> 
    * <li>The given <code>level</code> is active for given <code>logger</code>.</li> 
    * </ul> 
    * Note: If messages are skipped, they are counted. When <code>timeBetweenLogs</code> has passed, and a repeat message is logged, 
    * the count will be displayed. 
    * @param logger Where to log. 
    * @param level Level to log. 
    * @param timeBetweenLogs Milliseconds to wait between similar log messages. 
    * @param message The actual message to log. 
    * @param t Can be null. Will log stack trace if not null. 
    */ 
    public static void log(Logger logger, Level level, long timeBetweenLogs, String message, Throwable t) { 
     if (logger.isEnabledFor(level)) { 
      String uniqueIdentifier = getFileAndLine(); 
      TimeAndCount lastTimeAndCount = lastLoggedTime.get(uniqueIdentifier); 
      if (lastTimeAndCount != null) { 
       synchronized (lastTimeAndCount) { 
        long now = System.currentTimeMillis(); 
        if (now - lastTimeAndCount.time < timeBetweenLogs) { 
         lastTimeAndCount.count++; 
         return; 
        } else { 
         log(logger, level, "|x" + lastTimeAndCount.count + "| " + message, t); 
        } 
       } 
      } else { 
       log(logger, level, message, t); 
      } 
      lastLoggedTime.put(uniqueIdentifier, new TimeAndCount()); 
     } 
    } 

    private static String getFileAndLine() { 
     StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); 
     boolean enteredLogConsolidated = false; 
     for (StackTraceElement ste : stackTrace) { 
      if (ste.getClassName().equals(LogConsolidated.class.getName())) { 
       enteredLogConsolidated = true; 
      } else if (enteredLogConsolidated) { 
       // We have now file/line before entering LogConsolidated. 
       return ste.getFileName() + ":" + ste.getLineNumber(); 
      } 
     } 
     return "?"; 
    }  

    private static void log(Logger logger, Level level, String message, Throwable t) { 
     if (t == null) { 
      logger.log(level, message); 
     } else { 
      logger.log(level, message, t); 
     } 
    } 

    private static class TimeAndCount { 
     long time; 
     int count; 
     TimeAndCount() { 
      this.time = System.currentTimeMillis(); 
      this.count = 0; 
     } 
    } 
} 
+0

Tenga en cuenta que los accesos al mapa no son seguros para subprocesos. Además, el caso else que hace el registro real puede dar como resultado dos subprocesos que registran el mismo error al mismo tiempo. –

+0

Es cierto que es posible que dos subprocesos que llaman al registrador desde la misma línea de código exacta, puedan hacer que el registrador registre lo mismo dos veces. Podría solucionar esto haciendo que los accesos al hilo del mapa sean seguros, pero renunciaré al desempeño para tener la posibilidad de un mensaje de registro duplicado. La idea principal de todo esto es eliminar los mensajes de correo no deseado cuando la aplicación entra en mal estado, por lo que es más fácil de digerir el registro. Gracias por señalar los problemas, ¡realmente lo aprecio! – 11101101b

+0

alerta de error: lastTimeAndCount.time debe restablecerse después de cada mensaje de registro; de lo contrario, después del tiempo + delta: finaliza la sesión de todos los mensajes. – rjha94

Cuestiones relacionadas