【问题标题】:Why is Exception output not captured by contextlib.redirect_stderr context manager?为什么 contextlib.redirect_stderr 上下文管理器未捕获异常输出?
【发布时间】:2021-11-20 15:44:48
【问题描述】:

在下面的代码中,我希望将异常输出和回溯写入test.txt 文件,但事实并非如此。

import contextlib

with open("test.txt", "a") as f:
    with contextlib.redirect_stderr(f):
        raise Exception("Hello")

为什么它没有按预期工作,如何正确地将异常输出重定向到文件?

【问题讨论】:

    标签: python exception stdout stderr


    【解决方案1】:

    因为重定向 sys.stderr 不会在 python 中捕获异常(如果这样做会很糟糕,对吗?因为异常应该只被 try/except 块捕获。)所以异常处理代码会中断您正在运行的代码(以及重定向)。

    证明:

    import contextlib
    from sys import stderr
    
    with open("test.txt", "a") as f:
        with contextlib.redirect_stderr(f):
            sys.stderr.write("hello")
            sys.stderr.flush()
    

    写得对。

    要将所有异常的输出重定向到文件,您有两个选择:在 try/except 块中全局捕获所有异常并自己编写,或使用 shell 重定向发送 脚本的 stderr到一个文件,类似于

    python my_script.py 2>errors.txt
    

    如果您想自己做,请参阅this question 了解有关获取回溯的讨论。

    【讨论】:

    • 谢谢。我想我可以绕过它:这是因为运行时,包括 contextlib 的 stderr 重定向被中断并在出现异常时停止,然后输出传播到重定向之外。我在自定义上下文管理器中使用了 try/except 块解决方案(如您所建议的)
    • @JeanMonet 完全是 :)
    【解决方案2】:

    只是为了添加一个我选择的替代实现。

    以下是将异常输出重定向到记录器。在我的例子中,记录器设置为标准输出(默认),所以我使用contextlib.redirect_stdout 将记录器的输出重定向到文件,但您当然可以直接让记录器写入文件。

    import logging
    import contextlib
    from typing import Iterator
    
    
    @contextlib.contextmanager
    def capture_exception(logger: logging.Logger) -> Iterator[None]:
        """
        Captures exceptions and redirects output to the logger
        >>> import logging
        >>> logger = logging.getLogger()
        >>> with capture_exception(logger=logger):
        >>>     raise Exception("This should be outputed to the logger")
        """
        try:
            # try/except block where exception is captured and logged
            try:
                yield None
            except Exception as e:
                logger.exception(e)
        finally:
            pass
    

    用法:

    logger = logging.getLogger()   # default should be stdout
    
    with open("test.txt", "a") as f:
        with contextlib.redirect_stdout(f), capture_exception(logger):
            # ^-- multiple context managers in one `with` statement
            raise Exception("The output of this exception will appear in the log. Great success.")
    

    日志中出现的异常输出:

    The output of this exception will appear in the log. Great success.
    Traceback (most recent call last):
      File "/tmp/ipykernel_9802/2529855525.py", line 52, in capture_exception
        yield None
      File "/tmp/ipykernel_9802/3344146337.py", line 9, in <module>
        raise Exception("The output of this exception will appear in the log. Great success.")
    Exception: The output of this exception will appear in the log. Great success.
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-26
      • 2014-11-30
      • 1970-01-01
      • 2023-03-23
      • 2015-05-29
      • 1970-01-01
      • 2020-06-07
      相关资源
      最近更新 更多