2010-11-10 21 views
50

मैं अपने अजगर आवेदन में मानक अजगर लॉगिंग मॉड्यूल का उपयोग कर रहा:लेज़ी लकड़हारा संदेश स्ट्रिंग मूल्यांकन

 
import logging 
logging.basicConfig(level=logging.INFO) 
logger = logging.getLogger("log") 
while True: 
    logger.debug('Stupid log message " + ' '.join([str(i) for i in range(20)])) 
    # Do something 

मुद्दा है कि हालांकि डिबग स्तर सक्षम नहीं है, कि बेवकूफ लॉग संदेश प्रत्येक पाश यात्रा पर मूल्यांकन किया जाता है, जो प्रदर्शन को खराब तरीके से नुकसान पहुंचाता है।

क्या इसके लिए कोई समाधान है?

C++ में हम log4cxx पैकेज है कि इस तरह मैक्रो प्रदान करता है:
LOG4CXX_DEBUG(logger, messasage)
प्रभावी रूप से

 
if (log4cxx::debugEnabled(logger)) { 
    log4cxx.log(logger,log4cxx::LOG4CXX_DEBUG, message) 
} 

का आकलन करती लेकिन उस के बाद से वहाँ (AFAIK), अगर वहाँ एक कारगर तरीका अजगर में कोई मैक्रो नहीं है लॉगिंग करने के लिए?

उत्तर

61

लॉगिंग मॉड्यूल के पास पहले से ही आंशिक समर्थन है जो आप करना चाहते हैं। यह करें:

log.debug("Some message: a=%s b=%s", a, b) 

... बजाय इस की:

log.debug("Some message: a=%s b=%s" % (a, b)) 

लॉगिंग मॉड्यूल बहुत चालाक पूरा लॉग संदेश का उत्पादन नहीं है जब तक कि संदेश वास्तव में कहीं लॉग होता है।

इस सुविधा को अपने विशिष्ट अनुरोध पर लागू करने के लिए, आप एक आलसीजॉइन क्लास बना सकते हैं।

class lazyjoin: 
    def __init__(self, s, items): 
     self.s = s 
     self.items = items 
    def __str__(self): 
     return self.s.join(self.items) 

इस तरह इसका इस्तेमाल (एक जनरेटर अभिव्यक्ति के उपयोग पर ध्यान दें, आलस्य को जोड़ने):

logger.info('Stupid log message %s', lazyjoin(' ', (str(i) for i in range(20)))) 

यहाँ एक डेमो से पता चलता है कि इस काम करता है।

>>> import logging 
>>> logging.basicConfig(level=logging.INFO) 
>>> logger = logging.getLogger("log") 
>>> class DoNotStr: 
...  def __str__(self): 
...   raise AssertionError("the code should not have called this") 
... 
>>> logger.info('Message %s', DoNotStr()) 
Traceback (most recent call last): 
... 
AssertionError: the code should not have called this 
>>> logger.debug('Message %s', DoNotStr()) 
>>> 

डेमो में, logger.info() कॉल, अभिकथन त्रुटि मारा जबकि logger.debug() है कि अब तक नहीं मिला।

20
import logging 
import time 

logging.basicConfig(level=logging.INFO) 
logger = logging.getLogger("log") 

class Lazy(object): 
    def __init__(self,func): 
     self.func=func 
    def __str__(self): 
     return self.func() 

logger.debug(Lazy(lambda: time.sleep(20))) 

logger.info(Lazy(lambda: "Stupid log message " + ' '.join([str(i) for i in range(20)]))) 
# INFO:log:Stupid log message 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 

ट चला, तो आपको पहले logger.debug आदेश 20 सेकंड नहीं ले करता है पर अमल करने पर ध्यान देंगे। यह दिखाता है कि लॉगिंग स्तर सेट स्तर से नीचे होने पर तर्क का मूल्यांकन नहीं किया जाता है।

9

शेन के रूप में बताते हैं, ...

log.debug("Some message: a=%s b=%s", a, b) 

उपयोग करने के बजाय इस बात का:

log.debug("Some message: a=%s b=%s" % (a, b)) 

केवल स्ट्रिंग स्वरूपण यदि संदेश वास्तव में लॉग होता है प्रदर्शन से कुछ समय बचाता है।

यह पूरी तरह से, समस्या का समाधान नहीं करता, हालांकि के रूप में आप पहले से प्रक्रिया जैसे मूल्यों स्ट्रिंग में स्वरूपित करने के लिए, हो सकता है:

log.debug("Some message: a=%s b=%s", foo.get_a(), foo.get_b()) 

उस मामले में, obj.get_a() और obj.get_b() अभिकलन किया जाएगा यहां तक ​​कि यदि कोई लॉगिंग नहीं होती है।

कि एक समाधान लैम्ब्डा कार्यों का उपयोग करने के लिए होगा, लेकिन यह कुछ अतिरिक्त मशीनरी की आवश्यकता है:

class lazy_log_debug(object): 
    def __init__(self, func): 
     self.func = func 
     logging.debug("%s", self) 
    def __str__(self): 
     return self.func() 

... तो आप निम्नलिखित के साथ प्रवेश कर सकते हैं:

lazy_log_debug(lambda: "Some message: a=%s b=%s" % (foo.get_a(), foo.get_b())) 

उस मामले में , लैम्ब्डा फ़ंक्शन केवल कहा जाएगा यदि log.debug स्वरूपण करने का निर्णय लेता है, इसलिए __str__ विधि को कॉल करना।

आपको याद है: उस समाधान का ओवरहेड लाभ से बहुत अधिक हो सकता है :-) लेकिन कम से कम सिद्धांत में, यह पूरी तरह से आलसी लॉगिंग करना संभव बनाता है।

26
बेशक

पीछा कर रहा है नहीं एक मैक्रो के रूप में के रूप में कुशल:

if logger.isEnabledFor(logging.DEBUG): 
    logger.debug(
     'Stupid log message ' + ' '.join([str(i) for i in range(20)]) 
    ) 

लेकिन सरल, evaluates in lazy fashion और है 4 गुना तेजी से स्वीकार किए जाते हैं जवाब से:

class lazyjoin: 
    def __init__(self, s, items): 
     self.s = s 
     self.items = items 

    def __str__(self): 
     return self.s.join(self.items) 

logger.debug(
    'Stupid log message %s', lazyjoin(' ', (str(i) for i in range(20))) 
) 

के लिए benchmark-src देखें मेरी सेट अप।

+1

सरल और कुशल। मुझे वह पसंद है। यह अधिक upvotes कमाने चाहिए। – Rockallite

+0

दक्षता इस मामले पर निर्भर करती है और आपको हमेशा अपने परिदृश्य को बेंचमार्क करना चाहिए। मेरे मामले में, आलसी लॉगिंग को किसी भी तर्क की आवश्यकता नहीं थी, लेकिन '__str__' का आह्वान करने पर कक्षा से सामान एकत्र कर सकता था। तो अनिवार्य रूप से, मुझे लगभग एक ही परिणाम मिला। मेरी टिप्पणी देखें [यहां] (https://gist.github.com/schnittstabil/9372911#gistcomment-1910973) – guyarad

+0

@ गुयाराड: आपने खाते में 'lazyjoin' उदाहरण बनाने के लिए आवश्यक समय भी लिया है। [पायथन: आलसी डीबग लॉगिंग कैसे करें] के लिए मेरा जवाब भी देखें (http://stackoverflow.com/questions/21377020/python-how-to-do-lazy-debug-logging/22204021#22204021)। – schnittstabil

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