使用回溯记录异常

时间:2009-10-02 09:07:15

标签: python exception logging error-handling

如何记录我的Python错误?

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

11 个答案:

答案 0 :(得分:165)

使用logging.exceptionexcept:处理程序一起记录当前的异常,并附上一条消息。

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 :(得分:102)

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

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

答案 2 :(得分:49)

我的工作最近要求我记录应用程序中的所有回溯/异常。我尝试了许多其他人在网上发布的技术,例如上面提到的技术,但我采用了不同的方法。覆盖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  

这适用于主线程但我很快发现我的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()

此Python问题线程上的消息确实导致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")

猴子修补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

直到我开始测试我的异常日志记录,我才意识到我错了。

测试我放了一个

raise Exception("Test")

我的代码中的某处。但是,包装一个调用此方法的方法是一个try,除了打印出回溯并吞下异常的块。这是非常令人沮丧的,因为我看到追溯打印到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

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

答案 3 :(得分:6)

您可以通过为sys.excepthook分配处理程序来记录主线程上所有未捕获的异常,也许使用exc_info parameter of Python's logging functions

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内部发生未捕获的异常时,使用Issue 1230540创建的线程不会触发sys.excepthook,如{{ 3}}在Python的问题跟踪器上。有些黑客已经建议在那里解决这个限制,例如使用猴子修补Thread.__init__来覆盖self.run,并使用另一个run方法将原始文件包裹在try块中从sys.excepthook块内部调用except。或者,您可以自己手动为try / except中的每个线程包装入口点。

答案 4 :(得分:3)

未捕获的异常消息转到STDERR,因此您可以使用用于运行Python脚本的任何shell将STDERR发送到文件,而不是在Python本身中实现日志记录。在Bash脚本中,您可以使用输出重定向执行此操作,如described in the BASH guide

实施例

将错误附加到文件,将其他输出附加到终端:

./test.py 2>> mylog.log

使用交错STDOUT和STDERR输出覆盖文件:

./test.py &> mylog.log

答案 5 :(得分:1)

这里是使用sys.excepthook的版本

const myCookies = {
  budgetMonth: someValue1,
  budgetDay: someValue2,
  expensesMonth: someValue3
};
function setCookies(myCookies) {
  for(let k of Object.keys(myCookies)) {
    document.cookie = k + "=" + myCookies[k];
  }
}
setCookies(myCookies);

答案 6 :(得分:0)

可能不那么时尚,但更容易:

#!/bin/bash
log="/var/log/yourlog"
/path/to/your/script.py 2>&1 | (while read; do echo "$REPLY" >> $log; done)

答案 7 :(得分:0)

我正在寻找的东西:

import sys
import traceback

exc_type, exc_value, exc_traceback = sys.exc_info()
traceback_in_var = traceback.format_tb(exc_traceback)

请参阅:

答案 8 :(得分:0)

您可以使用记录器进行任何级别(调试,信息等)的追溯。请注意,使用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: :(

答案 9 :(得分:0)

这就是我的方法。

try:
    do_something()
except:
    # How can I log my exception here, complete with its traceback?
    import traceback
    traceback.format_exc() # this will print a complete trace to stout.

答案 10 :(得分:-1)

以下是python 2.6 documentation

中的一个简单示例
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')