我开始使用django-channels
,发现它很棒。但是,调试使用者很痛苦,因为当使用者内部引发某些异常时,什么都没有打印到终端上,则Websocket就会断开连接。
未显示的异常种类不明显。 AssertionError
和其他一些系统地情况就是如此,例如下面的代码:
class MexicoProgressConsumer(ProgressConsumer):
def init(self, SSDBConfig, Sub_application):
subappli = models.Sub_application.objects.get(pk=Sub_application)
...
使用错误数量的参数调用此方法不会在控制台上打印任何内容并断开websocket。同上,如果最后一行的get
失败。
有没有办法像其他任何例外一样看到那些例外?
答案 0 :(得分:1)
从albar的answer开始,我达到的解决方案是定义一个这样的装饰器
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
这有几个改进:
logged_by_wrapper
的情况下记录异常。因为我们在第一次记录后设置了属性,所以这导致异常仅记录一次。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
适用于使用者中定义的所有异步方法,但不适用于它继承的方法-即仅适用于我们为使用者定制的方法。
答案 1 :(得分:0)
我找到了解决问题的方法。我首先定义一个装饰器:
import traceback
def catch_exception(f):
def wrapper(*args, **kwargs):
try:
return f(*args, **kwargs)
except StopConsumer:
raise
except Exception as e:
print(traceback.format_exc().strip('\n'), '<--- from consumer')
raise
return wrapper
然后,我为所有消费者定义一个基类,该基类以这种方式使用此装饰器:
import inspect
class BaseConsumer(JsonWebsocketConsumer):
def __getattribute__(self, name):
value = object.__getattribute__(self, name)
if inspect.ismethod(value):
return catch_exception(value)
return value
但是仍然存在2个问题:
第一种情况的示例(KeyError):
Traceback (most recent call last):
File "/home/alain/ADN/simutool/dbsimu/consumers.py", line 19, in wrapper
return f(*args, **kwargs)
File "/home/alain/ADN/simutool/dbsimu/consumers.py", line 31, in wrapper
result = f(owner, **kwargs)
File "/home/alain/ADN/simutool/dbsimu/consumers.py", line 110, in refresh
data = super().refresh.__wrapped__(self)
File "/home/alain/ADN/simutool/dbsimu/consumers.py", line 73, in refresh
pvalue = round(data['toto'] * 100, 1)
KeyError: 'toto' <--- from consumer
Exception in thread Thread-3:
Traceback (most recent call last):
File "/usr/lib/python3.6/threading.py", line 916, in _bootstrap_inner
self.run()
File "/usr/lib/python3.6/threading.py", line 864, in run
self._target(*self._args, **self._kwargs)
File "/home/alain/ADN/simutool/dbsimu/utils.py", line 193, in repeat
self.repeat_func()
File "/home/alain/ADN/simutool/dbsimu/consumers.py", line 19, in wrapper
return f(*args, **kwargs)
File "/home/alain/ADN/simutool/dbsimu/consumers.py", line 31, in wrapper
result = f(owner, **kwargs)
File "/home/alain/ADN/simutool/dbsimu/consumers.py", line 110, in refresh
data = super().refresh.__wrapped__(self)
File "/home/alain/ADN/simutool/dbsimu/consumers.py", line 73, in refresh
pvalue = round(data['toto'] * 100, 1)
KeyError: 'toto'
第二种情况的示例(拼写错误的变量):
WebSocket CONNECT /ws/dbsimu/Simuflow_progress/ [127.0.0.1:55866]
Traceback (most recent call last):
File "/home/alain/ADN/simutool/dbsimu/consumers.py", line 19, in wrapper
return f(*args, **kwargs)
File "/home/alain/ADN/simutool/dbsimu/consumers.py", line 57, in receive_json
return getattr(self, icommand)(**data)
NameError: name 'icommand' is not defined <--- from consumer
Traceback (most recent call last):
File "/home/alain/ADN/simutool/dbsimu/consumers.py", line 19, in wrapper
return f(*args, **kwargs)
File "/home/alain/.local/lib/python3.6/site-packages/channels/generic/websocket.py", line 125, in receive
self.receive_json(self.decode_json(text_data), **kwargs)
File "/home/alain/ADN/simutool/dbsimu/consumers.py", line 19, in wrapper
return f(*args, **kwargs)
File "/home/alain/ADN/simutool/dbsimu/consumers.py", line 57, in receive_json
return getattr(self, icommand)(**data)
NameError: name 'icommand' is not defined <--- from consumer
Traceback (most recent call last):
File "/home/alain/ADN/simutool/dbsimu/consumers.py", line 19, in wrapper
return f(*args, **kwargs)
File "/home/alain/.local/lib/python3.6/site-packages/channels/generic/websocket.py", line 60, in websocket_receive
self.receive(text_data=message["text"])
File "/home/alain/ADN/simutool/dbsimu/consumers.py", line 19, in wrapper
return f(*args, **kwargs)
File "/home/alain/.local/lib/python3.6/site-packages/channels/generic/websocket.py", line 125, in receive
self.receive_json(self.decode_json(text_data), **kwargs)
File "/home/alain/ADN/simutool/dbsimu/consumers.py", line 19, in wrapper
return f(*args, **kwargs)
File "/home/alain/ADN/simutool/dbsimu/consumers.py", line 57, in receive_json
return getattr(self, icommand)(**data)
NameError: name 'icommand' is not defined <--- from consumer
如果有人有解决办法,请提出建议。