Log4j में, प्रदर्शन में सुधार करने से पहले लॉगिंग से पहले isDebugEnabled की जाँच करता है?


207

मैं लॉगिंग के लिए अपने आवेदन में Log4J का उपयोग कर रहा हूं । पहले मैं डिबग कॉल का उपयोग कर रहा था जैसे:

विकल्प 1:

logger.debug("some debug text");

लेकिन कुछ लिंक बताते हैं कि isDebugEnabled()पहले जाँच करना बेहतर है , जैसे:

विकल्प 2:

boolean debugEnabled = logger.isDebugEnabled();
if (debugEnabled) {
    logger.debug("some debug text");
}

तो मेरा सवाल है " क्या विकल्प 2 किसी भी तरह से प्रदर्शन में सुधार करता है? "।

क्योंकि किसी भी स्थिति में Log4J फ्रेमवर्क डिबग इनेबल्ड के लिए एक ही जाँच है। विकल्प 2 के लिए यह फायदेमंद हो सकता है यदि हम एकल विधि या वर्ग में कई डिबग स्टेटमेंट का उपयोग कर रहे हैं, जहां रूपरेखा को isDebugEnabled()कई बार (प्रत्येक कॉल पर) विधि की आवश्यकता नहीं है ; इस स्थिति में यह isDebugEnabled()केवल एक बार विधि कहता है , और यदि Log4J को डिबग स्तर पर कॉन्फ़िगर किया गया है, तो वास्तव में यह isDebugEnabled()दो बार विधि कहता है :

  1. डिबग इनेबल्ड चर के लिए मान असाइन करने के मामले में, और
  2. वास्तव में logger.debug () विधि द्वारा कहा जाता है।

मुझे नहीं लगता है कि यदि हम विकल्प 1 के अनुसार logger.debug()विधि या वर्ग और कॉलिंग debug()विधि में कई कथन लिखते हैं तो यह विकल्प 2 की तुलना में Log4J फ्रेमवर्क के लिए ओवरहेड है। चूंकि isDebugEnabled()बहुत छोटी विधि है (कोड के संदर्भ में), यह हो सकता है inlining के लिए अच्छा उम्मीदवार हो।

जवाबों:


247

इस विशेष मामले में, विकल्प 1 बेहतर है।

गार्ड स्टेटमेंट (चेकिंग isDebugEnabled()) लॉग संदेश के संभावित रूप से महंगी गणना को रोकने के लिए है जब इसमें toString()विभिन्न वस्तुओं के तरीकों का आह्वान और परिणामों को सम्मिलित करना शामिल है।

दिए गए उदाहरण में, लॉग संदेश एक निरंतर स्ट्रिंग है, इसलिए लकड़हारे को त्यागने देना केवल यह जाँचने में कुशल है कि लकड़हारा सक्षम है, और यह कोड की जटिलता को कम करता है क्योंकि इसकी शाखाएँ कम हैं।

बेहतर अभी तक एक अधिक अप-टू-डेट लॉगिंग फ्रेमवर्क का उपयोग करना है जहां लॉग स्टेटमेंट एक प्रारूप विनिर्देश लेते हैं और लकड़हारा द्वारा प्रतिस्थापित किए जाने वाले तर्कों की एक सूची है - लेकिन "लेज़ीली," केवल अगर लकड़हारा सक्षम है। यह slf4j द्वारा लिया गया दृष्टिकोण है ।

अधिक जानकारी के लिए संबंधित प्रश्न के लिए मेरा उत्तर देखें , और log4j के साथ ऐसा कुछ करने का एक उदाहरण।


3
log5j slf4j के समान ही log4j को बढ़ाता है
Bill Michell

यह java.util.ogging का भी दृष्टिकोण है।
पॉल

@ लॉग लॉग अक्षम होने पर यह अधिक कुशल है क्योंकि लॉग स्तर उच्च सेट है। यहां
इरिकसन

1
क्या यह लॉग 4j 2 में बदल गया है?
स्नेकडोक

3
@SnakeDoc No. यह पद्धति कॉलिंग के लिए मूलभूत है: कॉल से पहले विधि arg-सूचियों में अभिव्यक्तियों का प्रभावी रूप से मूल्यांकन किया जाता है। यदि उन भावों को a समझा जाता है कि महंगे हैं और b) केवल कुछ शर्तों के तहत चाहते हैं (जैसे कि जब डिबग सक्षम किया जाता है), तो आपकी एकमात्र पसंद कॉल के चारों ओर एक कंडीशन चेक करना है, और फ्रेमवर्क आपके लिए ऐसा नहीं कर सकता है। फ़ॉर्मेटर-आधारित लॉग विधियों के साथ बात यह है कि आप कुछ ऑब्जेक्ट (एस) (जो अनिवार्य रूप से नि: शुल्क है) पास कर सकते हैं, और लकड़हारा toString()केवल आवश्यकता होने पर कॉल करेगा ।
सुसानडब्ल्यू 14

