Python 监视作业中的gevent异常

Python 监视作业中的gevent异常,python,exception-handling,gevent,Python,Exception Handling,Gevent,我正在使用gevent构建一个应用程序。我的应用程序现在越来越大了,因为有很多工作正在产生和破坏。现在我注意到,当其中一个作业崩溃时,我的整个应用程序只会继续运行(如果异常来自非主greenlet),这很好。但问题是,我必须查看控制台才能看到错误。因此,我的应用程序的某些部分可能会“死亡”,而我并没有立即意识到这一点,应用程序仍在运行 用try-catch东西抖动我的应用程序似乎不是一个干净的解决方案。 可能是一个自定义的spawn函数,它可以执行一些错误报告 什么是监控gevent作业/绿地的

我正在使用gevent构建一个应用程序。我的应用程序现在越来越大了,因为有很多工作正在产生和破坏。现在我注意到,当其中一个作业崩溃时,我的整个应用程序只会继续运行(如果异常来自非主greenlet),这很好。但问题是,我必须查看控制台才能看到错误。因此,我的应用程序的某些部分可能会“死亡”,而我并没有立即意识到这一点,应用程序仍在运行

用try-catch东西抖动我的应用程序似乎不是一个干净的解决方案。 可能是一个自定义的spawn函数,它可以执行一些错误报告

什么是监控gevent作业/绿地的正确方法?捕获异常

在我的例子中,我倾听一些不同来源的事件,我应该处理每个不同的事件。 大概有5份工作非常重要。webserver greenlet、websocket greenlet、,
数据库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,而崩溃是“正常”的。