ट्रेसबैक के साथ अपवाद लॉग करें


152

मैं अपनी पायथन त्रुटियों को कैसे लॉग कर सकता हूं?

try:
    do_something()
except:
    # How can I log my exception here, complete with its traceback?

जवाबों:


203

ट्रेस जानकारी के साथ वर्तमान अपवाद को लॉग करने के logging.exceptionलिए except:हैंडलर / ब्लॉक के भीतर से उपयोग करें , संदेश के साथ प्रस्तुत किया गया।

import logging
LOG_FILENAME = '/tmp/logging_example.out'
logging.basicConfig(filename=LOG_FILENAME, level=logging.DEBUG)

logging.debug('This message should go to the log file')

try:
    run_my_stuff()
except:
    logging.exception('Got exception on main handler')
    raise

अब लॉग फाइल देख रहे हैं /tmp/logging_example.out:

DEBUG:root:This message should go to the log file
ERROR:root:Got exception on main handler
Traceback (most recent call last):
  File "/tmp/teste.py", line 9, in <module>
    run_my_stuff()
NameError: name 'run_my_stuff' is not defined

1
इसके लिए django कोड देखें, और मैं यह मान रहा हूं कि इसका उत्तर नहीं है, लेकिन क्या ट्रेसबैक को वर्णों या गहराई की एक निश्चित राशि तक सीमित करने का कोई तरीका है? समस्या यह है कि बड़े ट्रेसबैक के लिए, इसमें बहुत लंबा समय लगता है।
एडुआर्ड लुका

10
ध्यान दें कि यदि आप एक लकड़हारे को परिभाषित करते हैं तो आपको यह काम करने के logger = logging.getLogger('yourlogger')लिए लिखना logger.exception('...')होगा ...
576i

क्या हम इसे संशोधित कर सकते हैं ताकि संदेश लॉग स्तर INFO के साथ मुद्रित हो?
NM

ध्यान दें कि कुछ बाहरी ऐप्स, जैसे कि Azure अंतर्दृष्टि, के लिए ट्रैकबैक लॉग्स में संग्रहीत नहीं है। फिर नीचे दिखाए गए संदेश स्ट्रिंग पर उन्हें स्पष्ट रूप से पास करना आवश्यक है।
एडगर एच।

138

उपयोग के exc_infoविकल्प बेहतर हो सकते हैं, चेतावनी या त्रुटि शीर्षक रहता है:

try:
    # coode in here
except Exception as e:
    logging.error(e, exc_info=True)

मैं कभी याद नहीं कर सकता कि exc_info=कर्ग को क्या कहा जाता है; धन्यवाद!
बर्टो

4
यह logging.exception के समान है, इस अपवाद के साथ कि प्रकार को दो बार अनावश्यक रूप से लॉग किया गया है। जब तक आप त्रुटि के अलावा कोई अन्य स्तर नहीं चाहते हैं, तब तक बस लॉगिंग का उपयोग करें।
विरमवुड

@Wyrmwood यह समान नहीं है के रूप में आप के लिए एक संदेश की आपूर्ति करने के लिए हैlogging.exception
पीटर लकड़ी

57

मेरा काम हाल ही में हमारे आवेदन से सभी ट्रेसबैक / अपवाद लॉगिंग के साथ मुझे सौंपा। मैंने ऐसी कई तकनीकें आजमाईं जो दूसरों ने ऑनलाइन पोस्ट की थीं जैसे कि ऊपर एक लेकिन एक अलग दृष्टिकोण पर बस गया। ओवरराइडिंग traceback.print_exception

मुझे http://www.bbarrows.com/ पर लिखना है यह पढ़ना बहुत आसान होगा, लेकिन इल इसमें भी पेस्ट करें।

जब सभी अपवादों को लॉग करने के साथ काम किया जाता है, तो हमारे सॉफ्टवेयर का जंगली में सामना हो सकता है मैंने अपने अजगर अपवाद ट्रेसबैक को लॉग करने के लिए कई विभिन्न तकनीकों की कोशिश की। पहले मैंने सोचा था कि लॉगऑन कोड डालने के लिए अजगर सिस्टम अपवाद हुक, sys.excepthook सही जगह होगा। मैं कुछ इसी तरह की कोशिश कर रहा था:

import traceback
import StringIO
import logging
import os, sys

def my_excepthook(excType, excValue, traceback, logger=logger):
    logger.error("Logging an uncaught exception",
                 exc_info=(excType, excValue, traceback))

sys.excepthook = my_excepthook  

इसने मुख्य सूत्र के लिए काम किया लेकिन मुझे जल्द ही पता चला कि मेरी sys.excepthook मेरी प्रक्रिया शुरू होने वाले किसी भी नए सूत्र में मौजूद नहीं होगी। यह एक बहुत बड़ा मुद्दा है क्योंकि इस परियोजना में ज्यादातर सब कुछ थ्रेड में होता है।

गुग्लिंग और बहुत सारे दस्तावेज़ पढ़ने के बाद मुझे जो सबसे उपयोगी जानकारी मिली वह पायथन इश्यू ट्रैकर की थी।

