简体   繁体   English

为什么Log4j2的RollingFile附加程序会阻止独立的应用程序终止60秒?

[英]Why Log4j2's RollingFile appender prevents a stand alone application to terminate for 60 sec?

This code reproduces what I think is a bug of Log4j2. 这段代码重现了我认为是Log4j2的错误。 It's a simple loop that logs 2000 messages with two appenders: a console appender and a rolling file appender that rolls the file every 5Kb. 这是一个简单的循环,它通过两个附加程序记录2000条消息:控制台附加程序和滚动文件附加程序,每5Kb滚动文件一次。 This limit is intentionally low to reproduce what I think is a bug. 为了重现我认为是错误的缺陷,此限制故意较低。

Here's the code. 这是代码。

package bug;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Example {

    private static final Logger logger = LogManager.getLogger(Example.class);

    public static void main(String[] args) throws InterruptedException {
        for(int i = 0; i<2000; i++){
            logger.info("This is log message #{}.", i);
            Thread.sleep(0);
        }
    }

}

Here's the log4j2.xml configuration file. 这是log4j2.xml配置文件。

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="TRACE">
    <Appenders>
        <Console name="stdout" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} %p %m%n"/>
        </Console>
        <RollingFile name="roll-by-size"
                     fileName="target/log4j2/roll-by-size/app.log"
                     filePattern="target/log4j2/roll-by-size/app.%i.log.gz"
                     ignoreExceptions="false"
                     append="false">
            <PatternLayout>
                <Pattern>%d{yyyy-MM-dd HH:mm:ss} %p %m%n</Pattern>
            </PatternLayout>
            <Policies>
                <OnStartupTriggeringPolicy/>
                <SizeBasedTriggeringPolicy
                        size="5 KB"/>
            </Policies>
        </RollingFile>
    </Appenders>
    <Loggers>
        <Logger name="bug" level="TRACE">
            <AppenderRef ref="roll-by-size"/>
        </Logger>
        <Root level="DEBUG">
            <AppenderRef ref="stdout"/>
        </Root>
    </Loggers>
</Configuration>

What is strange is that when the application is launched you will see this logs in the console. 奇怪的是,当启动应用程序时,您将在控制台中看到此日志。

2016-12-22 22:12:36 INFO This is log message #1993.
2016-12-22 22:12:36 INFO This is log message #1994.
2016-12-22 22:12:36 INFO This is log message #1995.
2016-12-22 22:12:36 INFO This is log message #1996.
2016-12-22 22:12:36 INFO This is log message #1997.
2016-12-22 22:12:36 INFO This is log message #1998.
2016-12-22 22:12:36 INFO This is log message #1999.
2016-12-22 22:13:36,380 pool-1-thread-1 DEBUG Stopping LoggerContext[name=60199c81, org.apache.logging.log4j.core.LoggerContext@4597ec68]
2016-12-22 22:13:36,380 pool-1-thread-1 DEBUG Stopping LoggerContext[name=60199c81, org.apache.logging.log4j.core.LoggerContext@4597ec68]...
2016-12-22 22:13:36,381 pool-1-thread-1 TRACE Unregistering 1 MBeans: [org.apache.logging.log4j2:type=60199c81]
2016-12-22 22:13:36,381 pool-1-thread-1 TRACE Unregistering 1 MBeans: [org.apache.logging.log4j2:type=60199c81,component=StatusLogger]
2016-12-22 22:13:36,381 pool-1-thread-1 TRACE Unregistering 1 MBeans: [org.apache.logging.log4j2:type=60199c81,component=ContextSelector]
2016-12-22 22:13:36,381 pool-1-thread-1 TRACE Unregistering 2 MBeans: [org.apache.logging.log4j2:type=60199c81,component=Loggers,name=bug, org.apache.logging.log4j2:type=60199c81,component=Lo
ggers,name=]
2016-12-22 22:13:36,381 pool-1-thread-1 TRACE Unregistering 2 MBeans: [org.apache.logging.log4j2:type=60199c81,component=Appenders,name=roll-by-size, org.apache.logging.log4j2:type=60199c81,c
omponent=Appenders,name=stdout]
2016-12-22 22:13:36,382 pool-1-thread-1 TRACE Unregistering but no MBeans found matching 'org.apache.logging.log4j2:type=60199c81,component=AsyncAppenders,name=*'
2016-12-22 22:13:36,382 pool-1-thread-1 TRACE Unregistering but no MBeans found matching 'org.apache.logging.log4j2:type=60199c81,component=AsyncLoggerRingBuffer'
2016-12-22 22:13:36,382 pool-1-thread-1 TRACE Unregistering but no MBeans found matching 'org.apache.logging.log4j2:type=60199c81,component=Loggers,name=*,subtype=RingBuffer'
2016-12-22 22:13:36,382 pool-1-thread-1 TRACE Stopping XmlConfiguration[location=C:\Users\danidemi\workspace\bug-log4j2-hanging-up-before-shutdown\target\classes\log4j2.xml]...
2016-12-22 22:13:36,382 pool-1-thread-1 TRACE XmlConfiguration notified 3 ReliabilityStrategies that config will be stopped.
2016-12-22 22:13:36,382 pool-1-thread-1 TRACE XmlConfiguration stopping 2 LoggerConfigs.
2016-12-22 22:13:36,382 pool-1-thread-1 TRACE XmlConfiguration stopping root LoggerConfig.
2016-12-22 22:13:36,382 pool-1-thread-1 TRACE XmlConfiguration notifying ReliabilityStrategies that appenders will be stopped.
2016-12-22 22:13:36,382 pool-1-thread-1 TRACE XmlConfiguration stopping remaining Appenders.
2016-12-22 22:13:36,383 pool-1-thread-1 DEBUG Shutting down RollingFileManager target/log4j2/roll-by-size/app.log
2016-12-22 22:13:36,383 pool-1-thread-1 DEBUG Shut down RollingFileManager target/log4j2/roll-by-size/app.log, all resources released: true
2016-12-22 22:13:36,383 pool-1-thread-1 DEBUG Shutting down OutputStreamManager SYSTEM_OUT.false.false
2016-12-22 22:13:36,383 pool-1-thread-1 DEBUG Shut down OutputStreamManager SYSTEM_OUT.false.false, all resources released: true
2016-12-22 22:13:36,384 pool-1-thread-1 TRACE XmlConfiguration stopped 2 remaining Appenders.
2016-12-22 22:13:36,384 pool-1-thread-1 TRACE XmlConfiguration cleaning Appenders from 3 LoggerConfigs.
2016-12-22 22:13:36,384 pool-1-thread-1 DEBUG Stopped XmlConfiguration[location=C:\Users\danidemi\workspace\bug-log4j2-hanging-up-before-shutdown\target\classes\log4j2.xml] OK
2016-12-22 22:13:36,385 pool-1-thread-1 DEBUG Stopped LoggerContext[name=60199c81, org.apache.logging.log4j.core.LoggerContext@4597ec68]...

