【问题标题】:How do you flush a buffered log4j FileAppender?如何刷新缓冲的 log4j FileAppender?
【发布时间】:2010-06-17 09:03:25
【问题描述】:

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

【问题讨论】:

  • Log4J 在正常关机时不会自动刷新 appender 吗?我至少希望它会这样做。
  • 据我了解代码 - 当您决定使用 BufferedIO 时不会刷新。你获得了性能但付出了代价:你会丢失最后的日志条目......
  • 当我编写自己的附加程序(到 DB,但并不重要)时,我做了缓冲输出,同时每隔几秒自动刷新一次。

标签: java log4j buffer


【解决方案1】:

关闭 LogManager 时:

LogManager.shutdown();

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

【讨论】:

  • 请选择这个作为答案 - 这显然是最好的选择。如果您是自己赢得的,请不要因为从某人身上删除绿色勾号而感到难过。
  • 这看起来是最好的答案......但是你如何访问这个“LogManager”对象? (log4php新手)
【解决方案2】:
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() 并刷新这些记录器。
【解决方案3】:
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();
            }
        }
    }
}

【讨论】:

  • 虽然此代码可能会回答问题,但提供有关它如何和/或为什么解决问题的额外上下文将提高​​答案的长期价值。
  • 这段代码只是尝试刷新所有可刷新的附加程序(所有附加程序扩展 AbstractOutputStreamAppender,其中声明了方法“flush”)。在我的项目中将其与 Log4J2 v2.8.2 一起使用。
【解决方案4】:

也许你可以覆盖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

【讨论】:

    【解决方案5】:

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

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

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

    AsyncAppender 使用了一个单独的线程(以及对disruptor jar 的额外依赖),这使得它的性能更高,但代价是更多的 CPU 和更多的磁盘刷新(无论是高负载刷新)批量)。

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

    【讨论】:

      【解决方案6】:

      试试:

      LogFactory.releaseAll();
      

      【讨论】:

      • 不幸的是,调用它没有效果。
      【解决方案7】:

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

      【讨论】:

        【解决方案8】:

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

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

        【讨论】:

          猜你喜欢
          • 2010-09-20
          • 1970-01-01
          • 2015-08-10
          • 2021-09-03
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-07-11
          相关资源
          最近更新 更多