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

前端之家收集整理的这篇文章主要介绍了java – 毕竟log4j不是线程安全的吗?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
(这是在SLES11,Java 7,Tomcat 6,log4j-1.2.16)

我们使用log4j将不同的东西写入不同的日志文件.我继承了这段代码,所以无论好坏,一般结构都会暂时停留.

记录器将创建两个日志文件:main.log和stats.log.通过单独的调用将两个记录器记录到某个统计信息(您将在下面看到),并将大量其他内容记录到主日志中.

因此,通过我们的代码,您将看到诸如Log.logMain(someMessageToLog);之类的内容.在我们的代码中的一个地方(由多个线程执行)有以下内容

  1. String statsMessage = createStatsMessage();
  2. Log.logMain(statsMessage);
  3. Log.logStats(statsMessage);

主记录器的名称为main,统计记录器的名称为stats.问题是,有时在负载很重的情况下,我们会在main.log中看到其中包含字符串统计信息INFO的行. main.log中的所有内容都应该只包含主要的INFO,因为这是唯一记录到该文件的记录器,而且我们在某些行中看到混合输出.这似乎是线程安全问题,但log4j文档说log4j是线程安全的.这是我的意思的一个例子:

  1. 2012-03-21 16:01:34,7742012-03-21 16:01:34,774| | stats main INFO [INFO http-8080-18]: [message redacted].
  2. 2012-03-21 16:01:36,380| main 2012-03-21 16:01:36,380| INFO [stats INFO http-8080-15]: [message redacted].
  3. 2012-03-21 16:01:37,465| main INFO 2012-03-21 16:01:37,465 [| stats http-8080-1]: [message redacted].

这是Log类(被剥离以仅显示有问题的记录器 – 其中实际上有一堆其他记录器,所有记录器都与这些类似):

  1. import org.apache.log4j.*;
  2.  
  3. import java.io.IOException;
  4.  
  5. final public class Log
  6. {
  7. private static final String LOG_IDENTIFIER_MAINLOG = "main";
  8. private static final String LOG_IDENTIFIER_STATSLOG = "stats";
  9.  
  10. private static final String MAIN_FILENAME = "/var/log/app_main.log";
  11. private static final String STATS_FILENAME = "/var/log/app_stats.log";
  12.  
  13. private static final int BACKUP_INDEX = 40;
  14. private static final String BACKUP_SIZE = "10MB";
  15.  
  16. private static final PatternLayout COMMON_LAYOUT =
  17. new PatternLayout("%d| %c %-6p [%t]: %m.%n");
  18.  
  19. private static Logger mainLogger;
  20. private static Logger statsLogger;
  21.  
  22. public static void init() {
  23. init(MAIN_FILENAME,STATS_FILENAME);
  24. }
  25.  
  26. public static void init(String mainLogFilename,String statsLogFilename) {
  27. mainLogger = initializeMainLogger(mainLogFilename);
  28. statsLogger = initializeStatsLogger(statsLogFilename);
  29. }
  30.  
  31. public static void logMain(String message) {
  32. if (mainLogger != null) {
  33. mainLogger.info(message);
  34. }
  35. }
  36.  
  37. public static void logStats(String message) {
  38. if (statsLogger != null) {
  39. statsLogger.info(message);
  40. }
  41. }
  42.  
  43. private static Logger getLogger(String loggerIdentifier) {
  44. Logger logger = Logger.getLogger(loggerIdentifier);
  45. logger.setAdditivity(false);
  46. return logger;
  47. }
  48.  
  49. private static boolean addFileAppender(Logger logger,String logFilename,int maxBackupIndex,String maxSize) {
  50. try {
  51. RollingFileAppender appender =
  52. new RollingFileAppender(COMMON_LAYOUT,logFilename);
  53. appender.setMaxBackupIndex(maxBackupIndex);
  54. appender.setMaxFileSize(maxSize);
  55. logger.addAppender(appender);
  56. }
  57. catch (IOException ex) {
  58. ex.printStackTrace();
  59. return false;
  60. }
  61. return true;
  62. }
  63.  
  64. private static Logger initializeMainLogger(String filename) {
  65. Logger logger = getLogger(LOG_IDENTIFIER_MAINLOG);
  66. addFileAppender(logger,filename,BACKUP_INDEX,BACKUP_SIZE);
  67. logger.setLevel(Level.INFO);
  68. return logger;
  69. }
  70.  
  71. private static Logger initializeStatsLogger(String filename) {
  72. Logger logger = getLogger(LOG_IDENTIFIER_STATSLOG);
  73. addFileAppender(logger,BACKUP_SIZE);
  74. logger.setLevel(Level.INFO);
  75. return logger;
  76. }
  77.  
  78. }

