2012-03-23 12 views
6

(यह एसएलएस 11, जावा 7, टॉमकैट 6, log4j-1.2.16 पर है)क्या लॉग 4j धागा सुरक्षित नहीं है?

हम विभिन्न लॉगफाइलों को अलग-अलग चीज़ों को लिखने के लिए log4j का उपयोग करते हैं। मुझे इस कोड को विरासत में मिला है, इसलिए अच्छे या बुरे के लिए सामान्य संरचना समय के लिए रहने के लिए यहां है।

लॉगर दो लॉगफाइल बनाएगा: main.log और stats.log। अलग-अलग कॉल के माध्यम से दोनों लॉगर्स को एक निश्चित आंकड़े संदेश लॉग किया जाता है (आप नीचे देखेंगे) और मुख्य लॉग में अन्य चीजों का एक पूरा समूह लॉग है।

तो हमारे कोड के माध्यम से आप 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 वर्ग (केवल करने के लिए नीचे छीन प्रश्न में वालों को दिखाने - वहाँ वास्तव में उस में अन्य वालों का एक समूह, इन सभी की तरह ही की स्थापना कर रहे हैं): यहाँ मैं क्या मतलब है की एक उदाहरण है:

import org.apache.log4j.*; 

import java.io.IOException; 

final public class Log 
{ 
    private static final String LOG_IDENTIFIER_MAINLOG = "main"; 
    private static final String LOG_IDENTIFIER_STATSLOG = "stats"; 

    private static final String MAIN_FILENAME = "/var/log/app_main.log"; 
    private static final String STATS_FILENAME = "/var/log/app_stats.log"; 

    private static final int BACKUP_INDEX = 40; 
    private static final String BACKUP_SIZE = "10MB"; 

    private static final PatternLayout COMMON_LAYOUT = 
     new PatternLayout("%d| %c %-6p [%t]: %m.%n"); 

    private static Logger mainLogger; 
    private static Logger statsLogger; 

    public static void init() { 
     init(MAIN_FILENAME, STATS_FILENAME); 
    } 

    public static void init(String mainLogFilename, 
          String statsLogFilename) { 
     mainLogger = initializeMainLogger(mainLogFilename); 
     statsLogger = initializeStatsLogger(statsLogFilename); 
    } 

    public static void logMain(String message) { 
     if (mainLogger != null) { 
      mainLogger.info(message); 
     } 
    } 

    public static void logStats(String message) { 
     if (statsLogger != null) { 
      statsLogger.info(message); 
     } 
    } 

    private static Logger getLogger(String loggerIdentifier) { 
     Logger logger = Logger.getLogger(loggerIdentifier); 
     logger.setAdditivity(false); 
     return logger; 
    } 

    private static boolean addFileAppender(Logger logger, 
              String logFilename, 
              int maxBackupIndex, 
              String maxSize) { 
     try { 
      RollingFileAppender appender = 
       new RollingFileAppender(COMMON_LAYOUT, logFilename); 
      appender.setMaxBackupIndex(maxBackupIndex); 
      appender.setMaxFileSize(maxSize); 
      logger.addAppender(appender); 
     } 
     catch (IOException ex) { 
      ex.printStackTrace(); 
      return false; 
     } 
     return true; 
    } 

    private static Logger initializeMainLogger(String filename) { 
     Logger logger = getLogger(LOG_IDENTIFIER_MAINLOG); 
     addFileAppender(logger, filename, BACKUP_INDEX, BACKUP_SIZE); 
     logger.setLevel(Level.INFO); 
     return logger; 
    } 

    private static Logger initializeStatsLogger(String filename) { 
     Logger logger = getLogger(LOG_IDENTIFIER_STATSLOG); 
     addFileAppender(logger, filename, BACKUP_INDEX, BACKUP_SIZE); 
     logger.setLevel(Level.INFO); 
     return logger; 
    } 

} 

अद्यतन:

यहाँ अल है ittle कार्यक्रम है कि (मेरे लिए कम से कम) के ऊपर 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. 

और

$ 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. 

के लिए यह 145516-लाइन main.log फ़ाइल से बाहर क्या है, "आंकड़े" 2452 बार इसमें दिखाई दिए। तो यह असामान्य नहीं है लेकिन ऐसा नहीं लगता है कि यह हर समय होता है, या तो (और निश्चित रूप से यह परीक्षण बहुत चरम है)।

+0

यह भी appenders किसी भी तरह पार हो रही है के साथ एक समस्या हो सकती है के लिए एक नया PatternLayout बनाने के लिए, लेकिन यह आप यहाँ क्या दिखाया है से होने की संभावना नहीं लगती है। आप लॉग इन करने के लिए लॉग इन करने के लिए लॉगइन/लॉगस्टैट्स में हमेशा 'सिंक्रनाइज़' जोड़ सकते हैं, लेकिन यदि आप भारी भार के तहत दौड़ रहे हैं तो प्रदर्शन हिट इसे उत्पादन के लिए अनुपयुक्त बना सकता है। – Thomas

उत्तर

10

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

आप दो appenders है, जो ऊपर एपीआई लिंक के अनुसार के बीच PatternLayout साझा कर रहे हैं:

इस कोड तुल्यकालन और अन्य मुद्दों जो संगठन में मौजूद नहीं हैं के लिए जाना जाता है। apache.log4j.EnhancedPatternLayout। EnternancedPatternLayout PatternLayout को वरीयता में इस्तेमाल किया जाना चाहिए। EnhancedPatternLayout log4j अतिरिक्त साथी में वितरित किया जाता है।

इसलिए प्रत्येक appender

+0

लॉग 4j एफएक्यू और उसके दावों पर विश्वास करने के लिए मुझे यही मिलता है कि log4j थ्रेड-सुरक्षित है :) धन्यवाद! एक बार मुझे वास्तविक प्रणाली पर इसे सत्यापित करने का मौका मिलने के बाद मैं इसे उत्तर के रूप में स्वीकार करूंगा। – QuantumMechanic

+1

+1 यह ऑब्जेक्ट बस थ्रेड-सुरक्षित नहीं होने की गंध करता है। यह पैट्रन स्ट्रिंग के साथ बना है और इसके पैटर्न की संपत्ति बाद में बदल सकती है। यह स्पष्ट रूप से राज्य को बनाए रखता है :(यदि बेहतर होगा तो पैटर्न कॉलिंग पॉइंटर प्रत्येक कॉल पर पारित किया गया होगा। –

+2

प्रत्येक एपेंडर के लिए एक नया 'पैटर्न लयआउट' बनाना वास्तव में समस्या को विशिष्ट सिस्टम पर समस्या दूर कर देता है। इस जवाब को स्वीकार कर रहा हूँ। धन्यवाद! – QuantumMechanic

संबंधित मुद्दे