如何记录我的Python错误?
try:
do_something()
except:
# How can I log my exception here, complete with its traceback?
答案 0 :(得分:165)
使用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 :(得分: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)
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')