थ्रेड पर पहला पोस्ट sys.excepthookथ्रेड्स के पार नहीं जारी ( उदाहरण के रूप में नीचे दिखाया गया है) का एक कार्यशील उदाहरण दिखाता है । स्पष्ट रूप से यह अपेक्षित व्यवहार है।

import sys, threading

def log_exception(*args):
    print 'got exception %s' % (args,)
sys.excepthook = log_exception

def foo():
    a = 1 / 0

threading.Thread(target=foo).start()

इस पायथन इश्यू थ्रेड पर संदेश वास्तव में 2 सुझाए गए हैक्स में परिणाम करते हैं। Threadब्लॉक को छोड़कर हमारी अपनी कोशिश में रनक्लैट को या तो सबक्लास में लपेटें और अपवादों को पकड़ने या लॉग इन करने के लिए या threading.Thread.runब्लॉक को छोड़कर अपनी खुद की कोशिश में लॉग इन करें और अपवादों को लॉग इन करें।

सबक्लासिंग की पहली विधि Threadमुझे अपने कोड में कम सुरुचिपूर्ण लगती है क्योंकि आपको अपनी कस्टम Threadक्लास EVERYWHERE का आयात और उपयोग करना होगा जिसे आप लॉगिंग थ्रेड करना चाहते थे। यह एक परेशानी का कारण बना क्योंकि मुझे अपना पूरा कोड आधार खोजना पड़ा और Threadsइस रिवाज के साथ सभी सामान्य को बदलना पड़ा Thread। हालांकि, यह स्पष्ट था कि यह क्या Threadकर रहा था और किसी के लिए निदान करने और डिबग करना आसान होगा यदि कस्टम लॉग कोड के साथ कुछ गलत हुआ। एक छोटा लॉगिंग थ्रेड इस तरह दिख सकता है:

class TracebackLoggingThread(threading.Thread):
    def run(self):
        try:
            super(TracebackLoggingThread, self).run()
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception, e:
            logger = logging.getLogger('')
            logger.exception("Logging an uncaught exception")

मंकी पैचिंग threading.Thread.runका दूसरा तरीका अच्छा है क्योंकि मैं इसे एक बार ही सही पर चला सकता हूं __main__और सभी अपवादों में अपना लॉगिंग कोड लिख सकता हूं। बंदर पैचिंग डिबग करने के लिए कष्टप्रद हो सकता है, क्योंकि यह किसी चीज़ की अपेक्षित कार्यक्षमता को बदल देता है। पायथन इश्यू ट्रैकर से सुझाया गया पैच था:

def installThreadExcepthook():
    """
    Workaround for sys.excepthook thread bug
    From
http://spyced.blogspot.com/2007/06/workaround-for-sysexcepthook-bug.html

(https://sourceforge.net/tracker/?func=detail&atid=105470&aid=1230540&group_id=5470).
    Call once from __main__ before creating any threads.
    If using psyco, call psyco.cannotcompile(threading.Thread.run)
    since this replaces a new-style class method.
    """
    init_old = threading.Thread.__init__
    def init(self, *args, **kwargs):
        init_old(self, *args, **kwargs)
        run_old = self.run
        def run_with_except_hook(*args, **kw):
            try:
                run_old(*args, **kw)
            except (KeyboardInterrupt, SystemExit):
                raise
            except:
                sys.excepthook(*sys.exc_info())
        self.run = run_with_except_hook
    threading.Thread.__init__ = init

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

परीक्षण करने के लिए मैंने एक रखा था

raise Exception("Test")

मेरे कोड में कहीं। हालाँकि, इस पद्धति को लपेटने वाली विधि को ब्लॉक करने के अलावा ट्रेसबैक को प्रिंट करने और अपवाद को निगलने के अलावा एक कोशिश थी। यह बहुत निराशाजनक था क्योंकि मैंने ट्रेसबैक को एसटीडीयूएस के लिए मुद्रित करके देखा, लेकिन लॉग नहीं किया जा रहा था। यह तब मैंने तय किया था कि ट्रेसबैक को लॉग करने का एक बहुत आसान तरीका सिर्फ बंदर का तरीका था जो कि सभी अजगर कोड ट्रेसबैक को प्रिंट करने के लिए उपयोग करता है, ट्रेसबैक।प्रिंट_एक्ससेप्शन। मैं निम्नलिखित के समान कुछ के साथ समाप्त हुआ:

def add_custom_print_exception():
    old_print_exception = traceback.print_exception
    def custom_print_exception(etype, value, tb, limit=None, file=None):
        tb_output = StringIO.StringIO()
        traceback.print_tb(tb, limit, tb_output)
        logger = logging.getLogger('customLogger')
        logger.error(tb_output.getvalue())
        tb_output.close()
        old_print_exception(etype, value, tb, limit=None, file=None)
    traceback.print_exception = custom_print_exception

