Python 监视作业中的gevent异常
我正在使用gevent构建一个应用程序。我的应用程序现在越来越大了,因为有很多工作正在产生和破坏。现在我注意到,当其中一个作业崩溃时,我的整个应用程序只会继续运行(如果异常来自非主greenlet),这很好。但问题是,我必须查看控制台才能看到错误。因此,我的应用程序的某些部分可能会“死亡”,而我并没有立即意识到这一点,应用程序仍在运行 用try-catch东西抖动我的应用程序似乎不是一个干净的解决方案。 可能是一个自定义的spawn函数,它可以执行一些错误报告 什么是监控gevent作业/绿地的正确方法?捕获异常 在我的例子中,我倾听一些不同来源的事件,我应该处理每个不同的事件。 大概有5份工作非常重要。webserver greenlet、websocket greenlet、,Python 监视作业中的gevent异常,python,exception-handling,gevent,Python,Exception Handling,Gevent,我正在使用gevent构建一个应用程序。我的应用程序现在越来越大了,因为有很多工作正在产生和破坏。现在我注意到,当其中一个作业崩溃时,我的整个应用程序只会继续运行(如果异常来自非主greenlet),这很好。但问题是,我必须查看控制台才能看到错误。因此,我的应用程序的某些部分可能会“死亡”,而我并没有立即意识到这一点,应用程序仍在运行 用try-catch东西抖动我的应用程序似乎不是一个干净的解决方案。 可能是一个自定义的spawn函数,它可以执行一些错误报告 什么是监控gevent作业/绿地的
数据库greenlet、报警greenlet和zmq greenlet。如果其中任何一个“死亡”,我的申请应该完全死亡。其他死亡的工作并不那么重要。例如,websocket greenlet可能由于引发了一些异常而死亡,而其余的应用程序保持正常运行,就像什么都没发生一样。它现在是完全无用和危险的,应该只是死机。 我认为最干净的方法是捕获你认为致命的异常,并执行<代码> sys .EXIT()/<代码>(在代码< > StaseExchange < /Cux>没有退出进程之前,你需要G事件)。 另一种方法是使用link_异常,如果greenlet因异常而死亡,将调用link_异常
spawn(important_greenlet).link_exception(lambda *args: sys.exit("important_greenlet died"))
请注意,您还需要gevent 1.0才能使其正常工作
如果启用0.13.6,请执行以下操作以终止进程:
gevent.get_hub().parent.throw(SystemExit())
你想把你所有的绿地都变成一个“到看门人”的功能
任何死亡的greenlet都会传递给看门人函数,看门人函数可以从中检查greenlet,看看发生了什么,如果有必要,还可以采取一些措施。greenlet.link_exception()的主要问题是,它没有提供任何关于回溯的信息,这对于日志记录来说非常重要 对于带有回溯的日志记录,我使用一个装饰器来spwan作业,该作业间接调用一个简单的日志记录函数:
from functools import wraps
import gevent
def async(wrapped):
def log_exc(func):
@wraps(wrapped)
def wrapper(*args, **kwargs):
try:
func(*args, **kwargs)
except Exception:
log.exception('%s', func)
return wrapper
@wraps(wrapped)
def wrapper(*args, **kwargs):
greenlet = gevent.spawn(log_exc(wrapped), *args, **kwargs)
return wrapper
当然,您可以添加
link\u异常
调用来管理作业(我不需要)正如@Denis和@lvo所说,link\u异常
是可以的,但我认为如果不更改当前代码来生成greenlet,会有更好的方法
通常,每当在greenlet中抛出异常时,将为该greenlet调用\u report\u error
方法(在gevent.greenlet.greenlet
中)。它将执行一些操作,比如调用所有链接函数,最后使用当前堆栈中的exc\u信息调用self.parent.handle\u error
。self.parent
这里是全局Hub
对象,这意味着每个greenlet中发生的所有异常将始终集中到一个方法进行处理。默认情况下,Hub.handle\u error
区分异常类型,忽略某些类型并打印其他类型(这是我们在控制台中经常看到的)
通过修补Hub.handle\u error
方法,我们可以轻松注册自己的错误处理程序,并且不再丢失错误。我编写了一个helper函数来实现它:
from gevent.hub import Hub
IGNORE_ERROR = Hub.SYSTEM_ERROR + Hub.NOT_ERROR
def register_error_handler(error_handler):
Hub._origin_handle_error = Hub.handle_error
def custom_handle_error(self, context, type, value, tb):
if not issubclass(type, IGNORE_ERROR):
# print 'Got error from greenlet:', context, type, value, tb
error_handler(context, (type, value, tb))
self._origin_handle_error(context, type, value, tb)
Hub.handle_error = custom_handle_error
要使用它,只需在初始化事件循环之前调用它:
def gevent_error_handler(context, exc_info):
"""Here goes your custom error handling logics"""
e = exc_info[1]
if isinstance(e, SomeError):
# do some notify things
pass
sentry_client.captureException(exc_info=exc_info)
register_error_handler(gevent_error_handler)
这个解决方案已经在gevent 1.0.2和1.1b3下进行了测试,我们使用它向sentry(一个异常跟踪系统)发送greenlet错误信息,到目前为止,它运行得非常好。在0.13.6上,我没有
gevent.get_hub()
。我确实有gevent.getcurrent()
,但是父属性是None
@scottm那么试试gevent.hub.get\u hub()。这比杀死整个进程更明智。我已经将任务关键的greenlet设置得非常小(8行代码),它们会依次生成greenlet,而崩溃是“正常”的。