Twisted未处理错误
当twisted反应器运行时,如果在一个未被捕获的延迟操作中发生了异常,终端会打印出“未处理的错误”,并附带错误追踪信息和异常内容。有没有办法处理或拦截这些异常呢?比如说,设置一个回调函数或者重写某个方法?
补充说明:我知道可以通过给延迟操作添加一个错误回调来捕获失败。我想知道的是,是否有办法拦截那些未处理的失败或异常,这些异常已经向上传递到反应器那里。
补充说明:基本上,我想知道twisted反应器是否有一个全局的错误处理器,或者有什么可以访问的东西。我之所以这么想,是因为它会打印出失败的追踪信息和错误。
示例:
Unhandled Error
Traceback (most recent call last):
File "/var/projects/python/server.py", line 359, in run_server
return server.run()
File "/var/projects/python/server.py", line 881, in run
reactor.run()
File "/usr/local/lib/python2.6/dist-packages/Twisted-11.0.0-py2.6-linux-x86_64.egg/twisted/internet/base.py", line 1162, in run
self.mainLoop()
File "/usr/local/lib/python2.6/dist-packages/Twisted-11.0.0-py2.6-linux-x86_64.egg/twisted/internet/base.py", line 1171, in mainLoop
self.runUntilCurrent()
--- <exception caught here> ---
File "/usr/local/lib/python2.6/dist-packages/Twisted-11.0.0-py2.6-linux-x86_64.egg/twisted/internet/base.py", line 793, in runUntilCurrent
call.func(*call.args, **call.kw)
File "/var/projects/python/server.py", line 524, in monitor
elapsed = time.time() - info.last
exceptions.NameError: global name 'info' is not defined
3 个回答
回应你的评论:
基本上,我在想 twisted reactor 有没有一个全局的错误处理器或者可以访问的东西。我之所以这么想,是因为它会打印出失败的追踪信息和错误。
答案是“没有以正确的方式处理”。
首先,reactor 和 deferreds 没有直接关系,实际上,整个 deferred 模块应该放在 twisted.python
包里,但由于一些依赖关系,现在还不能这样做。回到你的问题...
深入研究 twisted 的代码(更准确地说,是 twisted.internet.defer
模块),你可以概括出以下事件流程:
- 当调用
callback
方法并传入结果时,deferred 实例开始通过_runCallbacks
方法运行它的回调; - 如果其中一个回调抛出异常,它会被包装成 Failure(第 542 行);
- 如果回调链被耗尽,且最后的结果是失败,那么当前的结果会被赋值给
DebugInfo
实例的 failResult 属性(第 575 行); - 如果 deferred 实例及其
DebugInfo
实例被垃圾回收,但仍然有一个活动的失败结果,那么会调用DebugInfo.__del__
方法,并打印出追踪信息。
根据这些前提,最简单的解决方案之一就是对 DebugInfo
类进行猴子补丁:
from twisted.internet.defer import DebugInfo
del DebugInfo.__del__ # Hides all errors
你可以给这个延迟操作加一个错误处理函数;如果有异常没有被处理,它们会自动变成 twisted.python.failure.Failure
这种类型的错误。
因为这些错误追踪信息是通过调用 twisted.python.log.deferr()
来生成的(至少在 Twisted 10.2 版本中是这样),所以我们可以用一个日志观察者来重定向它们。这是处理这些堆栈跟踪信息最常见的方式。虽然我找不到日志观察者的基类(这有点意外),但有几个内置的选项:
twisted.python.log.PythonLoggingObserver
- 任何记录的信息都会发送到标准的 Python logging
模块。(我在我的应用程序中使用这个。)
twisted.python.log.FileLogObserver
- 任何记录的信息都会保存到一个文件中。
这两种方式都能捕捉到反应器报告的堆栈跟踪信息。你只需要创建一个日志观察者(不需要任何参数),然后调用这个对象的 start()
方法就可以了。
(顺便提一下,还有一个 StdioOnnaStick
类,你可以创建它并将其分配给 sys.stdout
或 sys.stderr
,如果你愿意的话。这样你用 print
输出的任何内容都会记录到 Twisted 日志中。)
如果你想真正地拦截这些调用,让堆栈跟踪信息完全不被记录,你可以选择以下两种方法:
- 创建一个
twisted.internet.SelectReactor
的子类,并重写它的runUntilCurrent()
方法。这个方法负责记录堆栈跟踪信息。在这样做之前,你需要研究一下twisted.internet.base.ReactorBase
的源代码。 - 在你完成所有
twisted.*
的导入后,将twisted.python.log.deferr
设置为你选择的一个函数,这个函数需要与原型def err(_stuff=None, _why=None, **kw)
兼容。