Java log4j毕竟不是线程安全的吗?

Java log4j毕竟不是线程安全的吗?,java,logging,thread-safety,log4j,Java,Logging,Thread Safety,Log4j,(这在SLES11、Java 7、Tomcat 6、log4j-1.2.16上) 我们使用log4j将不同的内容写入不同的日志文件。我继承了这段代码,所以无论好坏,总体结构都将暂时保留 记录器将创建两个日志文件:main.log和stats.log。一条特定的stats消息通过单独的调用(您将在下面看到)被记录到两个日志记录器中,而一大堆其他内容被记录到主日志中 因此,在我们的代码中,您将看到类似于Log.logMain(someMessageToLog)。在代码中的一个地方(由多个线程执行),

(这在SLES11、Java 7、Tomcat 6、log4j-1.2.16上)

我们使用log4j将不同的内容写入不同的日志文件。我继承了这段代码,所以无论好坏,总体结构都将暂时保留

记录器将创建两个日志文件:
main.log
stats.log
。一条特定的stats消息通过单独的调用(您将在下面看到)被记录到两个日志记录器中,而一大堆其他内容被记录到主日志中

因此,在我们的代码中,您将看到类似于
Log.logMain(someMessageToLog)。在代码中的一个地方(由多个线程执行),有以下内容:

String statsMessage = createStatsMessage();
Log.logMain(statsMessage);
Log.logStats(statsMessage);
主记录器的名称为
main
,统计记录器的名称为
stats
。问题是,有时在重载情况下,我们会在
main.log
中看到包含字符串
stats INFO
的行。
main.log
中的所有内容都应该只包含
main INFO
,因为这是唯一一个记录到该文件的记录器,而且我们在某些行中看到混合输出。这似乎是一个线程安全问题,但是log4j文档说log4j是线程安全的。这里有一个例子来说明我的意思:

2012-03-21 16:01:34,7742012-03-21 16:01:34,774| | stats main INFO   [INFO  http-8080-18]:  [message redacted]. 
2012-03-21 16:01:36,380| main 2012-03-21 16:01:36,380| INFO   [stats INFO  http-8080-15]: [message redacted]. 
2012-03-21 16:01:37,465| main INFO  2012-03-21 16:01:37,465 [| stats http-8080-1]: [message redacted]. 
下面是
Log
类(精简为只显示有问题的记录器——实际上其中有一堆其他记录器,所有设置与这些类似):


更新:

这里有一个小程序(至少对我来说)将重现上述
Log
类的问题:

final public class Stress
{
    public static void main(String[] args) throws Exception {
        if (args.length != 2) {
            Log.init();
        }
        else {
            Log.init(args[0], args[1]);
        }

        for (;;) {
            // I know Executors are preferred, but this
            // is a quick & dirty test program
            Thread t = new Thread(new TestLogging());
            t.start();
        }
    }

    private static final class TestLogging implements Runnable
    {
        private static int counter = 0;

        @Override
        public void run() {
            String msg = new StringBuilder("Count is: ")
                .append(counter++).toString();

            Log.logMain(msg);
            Log.logStats(msg);

            try {
                Thread.sleep(1);
            }
            catch (InterruptedException e) {
                Log.logMain(e.getMessage());
            }
        }
    }
}
以及日志中的一些示例输出:

