首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用contextlib contextmanager忽略并记录错误

使用contextlib contextmanager忽略并记录错误
EN

Stack Overflow用户
提问于 2018-05-08 12:42:44
回答 1查看 1.8K关注 0票数 1

我希望上下文管理器捕获异常,打印堆栈跟踪,然后允许执行继续。

我想知道是否可以使用contextlib contextmanager装饰器来完成这个任务。如果没有,我怎么做呢?

文件建议如下:

在生成器产生的地方,执行with语句中嵌套的块。然后,在该块退出后,该生成器被恢复。如果在块中发生未处理的异常,则在发生屈服的地方,在生成器内重新分配异常。因此,您可以使用try…除…外语句来捕获错误(如果有的话),或者确保进行某种清理。如果一个异常被捕获仅仅是为了记录它或执行某些操作(而不是完全抑制它),那么生成器必须重新定义该异常。

因此,我尝试了一种显而易见的方法,文档引导我这样做:

代码语言:javascript
复制
import contextlib
import logging


@contextlib.contextmanager
def log_error():
    try:
        yield
    except Exception as e:
        logging.exception('hit exception')
    finally:
        print 'done with contextmanager'


def something_inside_django_app():
    with log_error():
        raise Exception('alan!')


something_inside_django_app()


print 'next block of code'

这将产生输出。

代码语言:javascript
复制
ERROR:root:hit exception
Traceback (most recent call last):
  File "exception_test.py", line 8, in log_error
    yield
  File "exception_test.py", line 17, in something_inside_django_app
    raise Exception('alan!')
Exception: alan!
done with contextmanager
next block of code

这将丢失有关异常从何处引发的关键信息。当您调整上下文管理器以不抑制异常时,请考虑您得到了什么:

代码语言:javascript
复制
Traceback (most recent call last):
  File "exception_test.py", line 20, in <module>
    something_inside_django_app()
  File "exception_test.py", line 17, in something_inside_django_app
    raise Exception('alan!')
Exception: alan!

是的,它能够告诉我,异常是从第17行提出来的,非常感谢,但是前面第20行的呼叫丢失了信息。如何让上下文管理器给我实际的完整调用堆栈,而不是它的截断版本?概括地说,我想满足两个要求:

  • 让python上下文管理器抑制它包装的代码中引发的异常。
  • 如果没有使用上下文管理器,则打印由该代码生成的堆栈跟踪。

如果这不能用装饰器完成,那么我将使用另一种样式的上下文管理器。如果上下文管理器不能做到这一点,那么我想知道什么是好的pythonic选项。

EN

回答 1

Stack Overflow用户

发布于 2018-05-25 13:16:33

我在这里更新了我对这个问题的解决方案:

https://gist.github.com/AlanCoding/288ee96b60e24c1f2cca47326e2c0af1

问题漏掉了更多的背景。为了在异常点获得完整的堆栈,我们需要返回到上下文管理器的跟踪,以及当前的上下文。然后我们可以把堆栈的顶部和堆栈的底部粘合在一起。

为了更好地说明用例,请考虑以下几点:

代码语言:javascript
复制
def err_method1():
    print [1, 2][4]


def err_method2():
    err_method1()


def outside_method1():
    with log_error():
        err_method2()


def outside_method2():
    outside_method1()

outside_method2()

为了真正完成这个问题,我们希望看到调用堆栈中的外部方法和内部方法。

下面是一种解决方案,看起来确实适用于此:

代码语言:javascript
复制
class log_error(object):

    def __enter__(self):
        return

    def __exit__(self, exc_type, exc_value, exc_traceback):
        if exc_value:
            # We want the _full_ traceback with the context, so first we
            # get context for the current stack, and delete the last 2
            # layers of context, saying that we're in the __exit__ method...
            top_stack = StringIO.StringIO()
            tb.print_stack(file=top_stack)
            top_lines = top_stack.getvalue().strip('\n').split('\n')[:-4]
            top_stack.close()
            # Now, we glue that stack to the stack from the local error
            # that happened within the context manager
            full_stack = StringIO.StringIO()
            full_stack.write('Traceback (most recent call last):\n')
            full_stack.write('\n'.join(top_lines))
            full_stack.write('\n')
            tb.print_tb(exc_traceback, file=full_stack)
            full_stack.write('{}: {}'.format(exc_type.__name__, str(exc_value)))
            sinfo = full_stack.getvalue()
            full_stack.close()
            # Log the combined stack
            logging.error('Log message\n{}'.format(sinfo))
        return True

回溯看起来是:

代码语言:javascript
复制
ERROR:root:Log message
Traceback (most recent call last):
  File "exception_test.py", line 71, in <module>
    outside_method2()
  File "exception_test.py", line 69, in outside_method2
    outside_method1()
  File "exception_test.py", line 65, in outside_method1
    err_method2()
  File "exception_test.py", line 60, in err_method2
    err_method1()
  File "exception_test.py", line 56, in err_method1
    print [1, 2][4]
IndexError: list index out of range

这是您在尝试中执行logging.exception时所期望的相同信息--除了在上下文管理器中包装的相同代码之外。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/50233935

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档