使用回溯记录异常

TIM*_*MEX 123 python error-handling logging exception

如何记录我的Python错误?

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

nos*_*klo 178

使用logging.exceptionwith 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
Run Code Online (Sandbox Code Playgroud)

现在查看日志文件,/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
Run Code Online (Sandbox Code Playgroud)

  • 请注意,如果使用`logger = logging.getLogger('yourlogger')定义记录器,则必须编写`logger.exception('...')`以使其工作... (7认同)

fly*_*cee 116

使用exc_info选项可能更好,仍然是警告或错误标题:

try:
    # coode in here
except Exception as e:
    logging.error(e, exc_info=True)
Run Code Online (Sandbox Code Playgroud)

  • 我永远记不起 `exc_info=` kwarg 叫什么;谢谢! (2认同)
  • 这与logging.exception相同,但该类型被冗余记录两次.只需使用logging.exception,除非你想要一个非错误的级别. (2认同)
  • @Wyrmwood它不一样,因为你必须向“logging.exception”提供消息 (2认同)

Bra*_*ows 54

我的工作最近要求我记录我们的应用程序中的所有回溯/异常.我尝试了许多其他人在网上发布的技术,例如上面提到的技术,但我采用了不同的方法.压倒一切traceback.print_exception.

我在http://www.bbarrows.com/上写了一篇文章.这会更容易阅读,但我也会把它粘贴在这里.

当负责记录我们的软件可能在野外遇到的所有异常时,我尝试了许多不同的技术来记录我们的python异常回溯.起初我认为python系统异常钩子,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  
Run Code Online (Sandbox Code Playgroud)

这适用于主线程但我很快发现我的sys.excepthook在我的进程启动的任何新线程中都不存在.这是一个很大的问题,因为大多数事情都发生在这个项目的线程中.

在谷歌搜索和阅读大量文档后,我发现最有用的信息来自Python问题跟踪器.

线程上的第一篇文章显示了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()
Run Code Online (Sandbox Code Playgroud)

这个Python Issue线程上的消息确实导致了2个建议的黑客攻击.要么子类Thread并将run方法包装在我们自己的try除了块之外,以便捕获和记录异常或者猴子补丁threading.Thread.run在你自己的try中运行除了块并记录异常.

Thread我的代码中,第一种子类化方法似乎不那么优雅,因为你必须导入并使用Thread你希望拥有日志记录线程的自定义类.这最终是一个麻烦,因为我必须搜索我们的整个代码库并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")
Run Code Online (Sandbox Code Playgroud)

猴子补丁的第二种方法threading.Thread.run很好,因为我可以在刚刚运行一次__main__并在所有异常中检测我的日志代码.猴子修补可能很烦人调试,因为它改变了某些东西的预期功能.Python Issue跟踪器中建议的补丁是:

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
Run Code Online (Sandbox Code Playgroud)

直到我开始测试我的异常日志记录,我意识到我的一切都错了.

测试我放了一个

raise Exception("Test")
Run Code Online (Sandbox Code Playgroud)

在我的代码中的某个地方.但是,包装一个调用此方法的方法是一个尝试,除了打印出回溯并吞下异常的块.这是非常令人沮丧的,因为我看到追溯打印到STDOUT但没有被记录.然后我决定一种更容易记录回溯的方法就是修补所有python代码用来打印回溯本身的方法traceback.print_exception.我最终得到了类似于以下内容的东西:

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
Run Code Online (Sandbox Code Playgroud)

此代码将回溯写入字符串缓冲区并将其记录到记录ERROR.我有一个自定义日志处理程序设置'customLogger'记录器,它接收ERROR级别日志并将它们发送回家进行分析.

  • 相当有趣的方法.一个问题 - "add_custom_print_exception"似乎不在您链接的网站上,而是在那里有一些完全不同的最终代码.你会说哪一个更好/更终,为什么?谢谢! (2认同)

Mar*_*ery 7

您可以通过为sys.excepthook可能使用exc_infoPython的日志记录函数参数分配处理程序来记录主线程上的所有未捕获的异常:

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')
Run Code Online (Sandbox Code Playgroud)

如果你的程序使用线程,然而,则请注意,使用创建的线程threading.Thread不会触发sys.excepthook时,未捕获的异常在他们里面发生,在指出问题1230540 Python的问题跟踪器.有些黑客已经建议在那里解决这个限制,比如猴子修补用一个替代方法Thread.__init__覆盖self.run,该run方法将原始包装在一个try块中并sys.excepthookexcept块内部调用.或者,您可以手动为try/ 您except自己包装每个线程的入口点.

  • 谢谢你,这比用 `try: except` 将所有内容包装在我的 `main()` 函数中要优雅得多 (4认同)

Con*_*che 6

您可以在任何级别(调试、信息、...)使用记录器获取回溯。请注意,使用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())
Run Code Online (Sandbox Code Playgroud)
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: :(
Run Code Online (Sandbox Code Playgroud)

编辑:

这也有效(使用python 3.6)

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: :(
Run Code Online (Sandbox Code Playgroud)


Mar*_*oma 5

我在找什么:

import sys
import traceback

exc_type, exc_value, exc_traceback = sys.exc_info()
traceback_in_var = traceback.format_tb(exc_traceback)
Run Code Online (Sandbox Code Playgroud)

看: