如何查看从渠道消费者引发的异常

alb*_*bar 3 django exception django-channels

我开始使用django-channels,我觉得它很棒。然而调试消费者是痛苦的,因为当从消费者内部引发一些异常时,终端不会打印任何内容,websocket 只是断开连接。

未显示的异常类型不易识别。对于AssertionError,以及其他一些系统,情况就是如此,例如下面的代码:

class MexicoProgressConsumer(ProgressConsumer):
    def init(self, SSDBConfig, Sub_application):
        subappli = models.Sub_application.objects.get(pk=Sub_application)
        ...
Run Code Online (Sandbox Code Playgroud)

使用错误数量的参数调用此方法不会在控制台上打印任何内容并断开 websocket。如果get最后一行失败,同上。

有没有办法像其他任何例外一样看待这些例外?

Mod*_*ech 5

从 albar 的回答开始,我得到的解决方案是像这样定义一个装饰器

from functools import wraps
from logging import getLogger

from channels.exceptions import AcceptConnection, DenyConnection, StopConsumer

logger = getLogger("foo-logger")

def log_exceptions(f):
    @wraps(f)
    async def wrapper(*args, **kwargs):
        try:
            return await f(*args, **kwargs)
        except (AcceptConnection, DenyConnection, StopConsumer):
            raise
        except Exception as exception:
            if not getattr(exception, "logged_by_wrapper", False):
                logger.error(
                    "Unhandled exception occurred in {}:".format(f.__qualname__),
                    exc_info=exception,
                )
                setattr(exception, "logged_by_wrapper", True)
            raise

    return wrapper
Run Code Online (Sandbox Code Playgroud)

这有几个改进:

  • 使用 functools.wraps 使包装的函数更接近原始函数。
  • 使用 async/await 语法,因为我使用的是异步消费者(如果不是,请删除)
  • 不记录 django-channels 故意引发的几个异常。
  • 如果没有logged_by_wrapper设置属性,则仅记录异常。这导致异常只记录一次,因为我们在第一次记录后设置了属性。
  • 使用 python 的内置logging模块记录错误。这会自动格式化异常和回溯,因为我们在exc_info=exception.

然后我定义了一个类装饰器而不是基类来将其应用于消费者的方法

from inspect import iscoroutinefunction

def log_consumer_exceptions(klass):
    for method_name, method in list(klass.__dict__.items()):
        if iscoroutinefunction(method):
            setattr(klass, method_name, log_exceptions(method))

    return klass
Run Code Online (Sandbox Code Playgroud)

这适用log_exceptions于消费者中定义的所有异步方法,但不适用于它继承的方法——即仅适用于我们为消费者定制的方法。