Как регистрировать повторные предупреждения только один раз - PullRequest
15 голосов
/ 02 декабря 2011

Есть модель, которая происходит время от времени. У меня есть метод, который вызывается много раз, и он содержит этот фрагмент:

Foo foo = getConfiguredFoo();
if (foo == null) {
  logger.warn("Foo not configured");
  foo = getDefaultFoo();
}

Тогда мой файл журнала сотни раз загромождается этим предупреждением. Я знаю, что могу grep это сделать, но мне интересно, есть ли лучший способ увидеть это предупреждение только один раз.

Примечание: дублирование сообщений является правильным поведением по умолчанию, поэтому речь идет не о во избежание непреднамеренного дублирования сообщения журнала . Я пометил свой вопрос как log4j, но я открыт для других сред ведения журналов java.

Ответы [ 4 ]

4 голосов
/ 04 декабря 2011

Вот что я могу придумать: класс, который накапливает предупреждения, которые могут быть сброшены в конце. Это в отличной форме, но вы можете понять суть. Разумеется, демпинговую часть можно настроить на использование регистратора.

class BadNews {
  static Map<String,List<Object>> warnings = [:];

  static void warn(String key, Object uniqueStuff) {
    def knownWarnings = warnings[key]
    if (! knownWarnings) {
      knownWarnings = []
      warnings[key] = knownWarnings
    }
    knownWarnings << uniqueStuff
  }

  static void dumpWarnings(PrintStream out) {
    warnings.each{key, stuffs ->
      out.println("$key: " + stuffs.size())
      stuffs.each{
        out.println("\t$it")
      }
    }
  }
}

class SomewhereElse {
  def foo(Bar bar) {
    if (! bar)
      BadNews.warn("Empty bar", this)
  }
}
2 голосов
/ 02 декабря 2011

Я столкнулся с подобной проблемой некоторое время назад, но не смог найти никакого способа справиться с этим в Log4J.Наконец, я сделал следующее:

Foo foo = getConfiguredFoo();
if (foo == null) {
  if(!warningLogged)logger.warn("Foo not configured");
  warningLogged = true
  foo = getDefaultFoo();
}

Это решение подходит, если у вас есть один или два оператора журнала, которые вы не хотите видеть повторяющимися в своих журналах, но не масштабируете их большим количеством операторов журнала (вам нужнологическое значение для каждого зарегистрированного сообщения)

1 голос
/ 02 декабря 2011

Вы можете написать обертку вокруг своей регистрации, чтобы сохранить последнюю зарегистрированную строку. В зависимости от того, как вы реализуете, вы можете добавить какой-то счетчик, чтобы регистрировать, сколько раз он был зарегистрирован, или вы можете выбрать подкласс Logger вместо использования внешней оболочки. Может быть сконфигурировано с логическим значением suppressDuplicates, если вам это тоже нужно.

public class LogWrapper{
    Logger logger = Logger.getLogger("your logger here");
    String lastLine = new String();

    public void warn(String s){
        if (lastLine.compareToIgnoreCase(s) == 0)
            return;
        else {
            lastLine = s;
            logger.warn(s);
        }
    }
}
0 голосов
/ 08 июня 2016

Если это единственное, что вы хотите напечатать один раз, то лучшим вариантом будет использование сохраненного логического значения.Если вы хотели что-то, что вы могли бы использовать в своем проекте, я создал что-то, что может быть полезным.Я только что создал класс Java, который использует экземпляр log4j logger.Когда я хочу записать сообщение в журнал, я просто делаю что-то вроде этого:

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

Вместо:

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

, что позволяет вести журнал максимум 1 раз каждые 5 секунд, ипечатает, сколько раз он должен был войти (например, | x53 |).Очевидно, вы можете сделать так, чтобы у вас не было столько параметров, или вытащить уровень, выполнив log.warn или что-то еще, но это работает для моего варианта использования.

Для вас (если вы хотите толькопечатать один раз, каждый раз) это излишне, но вы все равно можете сделать это, передав что-то вроде: Long.MAX_LONG в качестве 3-го параметра.Мне нравится гибкость, позволяющая определять частоту для каждого конкретного сообщения журнала (отсюда и параметр).Например, это выполнит то, что вы хотите:

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

Вот класс LogConsolidated:

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;
        }
    }
}
...