Twisted未处理错误

9 投票
3 回答
7022 浏览
提问于 2025-04-16 22:59

当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 个回答

1

回应你的评论:

基本上,我在想 twisted reactor 有没有一个全局的错误处理器或者可以访问的东西。我之所以这么想,是因为它会打印出失败的追踪信息和错误。

答案是“没有以正确的方式处理”。

首先,reactor 和 deferreds 没有直接关系,实际上,整个 deferred 模块应该放在 twisted.python 包里,但由于一些依赖关系,现在还不能这样做。回到你的问题...

深入研究 twisted 的代码(更准确地说,是 twisted.internet.defer 模块),你可以概括出以下事件流程:

  1. 当调用 callback 方法并传入结果时,deferred 实例开始通过 _runCallbacks 方法运行它的回调;
  2. 如果其中一个回调抛出异常,它会被包装成 Failure(第 542 行);
  3. 如果回调链被耗尽,且最后的结果是失败,那么当前的结果会被赋值给 DebugInfo 实例的 failResult 属性(第 575 行);
  4. 如果 deferred 实例及其 DebugInfo 实例被垃圾回收,但仍然有一个活动的失败结果,那么会调用 DebugInfo.__del__ 方法,并打印出追踪信息。

根据这些前提,最简单的解决方案之一就是对 DebugInfo 类进行猴子补丁:

from twisted.internet.defer import DebugInfo
del DebugInfo.__del__  # Hides all errors
3

你可以给这个延迟操作加一个错误处理函数;如果有异常没有被处理,它们会自动变成 twisted.python.failure.Failure 这种类型的错误。

6

因为这些错误追踪信息是通过调用 twisted.python.log.deferr() 来生成的(至少在 Twisted 10.2 版本中是这样),所以我们可以用一个日志观察者来重定向它们。这是处理这些堆栈跟踪信息最常见的方式。虽然我找不到日志观察者的基类(这有点意外),但有几个内置的选项:

twisted.python.log.PythonLoggingObserver - 任何记录的信息都会发送到标准的 Python logging 模块。(我在我的应用程序中使用这个。)

twisted.python.log.FileLogObserver - 任何记录的信息都会保存到一个文件中。

这两种方式都能捕捉到反应器报告的堆栈跟踪信息。你只需要创建一个日志观察者(不需要任何参数),然后调用这个对象的 start() 方法就可以了。

(顺便提一下,还有一个 StdioOnnaStick 类,你可以创建它并将其分配给 sys.stdoutsys.stderr,如果你愿意的话。这样你用 print 输出的任何内容都会记录到 Twisted 日志中。)

如果你想真正地拦截这些调用,让堆栈跟踪信息完全不被记录,你可以选择以下两种方法:

  • 创建一个 twisted.internet.SelectReactor 的子类,并重写它的 runUntilCurrent() 方法。这个方法负责记录堆栈跟踪信息。在这样做之前,你需要研究一下 twisted.internet.base.ReactorBase 的源代码。
  • 在你完成所有 twisted.* 的导入后,将 twisted.python.log.deferr 设置为你选择的一个函数,这个函数需要与原型 def err(_stuff=None, _why=None, **kw) 兼容。

撰写回答