从阿尔巴尔开始answer /questions/54458852/how-to-see-exceptions-raised-from-a-channels-consumer/54494878#54494878,我达到的解决方案是像这样定义一个装饰器
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
这有几个改进:
- 使用 functools.wraps 使包装函数更类似于原始函数。
- 使用异步/等待语法,因为我正在使用异步消费者(如果不是,请删除)
- 不记录 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
这适用log_exceptions
对于 Consumer 中定义的所有异步方法,但不是它继承的方法 - 即仅对于 Consumer 的自定义方法。