$ grep stats main.log    
2012-03-23 15:30:35,919| stats 2012-03-23 15:30:35,919| main INFO  INFO   [ [Thread-313037]: Thread-313036]: Count is: 312987.
2012-03-23 15:30:35,929| stats INFO   [Thread-313100]: Count is: 313050.
2012-03-23 15:30:35,937| stats INFO   [Thread-313168]: Count is: 313112.
2012-03-23 15:30:35,945| stats INFO   [Thread-313240]: Count is: 313190.
2012-03-23 15:30:35,946| stats INFO   [Thread-313251]: Count is: 313201.
2012-03-23 15:30:35,949| stats INFO   [2012-03-23 15:30:35,949| main INFO  Thread-313281]: Count is: 313231.
2012-03-23 15:30:35,954| stats INFO   [Thread-313331]: Count is: 313281.
2012-03-23 15:30:35,956| 2012-03-23 15:30:35,956stats | main INFOINFO   [   [Thread-313356]: Count is: 313306.
2012-03-23 15:30:35,9562012-03-23 15:30:35,956| main | INFO  stats  [INFOThread-313359]:   Count is: 313309.
2012-03-23 15:30:35,962| stats INFO  2012-03-23 15:30:35,962| main INFO   [Thread-313388]:  [Count is: 313338.

值得一提的是,在145516行
main.log
文件中,“stats”出现了2452次。所以这并不罕见,但也不是一直都会发生(当然,这个测试非常极端)。

您正在两个附加器之间共享PatternLayout,根据上述API链接:

已知此代码存在org.apache.log4j.EnhancedPatternLayout中不存在的同步和其他问题。EnhancedPatternLayout应优先于PatternLayout使用。EnhancedPatternLayout分布在log4j extras companion中


因此,为每个追加器创建一个新的PatternLayout

追加器也可能有交叉的问题,但从这里显示的情况来看,这不太可能。您可以随时将
synchronized
添加到logMain/logStats中,以排除线程冲突,但如果您在重载下运行,性能下降可能会使其不适合生产。这就是我相信log4j常见问题解答以及它声称log4j是线程安全的原因:)谢谢!一旦我有机会在有问题的实际系统上验证这一点,我将接受这一点作为答案。+1这个对象只是闻起来不是线程安全的。它是用pattrn字符串构造的,也可以在以后更改其模式属性。它显然保持状态:(如果在每次调用时都传入模式字符串指针会更好。为每个appender创建一个新的
PatternLayout
,确实会使问题在所讨论的特定系统上消失。我接受这个答案。谢谢!
$ grep stats main.log    
2012-03-23 15:30:35,919| stats 2012-03-23 15:30:35,919| main INFO  INFO   [ [Thread-313037]: Thread-313036]: Count is: 312987.
2012-03-23 15:30:35,929| stats INFO   [Thread-313100]: Count is: 313050.
2012-03-23 15:30:35,937| stats INFO   [Thread-313168]: Count is: 313112.
2012-03-23 15:30:35,945| stats INFO   [Thread-313240]: Count is: 313190.
2012-03-23 15:30:35,946| stats INFO   [Thread-313251]: Count is: 313201.
2012-03-23 15:30:35,949| stats INFO   [2012-03-23 15:30:35,949| main INFO  Thread-313281]: Count is: 313231.
2012-03-23 15:30:35,954| stats INFO   [Thread-313331]: Count is: 313281.
2012-03-23 15:30:35,956| 2012-03-23 15:30:35,956stats | main INFOINFO   [   [Thread-313356]: Count is: 313306.
2012-03-23 15:30:35,9562012-03-23 15:30:35,956| main | INFO  stats  [INFOThread-313359]:   Count is: 313309.
2012-03-23 15:30:35,962| stats INFO  2012-03-23 15:30:35,962| main INFO   [Thread-313388]:  [Count is: 313338.
$ grep main stats.log
2012-03-23 15:30:35,913| 2012-03-23 15:30:35,913| main INFO   [Thread-312998]: Count is: 312948.
2012-03-23 15:30:35,915| main INFO   [Thread-313014]: Count is: 312964.
2012-03-23 15:30:35,919| stats 2012-03-23 15:30:35,919| main INFO  INFO   [ [Thread-313037]: Thread-313036]: Count is: 312987.
2012-03-23 15:30:35,931| main INFO   [Thread-313116]: Count is: 313066.
2012-03-23 15:30:35,947| main INFO   [2012-03-23 15:30:35,947Thread-313264]: | Count is: 313214.
2012-03-23 15:30:35,949| stats INFO   [2012-03-23 15:30:35,949| main INFO  Thread-313281]: Count is: 313231.
2012-03-23 15:30:35,956| 2012-03-23 15:30:35,956stats | main INFOINFO   [   [Thread-313356]: Count is: 313306.
2012-03-23 15:30:35,9562012-03-23 15:30:35,956| main | INFO  stats  [INFOThread-313359]:   Count is: 313309.
2012-03-23 15:30:35,962| stats INFO  2012-03-23 15:30:35,962| main INFO   [Thread-313388]:  [Count is: 313338.