Java 如何刷新缓冲的 log4j FileAppender?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3060240/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 16:06:34  来源:igfitidea点击:

How do you flush a buffered log4j FileAppender?

javalog4jbuffer

提问by Amos

In log4j, when using a FileAppender with BufferedIO=true and BufferSize=xxx properties (i.e. buffering is enabled), I want to be able to flush the log during normal shutdown procedure. Any ideas on how to do this?

在 log4j 中,当使用具有 BufferedIO=true 和 BufferSize=xxx 属性的 FileAppender(即启用缓冲)时,我希望能够在正常关闭过程中刷新日志。关于如何做到这一点的任何想法?

采纳答案by Niv

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

回答by rsp

Try:

尝试:

LogFactory.releaseAll();

回答by Amos

Managed to answer my own question :-)

设法回答了我自己的问题:-)

When shutting down the LogManager:

关闭 LogManager 时:

LogManager.shutdown();

all buffered logs get flushed.

所有缓冲的日志都被刷新。

回答by Ondra ?i?ka

Maybe you could override WriterAppender#shouldFlush( LoggingEvent ), so it would return truefor a special logging category, like log4j.flush.now, and then you call:

也许你可以覆盖WriterAppender#shouldFlush( LoggingEvent ),所以它会返回true一个特殊的日志类别,比如log4j.flush.now,然后你调用:

LoggerFactory.getLogger("log4j.flush.now").info("Flush")

http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/WriterAppender.html#shouldFlush%28org.apache.log4j.spi.LoggingEvent%29

http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/WriterAppender.html#shouldFlush%28org.apache.log4j.spi.LoggingEvent%29

回答by ewramner

I have written an appender that fixes this, see GitHubor use name.wramner.log4j:FlushAppender in Maven. It can be configured to flush on events with high severity and it can make the appenders unbuffered when it receives a specific message, for example "Shutting down". Check the unit tests for configuration examples. It is free, of course.

我写了一个 appender 来解决这个问题,请参阅GitHub或在 Maven 中使用 name.wramner.log4j:FlushAppender。它可以配置为刷新具有高严重性的事件,并且可以在收到特定消息时使附加程序无缓冲,例如“关闭”。检查配置示例的单元测试。当然,它是免费的。

回答by Andrey Kurilov

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

回答by Станислав Борисов

Sharing my experience with using "Andrey Kurilov"'s code example, or at least simmilar.

分享我使用“Andrey Kurilov”的代码示例的经验,或者至少是类似的。

What I actually wanted to achieve was to implement an asynchronous log entries with manual flush (immediateFlush = false)to ensure an idle buffers content will be flushed before the bufferSize is reached.

我真正想要实现的是使用手动刷新实现异步日志条目,(immediateFlush = false)以确保在达到 bufferSize 之前刷新空闲缓冲区内容。

The initial performance results were actually comparable with the ones achieved with the AsyncAppender- so I think it is a good alternative of it.

最初的性能结果实际上与使用AsyncAppender-实现的结果相当- 所以我认为这是一个很好的替代方案。

The AsyncAppenderis using a separate thread (and additional dependency to disruptorjar), which makes it more performant, but with the cost of more CPU and even more disk flushing(no matter with high load flushes are made on batches).

AsyncAppender使用一个单独的线程(以及附加的依赖性disruptor罐),这使得它更高性能,但具有更多的CPU的成本和更磁盘刷新(不管具有高负载冲洗是在批量制造)。

So if you want to save disk IO operations and CPU load, but still want to ensure your buffers will be flushed asynchronously at some point, that is the way to go.

因此,如果您想节省磁盘 IO 操作和 CPU 负载,但仍想确保您的缓冲区在某个时候异步刷新,这就是要走的路。