Java log4j毕竟不是线程安全的吗?
(这在SLES11、Java 7、Tomcat 6、log4j-1.2.16上) 我们使用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)。在代码中的一个地方(由多个线程执行),
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.