【问题标题】:Logging handleError() not intercepting exception记录handleError()不拦截异常
【发布时间】:2021-03-19 23:06:01
【问题描述】:

我创建了一个自定义 Python 日志记录处理程序,其中我覆盖了 handleError() 方法。为了测试它,我删除了包含日志文件的目录以强制记录错误。下次我尝试使用记录器记录某些内容时,我希望这会触发对 handleError() 的调用。然而,这并没有发生。异常直接传递给我的方法进行日志记录。

最小可重现示例:

import logging
import os
import shutil

from logging.handlers import WatchedFileHandler
from pathlib import Path

class MySpecialWatchedFileHandler(WatchedFileHandler):
    def handleError(self, record):
        print("******************** HANDLING AN ERROR!!!! *********************")
        super().handleError(record)

print("************** Start ************")
test_logger = logging.getLogger(__name__)
test_logger.handlers = []
log_directory = "/tmp/test_logdir"
Path(log_directory).mkdir(parents=True, exist_ok=True)
handler = MySpecialWatchedFileHandler(os.path.join(log_directory, "test_logfile.log"), delay=False)
test_logger.addHandler(handler)
test_logger.setLevel(logging.INFO)

# Force a failure by deleting the logging directory, so that it does not auto-recover.
shutil.rmtree(log_directory, ignore_errors=False)

test_logger.info("This is a test")
print("************** End ************")

结果输出:

/home/joe/percipient/mirage-backend-django/venv/bin/python /home/joe/.config/JetBrains/PyCharm2020.3/scratches/scratch.py
************** Start ************
Traceback (most recent call last):
  File "/home/joe/.config/JetBrains/PyCharm2020.3/scratches/scratch.py", line 25, in <module>
    test_logger.info("This is a test")
  File "/usr/lib/python3.6/logging/__init__.py", line 1308, in info
    self._log(INFO, msg, args, **kwargs)
  File "/usr/lib/python3.6/logging/__init__.py", line 1444, in _log
    self.handle(record)
  File "/usr/lib/python3.6/logging/__init__.py", line 1454, in handle
    self.callHandlers(record)
  File "/usr/lib/python3.6/logging/__init__.py", line 1516, in callHandlers
    hdlr.handle(record)
  File "/usr/lib/python3.6/logging/__init__.py", line 865, in handle
    self.emit(record)
  File "/usr/lib/python3.6/logging/handlers.py", line 481, in emit
    self.reopenIfNeeded()
  File "/usr/lib/python3.6/logging/handlers.py", line 471, in reopenIfNeeded
    self.stream = self._open()
  File "/usr/lib/python3.6/logging/__init__.py", line 1061, in _open
    return open(self.baseFilename, self.mode, encoding=self.encoding)
FileNotFoundError: [Errno 2] No such file or directory: '/tmp/test_logdir/test_logfile.log'

Process finished with exit code 1

请注意,******************** HANDLING AN ERROR!!!! ********************* 永远不会被打印出来。

为什么我的处理程序没有捕捉到那个异常?

不确定这是否重要,但我已经尝试将logging.raiseExceptions 设置为TrueFalse,并在处理程序中同时设置TrueFalse 的延迟,以及行为两种选择都一样。

【问题讨论】:

  • 你试过try and except?
  • @llamaking136 是的,我有,当然会捕获异常,但我担心并非所有日志记录错误都一定会表现为异常,我想测试实际的官方错误处理机制。
  • 您是否在文件处理程序上设置了delay 参数?我无法重现您的错误。请提供一个最小的可重现示例。 stackoverflow.com/help/minimal-reproducible-example
  • @blues 谢谢。我已经尝试将delay 设置为TrueFalse,但这并没有什么不同。我已将我的问题更新为一个最小的可重现示例,以及生成的输出。我在一个临时文件中运行了它。

标签: python python-logging


【解决方案1】:

这就是我最终解决它的方法。在我的自定义处理程序中,我覆盖了 emit() 方法,在调用超类的 emit() 方法时使用了我自己的 try/except ,因此它现在可以在正常打开日志时捕获异常并将其路由到我的 handleError()方法。

class MySpecialWatchedFileHandler(WatchedFileHandler):
    def handleError(self, record):
        print("****** HANDLING AN ERROR!!!! *****")
        #
        # Do what I need to do to handle the error here
        #

    def emit(self, record):
        try:
            super().emit(record)
        except Exception:
            record.exc_info = sys.exc_info()
            self.handleError(record)

【讨论】:

    【解决方案2】:

    这是python中的一个“错误”。基本上 WatchedFileHandler 上的 reopenIfNeeded() 方法没有包装到捕获所有其他日志记录错误的 try/except 块中。根据目标的不同,有两种可能的改变来完成这项工作:

    1.) 只删除日志文件,而不是包含它的目录。 WatchedFileHandler 将能够检测到这一点并通过重新创建文件来恢复

    2.) 使用常规的FileHandler 而不是 WatchedFileHandler。不会尝试重新创建丢失的文件,错误将按预期由handleError 处理

    【讨论】:

    • 谢谢@blues。我只是在查看源代码并得出相同的结论。关于你的建议:1)我试过这个。它只是重新创建文件而不触发错误处理程序。我需要触发错误处理程序,因为我正在尝试测试我的自定义错误处理程序。 2) 对于我们的实现,我们需要 WatchedFileHandler。您是否知道任何其他触发日志记录错误的方法,这些错误不会在reopenIfNeeded() 中导致异常,但会在 try/except 块内触发?
    • 1.) 是 WatchedFileHandler 的目的。如果你需要一个错误来进行测试,你应该模拟发射。没有理由这样乱来
    • “WatchedFileHandler 的目的是什么”是什么意思?
    • 好吧,WatchedFileHandler 的目的是在日志文件被删除时重新创建日志文件,而不是抛出错误
    • 是的,如果您删除日志文件,这就是它的作用,这很好。问题是如果您删除包含日志文件的 DIRECTORY。无论如何都会引发错误。
    【解决方案3】:

    您可以通过sys.exc_info 访问异常,如下所示:

    import sys
    import logging
    
    
    def handleLoggingError(record):
        print(f"exc_info: {sys.exc_info()}")
    
    
    hdlr = logging.StreamHandler(sys.stdout)
    hdlr.handleError = handleLoggingError
    logger = logging.getLogger(__name__)
    logger.addHandler(hdlr)
    

    【讨论】:

      猜你喜欢
      • 2014-10-23
      • 1970-01-01
      • 1970-01-01
      • 2011-05-13
      • 1970-01-01
      • 2018-05-13
      • 1970-01-01
      • 1970-01-01
      • 2010-09-18
      相关资源
      最近更新 更多