在Tornado中,Exception被封装在Future中,而异步函数的调用者必须生成Future以解包Exception。如果我有一个长的异步函数调用链,如何让日志消息正确地打印出异常发生的函数名和行?
例如,在下面的代码中:
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:我意识到我的问题的答案可能与龙卷风无关,但我只是把它包括在这里,因为它是我的情况。
答案 0 :(得分:3)
我看到你正在使用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,因为每个Exception都附加了一个Traceback,并且Tornado正确地将协程的跟踪附加到异常,因为它在异常处理期间展开堆栈。 For some history, see the docs on Future
最后,如果用gen.coroutine
替换async def
,你将获得Python 3.5解释器本身实现的干净回溯。