What is strange is that the last log is issued at a certain time... 奇怪的是最后一个日志是在某个时间发布的...

2016-12-22 22:12:36 INFO This is log message #1999.

but the shutdown of log4j2 starts exactly one minute after the last "business" log message. 但是log4j2的关闭恰好在最后一条“业务”日志消息之后一分钟开始。

2016-12-22 22:13:36,380 pool-1-thread-1 DEBUG Stopping LoggerContext[name=60199c81, org.apache.logging.log4j.core.LoggerContext@4597ec68]

This is the problem! 这就是问题! The business logic terminates, but log4j2 waits for one minute before allow the app to stop! 业务逻辑终止,但是log4j2等待一分钟,然后允许该应用停止! Why that ? 为什么 ? I would prefer the application to stop immediately as one would probably expect. 我希望该应用程序可以像人们期望的那样立即停止。

I investigated a little... this 60 sec delay seems more or less independent from the number of messages being logged. 我调查了一下... 60秒的延迟似乎或多或少与记录的消息数量无关。

However, if you change the log4j2.xml incrementing the size from 5Kb... 但是,如果您更改log4j2.xml将大小从log4j2.xml ...

<Policies>
    <OnStartupTriggeringPolicy/>
    <SizeBasedTriggeringPolicy size="5 KB"/>
</Policies>

to 5Mb... 至5Mb ...

<Policies>
    <OnStartupTriggeringPolicy/>
    <SizeBasedTriggeringPolicy size="5 MB"/>
</Policies>

... that makes the application to stop immediately right after the last log message. ...,使应用程序在最后一条日志消息之后立即停止。 5Mb is a limit big enough not to require the rolling to actually take place. 5Mb是一个足够大的限制,不需要实际进行滚动。 So I think that is the rolling itself that in some way make this delay to occur. 因此,我认为正是滚动本身以某种方式导致了这种延迟的发生。 I think it's a bug but... what do you think? 我认为这是一个错误,但是...您认为呢?

I've set up a small Maven project on GitHub that demonstrates what I tried to explain here. 我在GitHub上建立了一个小型Maven项目,演示了我在这里试图解释的内容。

Thank you for raising this issue to the Log4j2 community. 感谢您向Log4j2社区提出此问题

The underlying cause is that Log4j creates two ThreadPoolExecutors, one daemon and one non-daemon. 根本原因是Log4j创建了两个ThreadPoolExecutor,一个守护程序和一个非守护程序。 These executors are created with the default settings, which sets the thread keepAliveTime to one minute. 这些执行程序是使用默认设置创建的,该默认设置将线程keepAliveTime设置为一分钟。

In the example, the application shuts down immediately after triggering a rollover. 在该示例中,应用程序在触发过渡后立即关闭。 The rollover compresses the old file in a background thread in the non-daemon executor. 翻转将非守护程序执行程序中的旧文件压缩到后台线程中。 Because the executor keeps this thread alive for one minute, and this is a non-daemon thread, the application as a whole is kept alive for one minute. 因为执行程序使该线程保持活动状态一分钟,而这是一个非守护程序线程,所以整个应用程序将保持活动状态一分钟。

This will only impact applications that exit immediately after a rollover. 这只会影响过渡后立即退出的应用程序。

Update 2016-12-26: This has been fixed. 更新2016-12-26:此问题已修复。 From 2.8, non-daemon threads will have a shorter (one second) keepAliveTime by default. 从2.8开始,默认情况下,非守护程序线程将具有较短(一秒)的keepAliveTime。

您可以尝试使用LogManager.shutdown()作为程序的最后一个命令。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM