2012-02-03 11 views
11

La nostra applicazione di produzione registra un errore quando non riesce a stabilire una connessione TCP/IP. Dal momento che riprova costantemente la connessione, registra lo stesso messaggio di errore più e più volte. Analogamente, altri componenti in esecuzione nell'applicazione possono entrare in un loop di errore se alcune risorse in tempo reale non sono disponibili per un periodo di tempo.log4j: metodo standard per evitare messaggi di registro ripetitivi?

Esiste un approccio standard per controllare il numero di volte in cui viene registrato lo stesso errore? (Stiamo usando log4j, quindi se c'è un'estensione per log4j per gestirlo, sarebbe perfetto.)

+0

controllare questo link http://stackoverflow.com/questions/8359839/how-to-log-repeated-warnings-only-once –

+1

@SajanChandran: ho pensato che avrei potuto "rotolare il mio", ma era come sperare che questo fosse un problema abbastanza comune, che esisteva già una soluzione standard/le migliori pratiche. Se faccio questo codice, molto probabilmente estenderò una classe log4j in modo che sia un'attività di configurazione, piuttosto che la codifica. –

+0

Questo potrebbe essere un buon primo passo: http://logging.apache.org/log4j/2.x/manual/filters.html#BurstFilter/ - Forse puoi scrivere il tuo filtro in modo simile a questo integrando il codice di questo answer: https://stackoverflow.com/a/37619797/1520422 –

risposta

2

Sarebbe abbastanza semplice controllarlo registrando un timestamp ogni volta che si registra l'errore, e quindi solo registrando la prossima volta se è trascorso un certo periodo. Idealmente questa sarebbe una caratteristica all'interno di log4j, ma codificarla all'interno della tua app non è poi così male, e potresti incapsularla all'interno di una classe helper per evitare lo stereotipo nel codice.

Chiaramente, ogni istruzione di registro ripetitivo avrebbe bisogno di una sorta di ID univoco in modo da poter unire le istruzioni dalla stessa origine.

3

Ho appena creato una classe Java che risolve questo problema esatto utilizzando log4j. Quando voglio registrare un messaggio, ho solo fare qualcosa di simile:

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

Invece di:

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

che lo rende registrare un massimo di 1 volta in assoluto 5 secondi, e stampe quante volte dovrebbe essere loggato (es. | x53 |). Ovviamente, puoi farlo in modo da non avere tanti parametri, o tirare fuori il livello facendo log.warn o qualcosa del genere, ma questo funziona per il mio caso d'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

Si noti che gli accessi alle mappe non sono thread-safe. Inoltre, il caso alternativo che esegue la registrazione effettiva può causare due thread che registrano lo stesso errore nello stesso momento. –

+0

È vero, è possibile che due thread che chiamano il logger dalla stessa identica riga di codice, possano far sì che il logger registri la stessa cosa due volte. Potrei risolvere il problema rendendo sicuri gli accessi al thread della mappa, ma perderò il colpo sulle prestazioni per avere la possibilità di un messaggio di registro duplicato. L'idea principale di tutto questo è di eliminare i messaggi di spam quando l'app si trova in uno stato negativo, quindi è più facile digerire il registro. Grazie per aver segnalato i problemi, lo apprezzo davvero! – 11101101b

+0

avviso bug: lastTimeAndCount.time deve essere ripristinato dopo ogni messaggio di registro in caso contrario dopo il tempo + delta - si termina la registrazione di tutti i messaggi. – rjha94

Problemi correlati