在协程Python3 + Tornado4.3 +本机日志记录模块中更正异常的日志记录

时间:2017-01-07 09:02:11

标签: python logging format tornado

在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()中引发了异常,则日志消息linenofuncName属于main(),而不是async_func()

对于trycatch每个yield语句,是否有任何解决方案?谢谢!

编辑1:我意识到我的问题的答案可能与龙卷风无关,但我只是把它包括在这里,因为它是我的情况。

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解释器本身实现的干净回溯。