31

चूंकि विकल्प 1 में संदेश स्ट्रिंग एक स्थिर है, लॉगिंग स्टेटमेंट को किसी शर्त के साथ लपेटने में कोई लाभ नहीं है, इसके विपरीत, यदि लॉग स्टेटमेंट डीबग सक्षम है, तो आप दो बार मूल्यांकन करेंगे, एक बार isDebugEnabled()विधि में और एक बार में debug()तरीका। आह्वान की लागत isDebugEnabled()5 से 30 नैनोसेकंड के क्रम में है जो कि अधिकांश व्यावहारिक उद्देश्यों के लिए नगण्य होनी चाहिए। इस प्रकार, विकल्प 2 वांछनीय नहीं है क्योंकि यह आपके कोड को प्रदूषित करता है और कोई अन्य लाभ प्रदान नहीं करता है।


17

isDebugEnabled()जब आप स्ट्रिंग्स को संक्षिप्त करके लॉग संदेशों का निर्माण कर रहे हैं, तो इसका उपयोग करना आरक्षित है:

Var myVar = new MyVar();
log.debug("My var is " + myVar + ", value:" + myVar.someCall());

हालाँकि, आपके उदाहरण में कोई गति नहीं है क्योंकि आप केवल एक स्ट्रिंग लॉग कर रहे हैं और इस तरह के संघटन के रूप में संचालन नहीं कर रहे हैं। इसलिए आप अपने कोड में केवल ब्लोट जोड़ रहे हैं और इसे पढ़ना कठिन बना रहे हैं।

मैं व्यक्तिगत रूप से स्ट्रिंग 1.5 वर्ग में जावा 1.5 प्रारूप कॉल का उपयोग इस तरह करता हूं:

Var myVar = new MyVar();
log.debug(String.format("My var is '%s', value: '%s'", myVar, myVar.someCall()));

मुझे संदेह है कि बहुत अनुकूलन है लेकिन इसे पढ़ना आसान है।

हालांकि ध्यान दें कि अधिकांश लॉगिंग एपीआई इस तरह के बॉक्स से बाहर स्वरूपण प्रदान करते हैं: उदाहरण के लिए slf4j निम्नलिखित प्रदान करता है:

logger.debug("My var is {}", myVar);

जिसे पढ़ना और भी आसान है।


8
आपका String.format (...) का उपयोग, लॉग लाइन को पढ़ने में आसान बनाते हुए, वास्तव में खराब तरीके से प्रदर्शन को प्रभावित कर सकता है। ऐसा करने का SLF4J तरीका पैरामीटर को logger.debug विधि में भेजता है और स्ट्रिंग के निर्माण से पहले isDebugEnabled का मूल्यांकन किया जाता है । जिस तरह से आप इसे कर रहे हैं, String.format (...) के साथ, स्ट्रिंग का निर्माण logger.debug करने के लिए विधि कॉल से पहले किया जाएगा, इसलिए आपने स्ट्रिंग बिल्डिंग के दंड का भुगतान किया होगा, भले ही डिबग स्तर हो सक्षम नहीं।
नाइट

2
String.format, concat की तुलना में 40 गुना धीमा है और slf4j में 2 पैरामेट्स की सीमा है। यहां देखें संख्याएं: stackoverflow.com/questions/925423/… मैंने कई प्रोफाइलर ग्राफ देखे हैं जहां प्रारूप ऑपरेशन डिबग स्टेटमेंट में तबाह होता है जब उत्पादन प्रणाली होती है INFO या ERROR के लॉग स्तर पर चल रहा है
AztecWarrior_25

10

जावा 8 में, आपको isDebugEnabled()प्रदर्शन को बेहतर बनाने के लिए उपयोग करने की आवश्यकता नहीं है ।

https://logging.apache.org/log4j/2.0/manual/api.html#Java_8_lambda_support_for_lazy_logging

import java.util.logging.Logger;
...
Logger.getLogger("hello").info(() -> "Hello " + name);

