【问题标题】:How to automatically collapse repetitive log output in log4j如何在 log4j 中自动折叠重复的日志输出
【发布时间】:2016-07-08 16:20:27
【问题描述】:

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

如果我可以让 log4j/log4j2/another 系统自动折叠重复错误,我希望它不会填满日志文件。例如,来自同一个地方的 10 或 100 个异常的阈值可能会触发 log4j 开始计数,并等到它们停止出现,然后输出它们出现的次数。

存在哪些预制解决方案(最好使用链接进行快速调查)?如果这是我应该自己实现的东西,什么是一个好的开始模式,我应该注意什么?

谢谢!

【问题讨论】:

  • 您是否尝试过扩展日志文件的大小和/或数量?此外,您的问题的真正解决方案似乎可以解决导致所有堆栈跟踪的问题。
  • @DwB,正如我在原始问题中提到的那样,我对 VM 磁盘大小没有太多控制权(这个决定大部分不在我的手中,正在单独解决);对于您关于修复堆栈跟踪原因的建议的另一部分:是的,当然这是给定的,但我们并不生活在一个理想的世界中;尤其是当系统具有超出我控制范围的集成点和依赖项时,我不一定有时间修复所有问题。另外,正如我所说的那样,当我有意外的错误输出时,要保留日志。
  • @KimballRobinson:你有没有找到任何好的现有解决方案?如果您能分享一些意见以及您最终如何解决这个问题,那就太好了。

标签: java logging log4j log4j2


【解决方案1】:

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

【讨论】:

  • 这看起来非常接近我正在寻找的内容,尽管我想通过行号或方法名称进一步限制它(例如,只过滤具有相同来源的类似消息)。不过,这是朝着正确方向迈出的一步。
【解决方案2】:

Log4j 的 BurstFilter 肯定会帮助您防止磁盘被填满。请记住对其进行配置,使其尽可能应用于有限的代码部分,否则您将过滤掉您可能想要保留的消息(也就是说,不要在 appender 上使用它,而是在特定的 logger 上使用它你在你的代码中隔离)。

我曾经编写了一个简单的实用程序类,它包装了一个记录器并根据给定 Duration 内的 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 实例,我们可以很容易地处理它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-31
    • 1970-01-01
    • 2015-05-05
    • 2016-08-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多