2012-02-03 8 views
11

Üretim uygulamamız bir TCP/IP bağlantısı kuramadığında bir hata kaydeder. Bağlantı sürekli olarak tekrar denendiği için, aynı hata mesajını tekrar tekrar kaydeder. Ve benzer şekilde, uygulamadaki diğer çalışan bileşenler, bir süre için bazı gerçek zamanlı kaynak kullanılamıyorsa bir hata döngüsüne girebilir.log4j: Tekrar eden günlük mesajlarını engellemenin standart yolu?

Aynı hatanın günlüğe kaydedilme sayısını kontrol etmek için standart bir yaklaşım var mı? (Log4j'i kullanmak için log4j kullanıyoruz, bu yüzden eğer bu işlemek için log4j için bir uzantı varsa, bu mükemmel olurdu.)

+0

Kontrol bu bağlantıyı http://stackoverflow.com/questions/8359839/how-to-log-repeated-warnings-only-once –

+1

@SajanChandran: Ben "Kendi rulo" düşündüm ama Bu zaten yeterli bir sorun olduğunu umuyordu, zaten standart çözüm/en iyi uygulama vardı. Bunu kodluyorsam, büyük olasılıkla bir log4j sınıfını genişleteceğim, böylece kodlama yerine bir yapılandırma görevi olacaktır. –

+0

Bu iyi bir ilk adım olabilir: http://logging.apache.org/log4j/2.x/manual/filters.html#BurstFilter/ - Belki de kendi kodunuzu buna benzer bir kod yazabilirsiniz. answer: https://stackoverflow.com/a/37619797/1520422 –

cevap

2

Bu, hatayı her kaydettiğinizde bir zaman damgası kaydederek bunu kontrol etmek oldukça basit olurdu, ve sadece Belirli bir süre geçtiğinde bir dahaki sefere kaydetme.

İdeal olarak bu, log4j içinde bir özellik olacaktır, ancak uygulamanızda kodlama yapmak çok kötü değildir ve kodunuz boyunca boilerplate'i önlemek için bir yardımcı sınıf içinde kapsülleyebilirsiniz.

Açıkçası, her bir yinelenen günlük deyimi, bir şekilde benzersiz bir ID'ye ihtiyaç duyar, böylece aynı kaynaktan gelen ifadeleri birleştirebilirsiniz.

3

Bu tam sorunu log4j kullanarak çözen bir Java sınıfı oluşturdum. Bir iletiyi günlüğe istediğinizde, sadece böyle bir şey yapmak:

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

yerine:

logger.warn("File: " + f + " not found.", e); 
o 1 defa maksimum şimdiye 5 saniye log yapar

ve baskılar kaç kez giriş yapmış olmalıydı (örneğin | x53 |). Açıkçası, çok fazla parametreye sahip olmamanız veya log.warn veya bir şey yaparak seviyeyi çekebilmek için bunu yapabilirsiniz, fakat bu benim kullanım durumum için işe yarıyor.

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

Harita erişiminin iş parçacığı güvenli olmadığını unutmayın. Ayrıca, gerçek günlük kaydı yapan aynı durumda aynı hatayı günlüğe kaydeden iki iş parçacığına neden olabilir. –

+0

Doğru, aynı kod satırından kaydediciyi çağıran iki iş parçacığının, aynı şeyleri iki kez günlüğe kaydetmesine neden olabilir. Bunu harita parçasına güvenli bir şekilde erişerek düzeltebilirim, ancak yinelenen bir günlük mesajı olasılığına sahip olmak için performans isabetini kaybedeceğim. Tüm bu şeyin ana fikri, uygulama kötü bir duruma girdiğinde spam mesajlarını kesmektir, bu yüzden günlüğü sindirmek daha kolaydır. Sorunları işaretlediğiniz için teşekkür ederim, gerçekten takdir ediyorum! – 11101101b

+0

hata uyarısı: lastTimeAndCount.time, her günlük mesajından sonra sıfırlanmalıdır, aksi halde + delta sonra - tüm iletileri günlüğe kaydedersiniz. – rjha94