बहुत ही शांत। लेकिन संस्करण 2 (अभी भी अल्फा में) तक slft4j में समर्थित नहीं है :(
अमलगोविनस

8

संक्षिप्त संस्करण: आप बूलियन isDebugEnabled () चेक भी कर सकते हैं।

कारण:
1- यदि जटिल तर्क / स्ट्रिंग समापक। आपके डिबग स्टेटमेंट में जोड़ा जाता है, आपके पास पहले से ही चेक होगा।
2- आपको "जटिल" डिबग स्टेटमेंट पर चुनिंदा रूप से स्टेटमेंट को शामिल करने की आवश्यकता नहीं है। सभी बयान इस तरह से शामिल हैं।
3- कॉलिंग log.debug लॉगिंग से पहले निम्नलिखित को निष्पादित करता है:

if(repository.isDisabled(Level.DEBUG_INT))
return;

यह मूल रूप से कॉलिंग लॉग के समान है। या बिल्ली। isDebugEnabled ()।

तथापि! यह वही है जो log4j डेवलपर्स को लगता है (जैसा कि उनके javadoc में है और आपको शायद इसके द्वारा जाना चाहिए।)

यह विधि है

public
  boolean isDebugEnabled() {
     if(repository.isDisabled( Level.DEBUG_INT))
      return false;
    return Level.DEBUG.isGreaterOrEqual(this.getEffectiveLevel());
  }

यह इसके लिए javadoc है

/**
*  Check whether this category is enabled for the <code>DEBUG</code>
*  Level.
*
*  <p> This function is intended to lessen the computational cost of
*  disabled log debug statements.
*
*  <p> For some <code>cat</code> Category object, when you write,
*  <pre>
*      cat.debug("This is entry number: " + i );
*  </pre>
*
*  <p>You incur the cost constructing the message, concatenatiion in
*  this case, regardless of whether the message is logged or not.
*
*  <p>If you are worried about speed, then you should write
*  <pre>
*    if(cat.isDebugEnabled()) {
*      cat.debug("This is entry number: " + i );
*    }
*  </pre>
*
*  <p>This way you will not incur the cost of parameter
*  construction if debugging is disabled for <code>cat</code>. On
*  the other hand, if the <code>cat</code> is debug enabled, you
*  will incur the cost of evaluating whether the category is debug
*  enabled twice. Once in <code>isDebugEnabled</code> and once in
*  the <code>debug</code>.  This is an insignificant overhead
*  since evaluating a category takes about 1%% of the time it
*  takes to actually log.
*
*  @return boolean - <code>true</code> if this category is debug
*  enabled, <code>false</code> otherwise.
*   */

1
JavaDoc को शामिल करने के लिए धन्यवाद। मुझे पता था कि मैंने यह सलाह पहले कहीं देखी थी और एक निश्चित संदर्भ खोजने की कोशिश कर रहा था। यह, यदि निश्चित नहीं है, तो कम से कम बहुत अच्छी तरह से सूचित किया गया है।
साइमन पीटर चैपल

7

जैसा कि दूसरों ने गार्ड स्टेटमेंट का उपयोग करते हुए उल्लेख किया है कि अगर स्ट्रिंग बनाना एक समय लेने वाली कॉल है तो वास्तव में केवल उपयोगी है। इसके विशिष्ट उदाहरण हैं जब स्ट्रिंग का निर्माण कुछ आलसी लोडिंग को ट्रिगर करेगा।

यह ध्यान देने योग्य है कि जावा या (SLF4J) - http://www.slf4j.org/manual.html के लिए सरल लॉगिंग फ़ेसडे का उपयोग करके इस समस्या से बचा जा सकता है । इससे विधि कॉल जैसे:

logger.debug("Temperature set to {}. Old temperature was {}.", t, oldT);

यदि डिबग सक्षम है, तो यह केवल पैरामीटर को स्ट्रिंग्स में परिवर्तित कर देगा। SLF4J जैसा कि इसके नाम से पता चलता है कि यह केवल एक मुखौटा है और लॉगिंग कॉल लॉग 4 जे को पारित किया जा सकता है।

आप बहुत आसानी से "अपना खुद का रोल" भी कर सकते हैं।

उम्मीद है की यह मदद करेगा।


6

विकल्प 2 बेहतर है।

प्रति से यह प्रदर्शन में सुधार नहीं करता है। लेकिन यह सुनिश्चित करता है कि प्रदर्शन में गिरावट न हो। ऐसे।

आम तौर पर हम logger.debug (someString) की अपेक्षा करते हैं;

लेकिन आमतौर पर, जैसे-जैसे एप्लिकेशन बढ़ता है, कई हाथों को बदलता है, एस्प नौसिव डेवलपर्स, आप देख सकते हैं

logger.debug (str1 + str2 + str3 + str4);

और जैसे।

भले ही लॉग स्तर ERROR या FATAL के लिए सेट किया गया हो, स्ट्रिंग्स का संयोजन होता है! यदि अनुप्रयोग में स्ट्रिंग कॉन्बनेनेशन के साथ बहुत सारे DEBUG स्तर के संदेश हैं, तो यह निश्चित रूप से विशेष रूप से jdk 1.4 या नीचे के साथ एक प्रदर्शन हिट लेता है। (Iam सुनिश्चित नहीं है कि बाद में jdk internall के संस्करण किसी भी stringbuffer.append ()) करते हैं।

विकल्प 2 सुरक्षित क्यों है। यहां तक ​​कि स्ट्रिंग संघनन भी नहीं होता है।


3

@Erickson की तरह यह निर्भर करता है। अगर मुझे याद है, isDebugEnabledपहले से ही debug()Log4j की विधि में निर्मित है।
जब तक आप अपने डिबग स्टेटमेंट्स में कुछ महंगी संगणनाएँ नहीं कर रहे हैं, जैसे ऑब्जेक्ट्स पर लूप, कंप्यूटर्स और कॉन्टेनेट स्ट्रिंग्स करना, आप मेरी राय में ठीक हैं।

StringBuilder buffer = new StringBuilder();
for(Object o : myHugeCollection){
  buffer.append(o.getName()).append(":");
  buffer.append(o.getResultFromExpensiveComputation()).append(",");
}
log.debug(buffer.toString());

के रूप में बेहतर होगा

if (log.isDebugEnabled(){
  StringBuilder buffer = new StringBuilder();
  for(Object o : myHugeCollection){
    buffer.append(o.getName()).append(":");
    buffer.append(o.getResultFromExpensiveComputation()).append(",");
  }
  log.debug(buffer.toString());
}

3

एक के लिए एक पंक्ति , मैं संदेश लॉग इन करने के लिए इस तरह से मैं संयोजन नहीं करते में, के एक त्रिगुट के अंदर का उपयोग करें:

ईजे:

logger.debug(str1 + str2 + str3 + str4);

मैं करता हूँ:

logger.debug(logger.isDebugEnable()?str1 + str2 + str3 + str4:null);

लेकिन कोड की कई लाइनों के लिए

ईजे।

for(Message mess:list) {
    logger.debug("mess:" + mess.getText());
}

मैं करता हूँ:

if(logger.isDebugEnable()) {
    for(Message mess:list) {
         logger.debug("mess:" + mess.getText());
    }
}

3

चूंकि बहुत से लोग log4j2 की खोज करते समय संभवतः इस उत्तर को देख रहे हैं और लगभग सभी वर्तमान उत्तर log4j2 या उसमें हाल के बदलावों पर विचार नहीं करते हैं, इसलिए इस प्रश्न का उत्तर उम्मीद से देना चाहिए।

log4j2 आपूर्तिकर्ता s का समर्थन करता है (वर्तमान में उनके स्वयं के कार्यान्वयन, लेकिन प्रलेखन के अनुसार यह संस्करण 3.0 में जावा के आपूर्तिकर्ता इंटरफ़ेस का उपयोग करने की योजना है)। आप मैनुअल में इसके बारे में थोड़ा और पढ़ सकते हैं । यह आपको एक लॉग में महंगी लॉग संदेश निर्माण करने की अनुमति देता है जो केवल संदेश बनाता है यदि यह लॉग होने जा रहा है:

LogManager.getLogger().debug(() -> createExpensiveLogMessage());

2

यह गति में सुधार करता है क्योंकि यह डीबग पाठ में तार को समतल करने के लिए आम है जो महंगा है जैसे:

boolean debugEnabled = logger.isDebugEnabled();
if (debugEnabled) {
    logger.debug("some debug text" + someState);
}

1
अगर हम jdk 1.5 और उसके बाद का उपयोग कर रहे हैं, तो मुझे लगता है कि इसलिए concatenate स्ट्रिंग्स से कोई फर्क नहीं पड़ेगा।
साइलेंट वारियर

कैसे? JDK5 अलग तरीके से क्या करेगा?
6

1
Jdk 1.5 में अगर हम सिंगल स्टेटमेंट में स्ट्रैंथ को बदलते हैं तो आंतरिक रूप से यह StringBuffer.append () मेथड का ही उपयोग करता है। इसलिए यह प्रदर्शन पर असर नहीं डालता है।
साइलेंट वारियर

2
स्ट्रिंग संघनन निस्संदेह समय लगता है। हालांकि मुझे यकीन नहीं है कि मैं इसे 'महंगा' बताऊंगा। उपरोक्त उदाहरण में कितना समय बचा है? आसपास की तुलना वास्तव में क्या कर रही है? (उदाहरण के लिए डेटाबेस पढ़ता है या स्मृति में गणना करता है)। मुझे लगता है कि इस प्रकार के कथनों को योग्य बनाने की आवश्यकता है
ब्रायन एग्न्यू

1
यहां तक ​​कि JDK 1.4 सरल स्ट्रिंग संघनन के साथ नई स्ट्रिंग ऑब्जेक्ट नहीं बनाएगा। प्रदर्शन दंड StringBuffer.append () का उपयोग करने से आता है जब कोई स्ट्रिंग बिल्कुल भी प्रदर्शित नहीं होनी चाहिए।
javashlook

1

चूंकि Log4j संस्करण 2.4(या slf4j-api 2.0.0-alpha1) धाराप्रवाह एपीआई (या आलसी लॉगिंग के लिए जावा 8 लैम्ब्डा समर्थन ) का उपयोग करना बेहतर है , Supplier<?>लॉग संदेश तर्क के लिए समर्थन , जो लैम्ब्डा द्वारा दिया जा सकता है :

log.debug("Debug message with expensive data : {}", 
           () -> doExpensiveCalculation());

या slf4j एपीआई के साथ:

log.atDebug()
            .addArgument(() -> doExpensiveCalculation())
            .log("Debug message with expensive data : {}");

0

यदि आप विकल्प 2 का उपयोग करते हैं तो आप बूलियन चेक कर रहे हैं जो कि तेज है। विकल्प एक में आप एक विधि कॉल कर रहे हैं (स्टैक पर सामान को धक्का दे रहे हैं) और फिर एक बूलियन चेक कर रहे हैं जो अभी भी तेज है। मुझे जो समस्या दिख रही है वह है संगति। यदि आपके कुछ डिबग और सूचना स्टेटमेंट्स लिपटे हुए हैं और कुछ हैं तो यह एक सुसंगत कोड शैली नहीं है। इसके अलावा बाद में कोई व्यक्ति डिबग स्टेटमेंट को बदल सकता है ताकि कॉन्टेनेट स्ट्रिंग्स को शामिल किया जा सके, जो अभी भी बहुत तेज है। मैंने पाया कि जब हमने एक बड़े एप्लिकेशन में डिबग और सूचना स्टेटमेंट को लपेटा और इसे प्रोफाइल किया तो हमने प्रदर्शन में कुछ प्रतिशत अंक बचाए। ज्यादा नहीं, लेकिन यह काम करने लायक बनाने के लिए काफी है। अब मेरे पास IntelliJ में मैक्रोज़ सेटअप का एक जोड़ा है जो मेरे लिए स्वचालित रूप से लिपटे डिबग और जानकारी स्टेटमेंट उत्पन्न करता है।


0

मैं विकल्प 2 का उपयोग करने की सिफारिश करूंगा क्योंकि यह वास्तव में सुपर महंगा नहीं है।

केस 1: log.debug ("एक स्ट्रिंग")

Case2: log.debug ("एक स्ट्रिंग" + "दो स्ट्रिंग" + ऑब्जेक्ट ।toString + object2.toString)

उस समय इनमें से किसी को भी बुलाया जाता है, log.debug (यह मामला 1 या Case2 हो) के भीतर पैरामीटर स्ट्रिंग का मूल्यांकन किया जाना है। यही सब 'महंगा' से मतलब है। यदि आपके पास इससे पहले की स्थिति है, तो'DebugEnabled () ', इनका मूल्यांकन नहीं किया जाना चाहिए, जहां प्रदर्शन सहेजा गया है।


0

2.x के रूप में, Apache Log4j में यह चेक इन बिल्ट है, इसलिए isDebugEnabled()अब आवश्यक नहीं है। बस एक कर debug()और यदि सक्षम नहीं हो गए संदेशों दबा दिया जाएगा।


-1

Log4j2 आपको एक संदेश टेम्प्लेट में मापदंडों को प्रारूपित करने देता है, इसी तरह String.format(), ऐसा करने की आवश्यकता के साथ दूर करना isDebugEnabled()

Logger log = LogManager.getFormatterLogger(getClass());
log.debug("Some message [myField=%s]", myField);

नमूना सरल log4j2.properties:

filter.threshold.type = ThresholdFilter
filter.threshold.level = debug
appender.console.type = Console
appender.console.name = STDOUT
appender.console.layout.type = PatternLayout
appender.console.layout.pattern = %d %-5p: %c - %m%n
appender.console.filter.threshold.type = ThresholdFilter
appender.console.filter.threshold.level = debug
rootLogger.level = info
rootLogger.appenderRef.stdout.ref = STDOUT
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.