正确记录协程中的异常 Python3 + Tornado4.3 + 本机日志记录模块
Correct logging for exception in coroutine Python3 + Tornado4.3 + native logging module
在 Tornado 中,异常被封装在 Future 中,异步函数的调用者必须产生 Future 才能解压异常。如果我有很长的异步函数调用链,如何获取日志消息以正确打印出函数名称和发生异常的行?
例如,在下面的代码中:
FORMAT = '%(asctime)s - %(levelname)s - %(filename)s : %(lineno)s'\
' - %(funcName)20s() - %(message)s\n'
logging.basicConfig(format=FORMAT)
...
@gen.coroutine
def layer_2():
return(yield async_func())
@gen.coroutine
def layer_1():
return(yield layer_2())
@gen.coroutine
def main():
try:
yield layer_1()
except Exception as e:
logging.warning('Error: {}'.format(str(e))
如果 async_func()
中出现异常,日志消息 lineno
和 funcName
是 main()
的日志消息,而不是 async_func()
的消息。
除了 try
和 catch
每个 yield
语句,还有什么解决方案吗?谢谢!
编辑 1:我意识到我的问题的答案可能与龙卷风无关,但我只是把它包括在这里,因为这是我的情况。
我看到您正在使用 Python 3,并且在 Python 3 中,Futures 的异常具有自动可用的回溯。使用 logging.exception
打印它:
@gen.coroutine
def main():
try:
yield layer_1()
except Exception as e:
logging.exception(e)
logging
没有什么神秘的事情发生。您可以自己打印回溯:
@gen.coroutine
def main():
try:
yield layer_1()
except Exception as e:
import traceback
traceback.print_exc()
与来自常规函数调用堆栈的回溯相比,此回溯中有一些额外的内容:对 gen.Runner.run 的调用,这是 Tornado 协程实现的一部分。但它达到了它的目的:你可以看到 main
在堆栈的顶部,而 async_func
在底部。
这在 Python 3 中有效,因为每个异常都附加了一个回溯,并且 Tornado 在异常处理期间展开堆栈时正确地将协程的回溯附加到异常。 For some history, see the docs on Future.
最后,如果您将 gen.coroutine
替换为 async def
,您将获得由 Python 3.5 解释器本身实现的清晰回溯。
在 Tornado 中,异常被封装在 Future 中,异步函数的调用者必须产生 Future 才能解压异常。如果我有很长的异步函数调用链,如何获取日志消息以正确打印出函数名称和发生异常的行?
例如,在下面的代码中:
FORMAT = '%(asctime)s - %(levelname)s - %(filename)s : %(lineno)s'\
' - %(funcName)20s() - %(message)s\n'
logging.basicConfig(format=FORMAT)
...
@gen.coroutine
def layer_2():
return(yield async_func())
@gen.coroutine
def layer_1():
return(yield layer_2())
@gen.coroutine
def main():
try:
yield layer_1()
except Exception as e:
logging.warning('Error: {}'.format(str(e))
如果 async_func()
中出现异常,日志消息 lineno
和 funcName
是 main()
的日志消息,而不是 async_func()
的消息。
除了 try
和 catch
每个 yield
语句,还有什么解决方案吗?谢谢!
编辑 1:我意识到我的问题的答案可能与龙卷风无关,但我只是把它包括在这里,因为这是我的情况。
我看到您正在使用 Python 3,并且在 Python 3 中,Futures 的异常具有自动可用的回溯。使用 logging.exception
打印它:
@gen.coroutine
def main():
try:
yield layer_1()
except Exception as e:
logging.exception(e)
logging
没有什么神秘的事情发生。您可以自己打印回溯:
@gen.coroutine
def main():
try:
yield layer_1()
except Exception as e:
import traceback
traceback.print_exc()
与来自常规函数调用堆栈的回溯相比,此回溯中有一些额外的内容:对 gen.Runner.run 的调用,这是 Tornado 协程实现的一部分。但它达到了它的目的:你可以看到 main
在堆栈的顶部,而 async_func
在底部。
这在 Python 3 中有效,因为每个异常都附加了一个回溯,并且 Tornado 在异常处理期间展开堆栈时正确地将协程的回溯附加到异常。 For some history, see the docs on Future.
最后,如果您将 gen.coroutine
替换为 async def
,您将获得由 Python 3.5 解释器本身实现的清晰回溯。