[output] add 'defer' option for logging files (#8523)
- defer file creation of error files by default - implement custom FileHandler class to support deferred directory creation
This commit is contained in:
@@ -9425,7 +9425,8 @@ Example
|
|||||||
"format" : "{asctime} {name}: {message}",
|
"format" : "{asctime} {name}: {message}",
|
||||||
"format-date": "%H:%M:%S",
|
"format-date": "%H:%M:%S",
|
||||||
"path" : "~/log.txt",
|
"path" : "~/log.txt",
|
||||||
"encoding" : "ascii"
|
"encoding" : "ascii",
|
||||||
|
"defer" : true
|
||||||
}
|
}
|
||||||
|
|
||||||
.. code:: json
|
.. code:: json
|
||||||
@@ -9474,9 +9475,15 @@ Description
|
|||||||
* encoding
|
* encoding
|
||||||
* File encoding
|
* File encoding
|
||||||
* Default: ``"utf-8"``
|
* Default: ``"utf-8"``
|
||||||
|
* defer
|
||||||
|
* Defer file opening/creation until writing the first logging message
|
||||||
|
* Default:
|
||||||
|
``true`` for `errorfile <output.errorfile_>`__,
|
||||||
|
``false`` otherwise
|
||||||
Note
|
Note
|
||||||
path, mode, and encoding are only applied when configuring
|
path, mode, encoding, and defer
|
||||||
logging output to a file.
|
are only applied when configuring logging output to a file.
|
||||||
|
(See `logging.FileHandler <https://docs.python.org/3/library/logging.handlers.html#filehandler>`__)
|
||||||
|
|
||||||
|
|
||||||
Postprocessor Configuration
|
Postprocessor Configuration
|
||||||
|
|||||||
@@ -291,7 +291,7 @@ def main():
|
|||||||
|
|
||||||
# error file logging handler
|
# error file logging handler
|
||||||
if handler := output.setup_logging_handler(
|
if handler := output.setup_logging_handler(
|
||||||
"errorfile", fmt="{message}", mode="a"):
|
"errorfile", fmt="{message}", mode="a", defer=True):
|
||||||
elog = input_manager.err = logging.getLogger("errorfile")
|
elog = input_manager.err = logging.getLogger("errorfile")
|
||||||
elog.addHandler(handler)
|
elog.addHandler(handler)
|
||||||
elog.propagate = False
|
elog.propagate = False
|
||||||
|
|||||||
@@ -176,6 +176,48 @@ class Formatter(logging.Formatter):
|
|||||||
return msg
|
return msg
|
||||||
|
|
||||||
|
|
||||||
|
class FileHandler(logging.StreamHandler):
|
||||||
|
def __init__(self, path, mode, encoding, delay=True):
|
||||||
|
self.path = path
|
||||||
|
self.mode = mode
|
||||||
|
self.errors = None
|
||||||
|
self.encoding = encoding
|
||||||
|
|
||||||
|
if delay:
|
||||||
|
logging.Handler.__init__(self)
|
||||||
|
self.stream = None
|
||||||
|
self.emit = self.emit_delayed
|
||||||
|
else:
|
||||||
|
logging.StreamHandler.__init__(self, self._open())
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
with self.lock:
|
||||||
|
try:
|
||||||
|
if self.stream:
|
||||||
|
try:
|
||||||
|
self.flush()
|
||||||
|
self.stream.close()
|
||||||
|
finally:
|
||||||
|
self.stream = None
|
||||||
|
finally:
|
||||||
|
logging.StreamHandler.close(self)
|
||||||
|
|
||||||
|
def _open(self):
|
||||||
|
try:
|
||||||
|
return open(self.path, self.mode,
|
||||||
|
encoding=self.encoding, errors=self.errors)
|
||||||
|
except FileNotFoundError:
|
||||||
|
os.makedirs(os.path.dirname(self.path))
|
||||||
|
return open(self.path, self.mode,
|
||||||
|
encoding=self.encoding, errors=self.errors)
|
||||||
|
|
||||||
|
def emit_delayed(self, record):
|
||||||
|
if self.mode != "w" or not self._closed:
|
||||||
|
self.stream = self._open()
|
||||||
|
self.emit = logging.StreamHandler.emit.__get__(self)
|
||||||
|
self.emit(record)
|
||||||
|
|
||||||
|
|
||||||
def initialize_logging(loglevel):
|
def initialize_logging(loglevel):
|
||||||
"""Setup basic logging functionality before configfiles have been loaded"""
|
"""Setup basic logging functionality before configfiles have been loaded"""
|
||||||
# convert levelnames to lowercase
|
# convert levelnames to lowercase
|
||||||
@@ -247,7 +289,8 @@ def configure_logging(loglevel):
|
|||||||
root.setLevel(minlevel)
|
root.setLevel(minlevel)
|
||||||
|
|
||||||
|
|
||||||
def setup_logging_handler(key, fmt=LOG_FORMAT, lvl=LOG_LEVEL, mode="w"):
|
def setup_logging_handler(key, fmt=LOG_FORMAT, lvl=LOG_LEVEL, mode="w",
|
||||||
|
defer=False):
|
||||||
"""Setup a new logging handler"""
|
"""Setup a new logging handler"""
|
||||||
opts = config.interpolate(("output",), key)
|
opts = config.interpolate(("output",), key)
|
||||||
if not opts:
|
if not opts:
|
||||||
@@ -258,12 +301,10 @@ def setup_logging_handler(key, fmt=LOG_FORMAT, lvl=LOG_LEVEL, mode="w"):
|
|||||||
path = opts.get("path")
|
path = opts.get("path")
|
||||||
mode = opts.get("mode", mode)
|
mode = opts.get("mode", mode)
|
||||||
encoding = opts.get("encoding", "utf-8")
|
encoding = opts.get("encoding", "utf-8")
|
||||||
|
delay = opts.get("defer", defer)
|
||||||
try:
|
try:
|
||||||
path = util.expand_path(path)
|
path = util.expand_path(path)
|
||||||
handler = logging.FileHandler(path, mode, encoding)
|
handler = FileHandler(path, mode, encoding, delay)
|
||||||
except FileNotFoundError:
|
|
||||||
os.makedirs(os.path.dirname(path))
|
|
||||||
handler = logging.FileHandler(path, mode, encoding)
|
|
||||||
except (OSError, ValueError) as exc:
|
except (OSError, ValueError) as exc:
|
||||||
logging.getLogger("gallery-dl").warning(
|
logging.getLogger("gallery-dl").warning(
|
||||||
"%s: %s", key, exc)
|
"%s: %s", key, exc)
|
||||||
|
|||||||
Reference in New Issue
Block a user