2011-05-03 6 views
8

मैं लॉगिंग मॉड्यूल को एक मजेदार तरीके से व्यवहार कर रहा हूं। क्या मैं कुछ भूल रहा हूँ ?अजीब: लॉगर केवल अपवादों के लिए पहले हैंडलर के फॉर्मेटर का उपयोग करता है

मैं दो हैंडलर रखने की सामान्य बात कर रहा हूं: केवल स्ट्रीमिंग के लिए स्ट्रीमहैंडर और कंसोल के लिए उच्च, और एक फाइलहैंडलर जो सभी डीईबीयूजी जानकारी को भी संभाल लेगा।

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

कंसोल हैंडलर और फ़ाइल हैंडलर दोनों का अपना फॉर्मेटर है। कोड में प्रिंट स्टेटमेंट इसे साबित करता है। हालांकि, logger.exception के लिए कॉल केवल पहले हैंडलर के प्रारूप अपवाद का उपयोग करेगा => फ़ाइल में अपवाद लॉग इन होना चाहिए जिसमें प्रारूप में होना चाहिए। Logger.addHandler लाइनों का क्रम बदलें और फिर यह फ़ाइल हैंडलर का प्रारूप अपवाद है जिसका हर जगह प्रयोग किया जाता है।

import logging 
import sys 

class ConsoleFormatter(logging.Formatter): 
    def formatException(self, exc_info): 
     # Ugly but obvious way to see who's talking. 
     return "CONSOLE EXCEPTION %s: %s" % exc_info[:2] 

def setup(path): 
    logger = logging.getLogger() 
    # 
    file_handler = logging.FileHandler(path, mode='w') 
    if __debug__: 
     file_handler.setLevel(logging.DEBUG) 
    else: 
     file_handler.setLevel(logging.INFO) 
    formatter = logging.Formatter("%(asctime)s %(levelname)-8s " 
            "%(name)-16s %(message)s " 
            "[%(filename)[email protected]%(lineno)d in %(funcName)s]") 
    file_handler.setFormatter(formatter) 

    # 
    console_handler = logging.StreamHandler(sys.stderr) 
    console_handler.setLevel(logging.INFO) 
    console_formatter = ConsoleFormatter("%(levelname)-8s - %(message)s") 
    console_handler.setFormatter(console_formatter) 

    # >>> FUN HAPPENS HERE <<< 
    # Only the formatter of the first handler is used ! Both on the console 
    # and in the file. Change the order of these two lines to see. 
    logger.addHandler(console_handler) 
    logger.addHandler(file_handler) 
    # 
    # Proof that the two handlers have different formatters: 
    print logger.handlers 
    print file_handler.formatter.formatException 
    print console_formatter.formatException 
    # 
    logger.setLevel(logging.DEBUG) 
    logger.info("Logger ready.") 

setup('test.log') 
logger = logging.getLogger() 
logger.debug("Only visible in the file.") 
try: 
    1/0 
except ZeroDivisionError: 
    logger.exception("boom") 

क्या चल रहा है?

संपादित करें: मैं रास्ते में पायथन 2.6 का उपयोग कर रहा हूं। संपादित करें: "console_formatter" चर नाम के बारे में कोड में टाइपो सही किया गया।

Traceback (most recent call last): 
    File "logger.py", line 42, in <module> 
    setup('test.log') 
    File "logger.py", line 37, in setup 
    print console_formatter.formatException 
NameError: global name 'console_formatter' is not defined 

शायद मुद्दे का स्रोत है कि:

उत्तर

5

मैं आपकी समस्या को मिला: जब मैं console_handler कोड बदल प्रारूपों सही थे! आप Formatter.format के लिए स्रोत कोड को logger/__init__.py में देखें, तो लाइन 440 (py2.6 के लिए) पर आप निम्नलिखित देखेंगे:

 if record.exc_info: 
      # Cache the traceback text to avoid converting it multiple times 
      # (it's constant anyway) 
      if not record.exc_text: 
       record.exc_text = self.formatException(record.exc_info) 

