用追踪loggingexception
我怎样才能logging我的Python错误?
try: do_something() except: # How can I log my exception here, complete with its traceback?
使用logging.exception
与except:
处理程序来logging当前的exception,并附加一条消息。
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
使用exc_info选项可能会更好,仍然是警告或错误标题:
try: # coode in here except Exception as e: logging.error(e, exc_info=True)
我最近的工作是让我logging我们应用程序中的所有回溯/exception。 我尝试了许多其他人在网上发布的技术,比如上面的技术,但采用了不同的方法。 覆盖traceback.print_exception
。
我有一个在http://www.bbarrows.com/写一个这将是更容易阅读,但我也粘贴在这里。;
当任务logging我们的软件可能在野外遇到的所有exception时,我尝试了许多不同的技术来logging我们的pythonexception回溯。 起初我以为Python系统exception挂钩,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不会存在于我的进程开始的任何新线程。 这是一个很大的问题,因为大部分事情都发生在这个项目的线程中。
Googlesearch和阅读大量文档后,我发现最有用的信息是来自Python Issue跟踪器。
线程上的第一篇文章展示了一个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个build议的黑客。 无论是Thread
子类,还是在我们自己的try方法中封装run方法,以便捕获并loggingexception或者monkey patch threading.Thread.run
在你自己的try块中运行并loggingexception。
子类化Thread
的第一种方法在我的代码中似乎不那么优雅,因为您必须导入并使用您想要的日志线程的自定义Thread
类EVERYWHERE。 这最终是一个麻烦,因为我不得不search我们的整个代码库,并用这个自定义Thread
replace所有正常的Threads
。 然而,很明显,这个Thread
正在做什么,如果某个用户自定义的日志logging代码发生错误,对于某人来说,诊断和debugging将会更容易。 客户日志logging线程可能如下所示:
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
的第二种方法threading.Thread.run
是很好的,因为我可以在__main__
之后运行一次,并在所有例外情况下logging我的日志logging代码。 猴子修补可能是烦人的debugging,但它会改变预期的function的东西。 来自Python Issue跟踪器的build议修补程序是:
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
直到我开始testingexception日志logging时,我才意识到我所做的一切都是错误的。
为了testing我放置了一个
raise Exception("Test")
在我的代码中的某处。 然而,包装一个调用这个方法的方法是一个尝试,除了打印出回溯的吞吐量和吞噬exception。 这是非常令人沮丧的,因为我看到回溯带来打印到STDOUT,但没有被logging。 这是我然后决定一个更简单的方法logging追踪只是猴子补丁的方法,所有的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
此代码将追溯写入string缓冲区并将其logging到日志logging错误。 我有一个自定义日志logging处理程序设置“自定义日志logging器”采取错误级别日志,并将其发送回家进行分析。
您可以通过为sys.excepthook
分配一个处理程序来sys.excepthook
主线程上所有未捕获到的sys.excepthook
,也许使用Python日志loggingfunction的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
创build的threading.Thread
不会触发sys.excepthook
当它们内部发生未捕获的exception时,正如Python问题追踪器上的问题1230540所述 。 一些黑客已经build议在那里解决这个限制,比如猴子补丁Thread.__init__
self.run
用另一个run
方法覆盖self.run
,它将原始文件封装在try
块中,并从except
块中调用sys.excepthook
。 或者,你可以手动包装每个线程的入口点在try
/ except
你自己。
未被捕获的exception消息转到STDERR,所以不用在Python中实现自己的日志loggingfunction,而是可以使用任何你用来运行你的Python脚本的shell来发送STDERR文件。 在Bash脚本中,您可以使用输出redirect来执行此操作,如BASH指南中所述 。
例子
追加错误到文件,其他输出到terminal:
./test.py 2>> mylog.log
用交错STDOUT和STDERR输出覆盖文件:
./test.py &> mylog.log
下面是一个从Python 2.6文档中取得的简单例子:
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')
也许不如时尚,但更容易:
#!/bin/bash log="/var/log/yourlog" /path/to/your/script.py 2>&1 | (while read; do echo "$REPLY" >> $log; done)