5

我希望我的代码使用 pythonlogging来记录异常。在我通常使用的代码中await,通常会引发异常,因此:

try: await code_that_can_raise() except Exception as e: logger.exception("Exception happended")

工作正常。

然而,当使用 loop.create_task(coro())

我不确定如何在这里捕获异常。
包装 create_task() 调用显然是行不通的。在代码中记录每个异常的最佳解决方案是什么?

4

3 回答 3

7

在代码中记录每个异常的最佳解决方案是什么?

如果您控制 的调用create_task,但不控制 中的代码coro(),那么您可以编写一个日志包装器:

async def log_exceptions(awaitable):
    try:
        return await awaitable
    except Exception:
        logger.exception("Unhandled exception")

然后你可以打电话loop.create_task(log_exceptions(coro()))

如果您不能或不想包装 every create_task,您可以调用loop.set_exception_handler,将异常设置为您自己的函数,该函数将在您认为合适的时候记录异常。

于 2019-04-16T12:43:03.420 回答
3

刚才已经提到了:asyncio.Task对象具有方法resultexception.
result

[...] 如果协程引发异常,则会重新引发该异常 [...]

exception

[...] 如果包装的协程引发异常,则返回异常 [...]

给定一个简单的设置(在 Python 3.7 语法中):

import asyncio
tasks =[]

async def bad_test():
    raise ValueError

async def good_test():
    return

async def main():
    tasks.append(asyncio.create_task(bad_test()))
    tasks.append(asyncio.create_task(good_test()))

asyncio.run(main())

使用result,可以这样做:

for t in tasks:
    try:
        f = t.result()
    except ValueError as e:
        logger.exception("we're all doomed")

或者,使用exception

for t in tasks:
    if isinstance(t.exception(), Exception):
        logger.exception("apocalypse now")

但是,这两种方法都需要Task完成,否则:

如果 Task 已被取消,则此方法会引发 CancelledError 异常。

( result ):如果 Task 的结果尚不可用,则此方法会引发 InvalidStateError 异常。

异常):如果任务尚未完成,此方法会引发 InvalidStateError 异常。

因此,与其他答案中的提议不同,当任务中出现异常时不会发生日志记录,而是在任务完成后评估任务时发生。

于 2019-04-16T14:12:08.833 回答
0

扩展 @user4815162342 的解决方案,我创建了一个包装器,log_exceptions以避免将每个协程嵌套在 2 个函数中:

import asyncio
from typing import Awaitable

def create_task_log_exception(awaitable: Awaitable) -> asyncio.Task:
    async def _log_exception(awaitable):
        try:
            return await awaitable
        except Exception as e:
            logger.exception(e)
    return asyncio.create_task(_log_exception(awaitable))

用法:

create_task_log_exception(coroutine())
于 2021-11-30T11:42:12.243 回答