更新:

这是一个小程序(至少对我而言)将使用上面的Log类重现问题:

  1. final public class Stress
  2. {
  3. public static void main(String[] args) throws Exception {
  4. if (args.length != 2) {
  5. Log.init();
  6. }
  7. else {
  8. Log.init(args[0],args[1]);
  9. }
  10.  
  11. for (;;) {
  12. // I know Executors are preferred,but this
  13. // is a quick & dirty test program
  14. Thread t = new Thread(new TestLogging());
  15. t.start();
  16. }
  17. }
  18.  
  19. private static final class TestLogging implements Runnable
  20. {
  21. private static int counter = 0;
  22.  
  23. @Override
  24. public void run() {
  25. String msg = new StringBuilder("Count is: ")
  26. .append(counter++).toString();
  27.  
  28. Log.logMain(msg);
  29. Log.logStats(msg);
  30.  
  31. try {
  32. Thread.sleep(1);
  33. }
  34. catch (InterruptedException e) {
  35. Log.logMain(e.getMessage());
  36. }
  37. }
  38. }
  39. }

以及日志中的一些示例输出

  1. $grep stats main.log
  2. 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.
  3. 2012-03-23 15:30:35,929| stats INFO [Thread-313100]: Count is: 313050.
  4. 2012-03-23 15:30:35,937| stats INFO [Thread-313168]: Count is: 313112.
  5. 2012-03-23 15:30:35,945| stats INFO [Thread-313240]: Count is: 313190.
  6. 2012-03-23 15:30:35,946| stats INFO [Thread-313251]: Count is: 313201.
  7. 2012-03-23 15:30:35,949| stats INFO [2012-03-23 15:30:35,949| main INFO Thread-313281]: Count is: 313231.
  8. 2012-03-23 15:30:35,954| stats INFO [Thread-313331]: Count is: 313281.
  9. 2012-03-23 15:30:35,956| 2012-03-23 15:30:35,956stats | main INFOINFO [ [Thread-313356]: Count is: 313306.
  10. 2012-03-23 15:30:35,9562012-03-23 15:30:35,956| main | INFO stats [INFOThread-313359]: Count is: 313309.
  11. 2012-03-23 15:30:35,962| stats INFO 2012-03-23 15:30:35,962| main INFO [Thread-313388]: [Count is: 313338.

  1. $grep main stats.log
  2. 2012-03-23 15:30:35,913| 2012-03-23 15:30:35,913| main INFO [Thread-312998]: Count is: 312948.
  3. 2012-03-23 15:30:35,915| main INFO [Thread-313014]: Count is: 312964.
  4. 2012-03-23 15:30:35,931| main INFO [Thread-313116]: Count is: 313066.
  5. 2012-03-23 15:30:35,947| main INFO [2012-03-23 15:30:35,947Thread-313264]: | Count is: 313214.
  6. 2012-03-23 15:30:35,962| main INFO [Thread-313388]: [Count is: 313338.

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

解决方法

http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/PatternLayout.html

你在两个appender之间共享PatternLayout,根据上面的API链接

已知此代码具有同步和org.apache.log4j.EnhancedPatternLayout中不存在的其他问题.应优先使用EnhancedPatternLayout而不是PatternLayout. EnhancedPatternLayout分布在log4j extras伴侣中.

因此,为每个appender创建一个新的PatternLayout

猜你在找的Java相关文章