log4j: 防止重复的日志信息的标准方法?[英] log4j: Standard way to prevent repetitive log messages?

本文是小编为大家收集整理的关于log4j: 防止重复的日志信息的标准方法?的处理/解决方法,可以参考本文帮助大家快速定位并解决问题,中文翻译不准确的可切换到English标签页查看源文。

问题描述

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

是否有任何标准方法来控制相同错误已记录的次数? (我们正在使用log4j,因此,如果log4j有任何扩展名处理,这将是完美的.)

推荐答案

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

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

其他推荐答案

每次记录错误时,通过记录时间戳来控制它非常简单,然后仅在某个周期经过的情况下记录下来.

理想情况下,这将是 log4j中的功能,但是在应用程序中编码它并不糟糕,您可以将其封装在助手类中,以避免整个代码中的样板.

显然,每个重复的日志语句都需要某种唯一的ID,以便您可以从同一源合并语句.

本文地址:https://www.itbaoku.cn/post/1574977.html

问题描述

Our production application logs an error when it fails to establish a TCP/IP connection. Since it is constantly retrying the connection, it logs the same error message over and over. And similarly, other running components in the application can get into an error loop if some realtime resource is unavailable for a period of time.

Is there any standard approach to controlling the number of times the same error gets logged? (We are using log4j, so if there is any extension for log4j to handle this, it would be perfect.)

推荐答案

I just created a Java class that solves this exact problem using log4j. When I want to log a message, I just do something like this:

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

Instead of:

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

Which makes it log a maximum of 1 time ever 5 seconds, and prints how many times it should have logged (e.g. |x53|). Obviously, you can make it so you don't have as many parameters, or pull the level out by doing log.warn or something, but this works for my use case.

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

其他推荐答案

It would be fairly simple to control this by recording a timestamp each time you log the error, and then only logging it next time if a certain period has elapsed.

Ideally this would be a feature within log4j, but coding it within your app isn't too bad, and you could encapsulate it within a helper class to avoid boilerplate throughout your code.

Clearly, each repetitive log statement would need some kind of unique ID so that you could merge statements from the same source.