यह कोड एक स्ट्रिंग बफ़र के लिए ट्रेसबैक लिखता है और लॉगिंग ईआरआरओआर में प्रवेश करता है। मेरे पास एक कस्टम लॉगिंग हैंडलर है जो 'customLogger' लॉगर सेट करता है जो ERROR लेवल लॉग लेता है और उन्हें विश्लेषण के लिए घर भेजता है।


2
काफी दिलचस्प तरीका। एक प्रश्न - add_custom_print_exceptionआप जिस साइट से जुड़े हुए हैं, उस पर प्रतीत नहीं होता है और इसके बजाय वहाँ कुछ अलग अंतिम कोड है। आप किसको बेहतर / अधिक अंतिम कहेंगे और क्यों? धन्यवाद!
काल्पनिक जूल

धन्यवाद, शानदार जवाब!
101

एक कट और पेस्ट टाइपो है। Old_print_exception, सीमा और फ़ाइल के लिए प्रत्यायोजित कॉल पर सीमा और फ़ाइल पारित की जानी चाहिए, न कि कोई भी - old_print_exception (etype, value, tb, limit, file)
मार्विन

अपने अंतिम कोड ब्लॉक के लिए, एक स्ट्रिंग को आरंभ करने और इसके अपवाद को मुद्रित करने के बजाय, आप केवल logger.error(traceback.format_tb())(या format_exc) कॉल कर सकते हैं (यदि आप अपवाद जानकारी भी चाहते हैं)।
जेम्स

8

आप मुख्य थ्रेड पर सभी अप्रकाशित अपवादों को एक हैंडलर को सौंपकर लॉग इन कर सकते हैं sys.excepthook, शायद exc_infoपायथन के लॉगिंग मानकों के पैरामीटर का उपयोग करते हुए :

import sys
import logging

logging.basicConfig(filename='/tmp/foobar.log')

def exception_hook(exc_type, exc_value, exc_traceback):
    logging.error(
        "Uncaught exception",
        exc_info=(exc_type, exc_value, exc_traceback)
    )

sys.excepthook = exception_hook

raise Exception('Boom')

यदि आपका प्रोग्राम थ्रेड्स का उपयोग करता है, हालाँकि, तो ध्यान दें कि उपयोग किए गए थ्रेड ट्रिगर नहींthreading.Thread होंगे जब उनके अंदर एक अनकहा अपवाद उत्पन्न होता है, जैसा कि पायथन के इश्यू ट्रैकर पर अंक 1230540 में उल्लेख किया गया है । इस सीमा के आसपास काम करने के लिए कुछ हैक का सुझाव दिया गया है, जैसे बंदर-पैचिंग एक वैकल्पिक विधि के साथ ओवरराइट करने के लिए जो मूल को ब्लॉक में लपेटता है और ब्लॉक के अंदर से कॉल करता है। वैकल्पिक रूप से, आप बस मैन्युअल रूप में अपने धागे से प्रत्येक के लिए प्रवेश बिंदु लपेट सकता है / खुद।sys.excepthookThread.__init__self.runruntrysys.excepthookexcepttryexcept


3

अनकैप्ड अपवाद संदेश STDERR पर जाते हैं, इसलिए Python में अपनी लॉगिंग को लागू करने के बजाय आप STDERR को अपनी पायथन लिपि को चलाने के लिए जो भी शेल का उपयोग कर रहे हैं उसे एक फ़ाइल में भेज सकते हैं। बैश स्क्रिप्ट में, आप इसे आउटपुट रीडायरेक्शन के साथ कर सकते हैं, जैसा कि BASH गाइड में वर्णित है

उदाहरण

टर्मिनल के लिए अन्य आउटपुट फाइल करने के लिए त्रुटियों को जोड़ें:

./test.py 2>> mylog.log

Interleaved STDOUT और STDERR आउटपुट के साथ ओवरराइट फ़ाइल:

./test.py &> mylog.log


1

आप किसी भी स्तर पर (एक प्रकार का जानवर, जानकारी, ...) का उपयोग करके ट्रेसबैक प्राप्त कर सकते हैं। ध्यान दें कि उपयोग करते हुए logging.exception, स्तर ERROR है।

# test_app.py
import sys
import logging

logging.basicConfig(level="DEBUG")

def do_something():
    raise ValueError(":(")

try:
    do_something()
except Exception:
    logging.debug("Something went wrong", exc_info=sys.exc_info())
DEBUG:root:Something went wrong
Traceback (most recent call last):
  File "test_app.py", line 10, in <module>
    do_something()
  File "test_app.py", line 7, in do_something
    raise ValueError(":(")
ValueError: :(

संपादित करें:

यह भी काम करता है (अजगर 3.6 का उपयोग करके)

logging.debug("Something went wrong", exc_info=True)

1

यहाँ एक संस्करण है जो sys.excepthook का उपयोग करता है

import traceback
import sys

logger = logging.getLogger()

def handle_excepthook(type, message, stack):
     logger.error(f'An unhandled exception occured: {message}. Traceback: {traceback.format_tb(stack)}')

sys.excepthook = handle_excepthook

के {traceback.format_exc()}बजाय का उपयोग कैसे करें {traceback.format_tb(stack)}?
चर


हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.