यह जब से तुम formatException अधिभावी रहे हैं आपके मामले में सच नहीं है। यदि आप if not record.exc_text (और तदनुसार इंडेंटेशन को ठीक करते हैं) पर टिप्पणी करते हैं, तो ऐसा लगता है कि यह अपेक्षित काम करता है।

बग पहले से ही यहाँ रिपोर्ट किया जा रहा है: http://bugs.python.org/issue6435

+0

बढ़िया! लॉगिंग subclassing द्वारा। फॉर्मेटर मैं प्रारूप() को ओवरराइड कर सकता था। यह ओवरडिन विधि सुपरक्लास 'प्रारूप() को कॉल करने से पहले किसी को भी record.exc_text सेट नहीं करती है और इसके बाद इसके पिछले मान को पुनर्स्थापित करती है। इस तरह, मैं किसी भी कोड को डुप्लिकेट किए बिना अपवाद टेक्स्ट की कैशिंग अक्षम करता हूं। फिर, मैं अपने दो हैंडलरों के लिए बेस कैस के रूप में इस कैशलेसफॉर्मेटर का उपयोग करता हूं और यह \ o /। धन्यवाद ! – Niriel

1

पहली बार मैं अपने कोड भाग गया, मैं एक ट्रैस बैक मिला है।

console_handler = logging.StreamHandler(sys.stderr) 
console_handler.setLevel(logging.INFO) 
console_formatter = ConsoleFormatter("%(levelname)-8s - %(message)s") 
console_handler.setFormatter(console_formatter) 
+0

यह मेरे सिस्टम पर कम से कम असंबंधित है। 'console_formatter' एक टाइपो था, लेकिन मैंने इसे ठीक करने के बाद, दो 'एडहैंडलर' लाइनों के क्रम को बदलते हुए संकेत दिया है कि आप अभी भी stderr पर लॉगिंग प्रारूप को बदलते हैं। –

+0

हां, टाइपो के लिए खेद है! वास्तव में, मैंने इस पोस्ट में अपना कोड आयात करते समय सबकुछ सही नहीं किया। मुझे खुशी है कि आप समस्या को पुन: उत्पन्न कर सकते हैं। आप किस अजगर का उपयोग करते हैं? – Niriel

+0

मैंने 2.6 और 2.7 (मेरी प्रणाली डिफ़ॉल्ट) दोनों में समस्या का पुनरुत्पादन किया। –

8

इस कोड को मैं के साथ आया है। यह नौकरी करता है :)।

class CachelessFormatter(logging.Formatter): 
    # I came up with that after reading the answers to 
    #  http://stackoverflow.com/questions/5875225/ 
    # which pointed me to 
    #  http://bugs.python.org/issue6435 
    # I still think Vinay Sajip has a bit of an attitude :p. 
    def format(self, record): 
     # Disable the caching of the exception text. 
     backup = record.exc_text 
     record.exc_text = None 
     s = logging.Formatter.format(self, record) 
     record.exc_test = backup 
     return s 

class ConsoleFormatter(CachelessFormatter): 
    def formatException(self, exc_info): 
     return "   %s: %s" % exc_info[:2] 

def setup(path): 
    file_handler = logging.FileHandler(path, mode='w') 
    file_handler.setLevel(logging.DEBUG) 
    formatter = CachelessFormatter("%(asctime)s %(levelname)-8s " 
            "%(name)-16s %(message)s " 
            "[%(filename)[email protected]%(lineno)d in %(funcName)s]") 
    file_handler.setFormatter(formatter) 

    console_handler = logging.StreamHandler(sys.stderr) 
    console_handler.setLevel(logging.INFO) 
    formatter = ConsoleFormatter("%(levelname)-8s - %(message)s") 
    console_handler.setFormatter(formatter) 

    logger = logging.getLogger() 
    logger.addHandler(file_handler) 
    logger.addHandler(console_handler) 
    if __debug__: 
     logger.setLevel(logging.DEBUG) 
    else: 
     logger.setLevel(logging.INFO) 
    logger.info("Logger ready.") 

if __name__ == '__main__': 
    setup('test.log') 
    logger = logging.getLogger() 
    logger.debug("Only shows in the file") 
    try: 
     1/0 
    except ZeroDivisionError: 
     pass 
    logger.exception("boom") 
संबंधित मुद्दे