java - log4j:防止重复日志消息的标准方法?

标签 java logging log4j

我们的生产应用程序在无法建立 TCP/IP 连接时记录错误。由于它不断重试连接,因此它一遍又一遍地记录相同的错误消息。同样,如果某些实时资源在一段时间内不可用,则应用程序中其他正在运行的组件可能会进入错误循环。

是否有任何标准方法来控制记录同一错误的次数? (我们正在使用 log4j,所以如果 log4j 有任何扩展来处理这个,那就完美了。)

最佳答案

我刚刚创建了一个 Java 类,它使用 log4j 解决了这个确切的问题。当我想记录一条消息时,我只是做这样的事情:

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

代替:

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

这使得它每 5 秒最多记录 1 次,并打印它应该记录的次数(例如 |x53|)。显然,您可以这样做,这样您就没有那么多参数,或者通过执行 log.warn 或其他操作来取消级别,但这适用于我的用例。

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

关于java - log4j:防止重复日志消息的标准方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9132193/

相关文章:

java - 如何对 ArrayList 进行洗牌,但洗牌后特定索引保持不变?

java - "${}"的名称是什么?在哪里可以找到有关 NetBeans IDE 的文档?

java - 尝试对 .txt 文件中的 double 进行排序时遇到问题

适用于 Node 和浏览器的 JavaScript 日志记录库

hadoop - 如何更改 hadoop 中 map reduce 函数的 log4j 级别

java - 打印多行字符串

java - Log4j:以编程方式更改日志级别,适用于要创建的记录器

java - 通过 TextArea 中的 JButton 删除单行

java - 在 application.yml 中设置根日志级别

java - 在部署时覆盖 log4j 设置