commit 7d8bcbd5bf8a6a31c78f9d8e4e51b19595209754 Author: Peter J. Holzer Date: Sat Dec 3 12:19:00 2022 +0100 Implement TimeoutSwitchingFileHandler diff --git a/README.md b/README.md new file mode 100644 index 0000000..a988607 --- /dev/null +++ b/README.md @@ -0,0 +1,13 @@ +TimeoutSwitchingFileHandler +=========================== + +This is a logging file handler for the Python logging system. +It automatically closes the log file after a period of inactivity +(or after the file has been open for some time whichever comes first) +and then opens a new file at the next emit. + +This is useful for long-running processes where short periods of +activity alternate with periods of inactivity. Log switchs will +typically occur during inactivity, so each log file will include one +complete active period. Also, since the log files are closed, they can +be safely compressed or removed. diff --git a/timeoutswitchingfilehandler.py b/timeoutswitchingfilehandler.py new file mode 100644 index 0000000..03cee4c --- /dev/null +++ b/timeoutswitchingfilehandler.py @@ -0,0 +1,42 @@ +import logging +import threading +import time + +class TimeoutSwitchingFileHandler(logging.Handler): + def __init__(self, filename, min_timeout=60, max_timeout=3600): + super().__init__() + self.basename = filename + self.min_timeout = min_timeout + self.max_timeout = max_timeout + self.fh = None + self.last_emit = 0 + self.first_emit = 0 + self.last_emit = 0 + cleanup = threading.Thread(target=self.cleanup_loop, daemon=True) + cleanup.start() + + def emit(self, record): + msg = self.format(record) + now = time.time() + if not self.fh: + now_tm = time.localtime(now) + filename = self.basename + time.strftime("%Y-%m-%d-%H-%M-%S", now_tm) + "-%06d" % (now % 1 * 1000000) + ".log" + self.fh = open(filename, "a") + self.first_emit = now + self.fh.write(msg) + self.fh.write("\n") + self.fh.flush() + self.last_emit = now + + def cleanup_loop(self): + while True: + time.sleep(1) + self.acquire() + now = time.time() + if self.fh and now - self.last_emit > self.min_timeout: + self.fh.close() + self.fh = None + if self.fh and now - self.first_emit > self.max_timeout: + self.fh.close() + self.fh = None + self.release()