Reputation: 3944
I created a custom Python logging handler, where I overrode the handleError() method. In order to test it, I deleted the directory containing the log file to force a logging error. I was expecting this to trigger a call to handleError() the next time I tried to log something with the logger. However, this did not happen. The exception was passed directly through to my method doing the logging.
Minimal Reproducible Example:
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 ************")
Resulting output:
/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
Note that ******************** HANDLING AN ERROR!!!! *********************
is never printed.
Why is my handler not catching that exception?
Not sure if it matters, but I've tried it with logging.raiseExceptions
set to both True
and False
, and also setting delay to both True
and False
in the handler, and the behavior is the same either way on both options.
Upvotes: 3
Views: 1450
Reputation: 5353
You can access the exception via sys.exc_info
like so:
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)
Upvotes: 0
Reputation: 3944
Here's how I finally solved it. In my custom handler, I overrode the emit() method, with my own try/except around the call the super class's emit() method, so that it now catches the exception while opening the log fine and routes it to my handleError() method.
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)
Upvotes: 2
Reputation: 5185
This is a "bug" in python. Basically the reopenIfNeeded()
method on the WatchedFileHandler is not wrapped into the try/except block that catches all other logging errors. There is two possible changes to make this work depending on what the goal is:
1.) only delete the log file, and not the directory that contains it. the WatchedFileHandler will be able to detect this and recover by recreating the file
2.) Use a regular FileHandler
instead of the WatchedFileHandler. no attempt to recreate the missing file will be made, and the error will be handled by handleError
as expected
Upvotes: 0