如何刷新缓冲的log4j FileAppender?

24
在log4j中,当使用带有BufferedIO=true和BufferSize=xxx属性的FileAppender(即启用缓冲)时,我想在正常关闭过程中刷新日志。有什么建议吗?

Log4J在正常关闭期间不自动刷新appender吗?至少我会期望它这么做。 - Péter Török
1
据我理解代码 - 当您选择BufferedIO时,不会刷新。您可以获得更好的性能,但代价是:您将失去最后的日志条目... - Andreas Dolk
当我编写自己的 appender(到 DB,但并不重要)时,我进行了缓冲输出,同时每隔几秒钟自动刷新。 - ripper234
8个回答

55

关闭LogManager时:

LogManager.shutdown();

所有缓存的日志都会被刷新。


10
请将此选项作为答案——显然是最好的选择。如果你自己赚到了绿色勾号,不要因为从其他人那里删除而感到难过。 - Duncan Jones
这看起来像是最佳答案...但是你如何访问这个“LogManager”对象?(log4php新手) - mike rodent

7
public static void flushAllLogs()
{
    try
    {
        Set<FileAppender> flushedFileAppenders = new HashSet<FileAppender>();
        Enumeration currentLoggers = LogManager.getLoggerRepository().getCurrentLoggers();
        while(currentLoggers.hasMoreElements())
        {
            Object nextLogger = currentLoggers.nextElement();
            if(nextLogger instanceof Logger)
            {
                Logger currentLogger = (Logger) nextLogger;
                Enumeration allAppenders = currentLogger.getAllAppenders();
                while(allAppenders.hasMoreElements())
                {
                    Object nextElement = allAppenders.nextElement();
                    if(nextElement instanceof FileAppender)
                    {
                        FileAppender fileAppender = (FileAppender) nextElement;
                        if(!flushedFileAppenders.contains(fileAppender) && !fileAppender.getImmediateFlush())
                        {
                            flushedFileAppenders.add(fileAppender);
                            //log.info("Appender "+fileAppender.getName()+" is not doing immediateFlush ");
                            fileAppender.setImmediateFlush(true);
                            currentLogger.info("FLUSH");
                            fileAppender.setImmediateFlush(false);
                        }
                        else
                        {
                            //log.info("fileAppender"+fileAppender.getName()+" is doing immediateFlush");
                        }
                    }
                }
            }
        }
    }
    catch(RuntimeException e)
    {
        log.error("Failed flushing logs",e);
    }
}

这并不会刷新所有的记录器。要想全部刷新,你需要迭代地调用每个记录器的getParent()方法,并将这些记录器也刷新。 - Audrius Meškauskas

5
public static void flushAll() {
    final LoggerContext logCtx = ((LoggerContext) LogManager.getContext());
    for(final org.apache.logging.log4j.core.Logger logger : logCtx.getLoggers()) {
        for(final Appender appender : logger.getAppenders().values()) {
            if(appender instanceof AbstractOutputStreamAppender) {
                ((AbstractOutputStreamAppender) appender).getManager().flush();
            }
        }
    }
}

虽然这段代码可能回答了问题,但是提供关于它是如何以及为什么解决问题的额外上下文信息会提高答案的长期价值。 - Donald Duck
这段代码尝试刷新所有可刷新的 appender(即扩展 AbstractOutputStreamAppender 的所有 appender,其中声明了 "flush" 方法)。我在我的项目中使用 Log4J2 v2.8.2。 - Andrey Kurilov

1

对我来说唯一有效的解决方案是等待一段时间:

private void flushAppender(Appender appender) {
    // this flush seems to be useless
    ((AbstractOutputStreamAppender<?>) appender).getManager().flush(); 
    try {
        Thread.sleep(500); // wait for log4j to flush logs
    } catch (InterruptedException ignore) {
    }
}

1

1
分享我使用"Andrey Kurilov"的代码示例,或者至少类似的经验。
我实际想要实现的是使用手动刷新(immediateFlush = false)的异步日志条目,以确保空闲缓冲区的内容在达到缓冲区大小之前被刷新。
最初的性能结果实际上与使用AsyncAppender实现的结果相当-因此我认为它是一个很好的替代方案。
AsyncAppender使用单独的线程(和额外的依赖于disruptor jar),这使得它更具有性能,但代价是更多的CPU和更多的磁盘刷新(无论负载高低都会进行批量刷新)。
因此,如果您想节省磁盘IO操作和CPU负载,但仍希望确保缓冲区最终会异步刷新,那么这就是正确的方式。

0

尝试:

LogFactory.releaseAll();

1
很遗憾,调用这个没有任何效果。 - Amos

0
我已经编写了一个修复此问题的appender,请参见GitHub或在Maven中使用name.wramner.log4j:FlushAppender。它可以配置为在高严重性事件上刷新,并在接收到特定消息(例如“关闭”)时使附加程序无缓冲区。请查看单元测试以获取配置示例。当然,它是免费的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接