记录 handleError() 不拦截异常
Logging handleError() not intercepting exception
我创建了一个自定义 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
设置为 True
和 False
,并将延迟设置为 True
和False
在处理程序中,两种方式的行为都相同。
这是 python 中的一个“错误”。基本上,WatchedFileHandler 上的 reopenIfNeeded()
方法没有包装到捕获所有其他日志记录错误的 try/except 块中。根据目标的不同,有两种可能的更改可以使这项工作生效:
1.) 只删除日志文件,不删除包含它的目录。 WatchedFileHandler 将能够检测到这一点并通过重新创建文件来恢复
2.) 使用常规 FileHandler
而不是 WatchedFileHandler。不会尝试重新创建丢失的文件,错误将按预期由 handleError
处理
这是我最终解决的方法。在我的自定义处理程序中,我覆盖了 emit() 方法,用我自己的 try/except 围绕调用 super class 的 emit() 方法,这样它现在可以在打开日志时捕获异常并将其路由到我的 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)
您可以像这样通过 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)
我创建了一个自定义 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
设置为 True
和 False
,并将延迟设置为 True
和False
在处理程序中,两种方式的行为都相同。
这是 python 中的一个“错误”。基本上,WatchedFileHandler 上的 reopenIfNeeded()
方法没有包装到捕获所有其他日志记录错误的 try/except 块中。根据目标的不同,有两种可能的更改可以使这项工作生效:
1.) 只删除日志文件,不删除包含它的目录。 WatchedFileHandler 将能够检测到这一点并通过重新创建文件来恢复
2.) 使用常规 FileHandler
而不是 WatchedFileHandler。不会尝试重新创建丢失的文件,错误将按预期由 handleError
处理
这是我最终解决的方法。在我的自定义处理程序中,我覆盖了 emit() 方法,用我自己的 try/except 围绕调用 super class 的 emit() 方法,这样它现在可以在打开日志时捕获异常并将其路由到我的 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)
您可以像这样通过 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)