如何自动折叠log4j中的重复日志输出

时间:2016-07-08 16:20:28

标签: java logging log4j log4j2

每隔一段时间,服务器或数据库错误就会在服务器日志文件中导致数千个相同的堆栈跟踪。今天它可能是一个不同的错误/堆栈跟踪比一个月前。但它会导致日志文件完全旋转,我不再能够看到之前发生的事情。 (或者,我不想耗尽磁盘空间,因为我现在无法控制的原因是有限的 - 我单独解决了这个问题)。无论如何,我不需要数千份相同的堆栈跟踪 - 只需十几个就足够了。

我想它如果我可以让log4j / log4j2 /另一个系统自动崩溃重复错误,这样它们就不会填满日志文件。例如,来自同一地方的10或100个异常的阈值可能会触发log4j刚开始计数,并等到它们停止运行,然后输出它们出现多少次的计数。

存在哪些预先制定的解决方案(带链接的快速调查最佳)?如果这是我应该自己实现的,那么开始时应该注意什么是好的模式?我应该注意什么?

谢谢!

2 个答案:

答案 0 :(得分:3)

BurstFilter会做你想要的吗?如果没有,请使用适合您的算法创建一个Jira问题,Log4j团队很乐意考虑它。更好的是,如果你能提供一个补丁,它将更有可能被合并。

答案 1 :(得分:0)

Log4j的BurstFilter肯定有助于防止你填满你的磁盘。请记住对其进行配置,以便尽可能地应用于限制的一段代码,或者您将过滤掉您可能想要保留的消息(也就是说,不要在您的appender上使用它,而是在您在代码中隔离的特定记录器。)

我在一个点编写了一个简单的实用程序类,它包装了一个记录器并根据给定持续时间内的n条消息进行过滤。我在大多数警告和错误日志中使用了它的实例来保护我遇到问题的机会。它对我的情况非常有效,特别是因为它更容易快速适应不同的情况。

类似的东西:

...

public DurationThrottledLogger(Logger logger, Duration throttleDuration, int maxMessagesInPeriod) {
   ...
}

public void info(String msg) {
    getMsgAddendumIfNotThrottled().ifPresent(addendum->logger.info(msg + addendum));
}


private synchronized Optional<String> getMsgAddendumIfNotThrottled() {
    LocalDateTime now = LocalDateTime.now();
    String msgAddendum;
    if (throttleDuration.compareTo(Duration.between(lastInvocationTime, now)) <= 0) {
        // last one was sent longer than throttleDuration ago - send it and reset everything
        if (throttledInDurationCount == 0) {
            msgAddendum = " [will throttle future msgs within throttle period]";
        } else {
            msgAddendum = String.format(" [previously throttled %d msgs received before %s]",
                    throttledInDurationCount, lastInvocationTime.plus(throttleDuration).format(formatter));
        }
        totalMessageCount++;
        throttledInDurationCount = 0;
        numMessagesSentInCurrentPeriod = 1;
        lastInvocationTime = now;
        return Optional.of(msgAddendum);
    } else if (numMessagesSentInCurrentPeriod < maxMessagesInPeriod) {
        msgAddendum = String.format(" [message %d of %d within throttle period]", numMessagesSentInCurrentPeriod + 1, maxMessagesInPeriod);
        // within throttle period, but haven't sent max messages yet - send it
        totalMessageCount++;
        numMessagesSentInCurrentPeriod++;
        return Optional.of(msgAddendum);
    } else {
        // throttle it
        totalMessageCount++;
        throttledInDurationCount++;
        return emptyOptional;
    }
}

不幸的是,我从旧版本的代码中提取了这个,但要点就在那里。我编写了一些我主要使用的静态工厂方法,因为它们让我编写一行代码来为这一条日志消息创建其中一个:

} catch (IOException e) {
     DurationThrottledLogger.error(logger, Duration.ofSeconds(1), "Received IO Exception. Exiting current reader loop iteration.", e);
}

这可能在你的案件中并不重要;对我们来说,我们使用的是一个功能不足的graylog实例,我们可以很容易地压缩它。