Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/logging/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 异步异常处理在使用sure\u future时有效,但在使用run\u直到\u完成时无效_Python_Logging_Python Asyncio - Fatal编程技术网

Python 异步异常处理在使用sure\u future时有效,但在使用run\u直到\u完成时无效

Python 异步异常处理在使用sure\u future时有效,但在使用run\u直到\u完成时无效,python,logging,python-asyncio,Python,Logging,Python Asyncio,我将用于异步日志记录,并编写了两个函数来覆盖默认异常处理程序: 从aiologger导入记录器作为aiologger 从aiologger.levels导入日志级别 导入异步 logger=AioLogger.with_default_handlers(name='test',level=LogLevel.NOTSET, loop=asyncio.get\u event\u loop()) def例外挂钩(exc_类型、exc_值、exc_回溯): 打印('调用了excepthook') 如果i

我将用于异步日志记录,并编写了两个函数来覆盖默认异常处理程序:

从aiologger导入记录器作为aiologger
从aiologger.levels导入日志级别
导入异步
logger=AioLogger.with_default_handlers(name='test',level=LogLevel.NOTSET,
loop=asyncio.get\u event\u loop())
def例外挂钩(exc_类型、exc_值、exc_回溯):
打印('调用了excepthook')
如果issubclass(exc_类型,键盘中断):
系统(exc类型、exc值、exc回溯)
返回
logger.error(f'Uncaught异常:{exc_type.\u_name\u_}')
def异步IO_异常_处理程序(循环、上下文):
打印('调用了异步IO\u异常\u处理程序')
exception=context.get('exception',None)
如有例外:
exc\U info=(类型(异常)、异常、异常.\uu回溯\uuuu)
如果issubclass(例外情况,键盘中断):
系统(除挂钩外)(*exc\U信息)
返回
logger.error(f'Uncaught异常:{exc_info[0].\u_name__}')
其他:
logger.error(上下文['message'])
然后,我用我提供的异常处理程序覆盖异常处理程序:

导入系统 sys.excepthook=excepthook asyncio.get_event_loop().set_exception_处理程序(asyncio_exception_处理程序) 最后,我编写了一个简单的代码来测试功能:

async def main():
引发运行时错误(“未捕获测试”)
loop=asyncio.get\u event\u loop()
asyncio.sure_future(main())
loop.run_forever()
其工作如预期,输出为:

asyncio_exception_handler called.
Uncaught Exception: RuntimeError
^Cexcepthook called.
Traceback (most recent call last):
  File "examples/sample.py", line 110, in <module>
    loop.run_forever()
  File "/usr/lib/python3.8/asyncio/base_events.py", line 570, in run_forever
    self._run_once()
  File "/usr/lib/python3.8/asyncio/base_events.py", line 1823, in _run_once
    event_list = self._selector.select(timeout)
  File "/usr/lib/python3.8/selectors.py", line 468, in select
    fd_event_list = self._selector.poll(timeout, max_ev)
KeyboardInterrupt
令人困惑的是,在本例中,执行的是我的
excepthook
函数,而不是
asyncio\u exception\u处理程序
。我的看法是,以某种方式使用
loop.run\u直到\u complete()
会将代码视为非异步的,因此调用创建异步任务的
logger.error()
,没有任何效果



当我的代码使用
循环运行时,如何使用异常处理程序记录未捕获的异常。运行直到完成()?我提供的两个场景之间有什么区别?我不太擅长
asyncio
,我可能错过了这里的一些小注意事项。

AioLogger
是一个异步日志框架,它依赖于要运行的事件循环。当您从
sure_future
提升时,事件循环仍在运行,直到按下Ctrl+C,这就是您看到日志的原因。另一方面,当您使用
run\u until\u complete(main())
时,在
run\u until\u complete
引发事件后不会运行事件循环,因此
excepthook
调度的日志消息将被丢弃

要解决此问题,可以在记录器调用
excepthook
后运行类似
asyncio.sleep(0.001)
的操作,以确保日志消息通过:

def excepthook(exc_type, exc_value, exc_traceback):
    print('excepthook called.')
    if issubclass(exc_type, KeyboardInterrupt):
        sys.__excepthook__(exc_type, exc_value, exc_traceback)
        return
    logger.error(f'Uncaught Exception: {exc_type.__name__}')
    # sleep a bit to give the log a chance to flush
    loop.run_until_complete(asyncio.sleep(0.001))

通常,asyncio异常处理程序是作为最后的错误报告手段,而不是业务逻辑应该依赖的东西。调用
run\u until\u complete()
时,没有理由将异常传递给asyncio异常处理程序,因为异常会传播到
run\u until\u complete()
的调用者,调用者可以用通常的方式处理它。(在您的情况下,
sys.excepthook
被调用,因为错误会传播到顶层。)当您调用
sure_future
时,您会得到一个
future
实例(或其
任务
子类的实例),您可以
等待
或调用
结果()
异常()
来获取异常。如果您不这样做,只允许未来被GC破坏,它会将异常传递给asyncio处理程序以记录它。

我只返回
键盘中断
实例。引发的异常是
RuntimeError
,不会返回。你可以放心地忽略这一部分。这实际上是我最后的错误处理解决方案,因为我有意地使用了非等待的协程,并且在asyncio world中也存在异常,这些异常不会返回到用户空间(我可以将名称及其解决方案作为示例),我想用JSON记录这些回溯,并将其发送到我的日志聚合系统,始终存在一些未捕获的异常,表示开发人员未处理的路径。我希望我的日志平台能够成功地以我的结构化格式记录和发送它们。正如您所知,Docker和其他容器运行时不太擅长处理多行日志(每行都将被视为单独的日志)。这就是为什么我需要一个未捕获异常的解决方案。@如果你说得对,问题是完全不同的。我现在修改了答案以解决这个问题。@A我觉得有趣的是,
asyncio.sleep()
没有帮助。为了消除混淆,我指的是修改顶级代码,现在我想到,修改
excepthook
@alitu可能更好。我现在已经将它包含在答案中。您可能希望尝试将
asyncio.sleep(0.001)
更改为
logger.shutdown()
(如示例所示),以实际确保刷新挂起的日志。