This repository has been archived by the owner on Apr 26, 2024. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a PeriodicallyFlushingMemoryHandler to prevent logging silence (#…
…10407) Signed-off-by: Olivier Wilkinson (reivilibre) <olivier@librepush.net>
- Loading branch information
1 parent
1394467
commit e16eab2
Showing
4 changed files
with
97 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Add a buffered logging handler which periodically flushes itself. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
import logging | ||
import time | ||
from logging import Handler, LogRecord | ||
from logging.handlers import MemoryHandler | ||
from threading import Thread | ||
from typing import Optional | ||
|
||
from twisted.internet.interfaces import IReactorCore | ||
|
||
|
||
class PeriodicallyFlushingMemoryHandler(MemoryHandler): | ||
""" | ||
This is a subclass of MemoryHandler that additionally spawns a background | ||
thread to periodically flush the buffer. | ||
This prevents messages from being buffered for too long. | ||
Additionally, all messages will be immediately flushed if the reactor has | ||
not yet been started. | ||
""" | ||
|
||
def __init__( | ||
self, | ||
capacity: int, | ||
flushLevel: int = logging.ERROR, | ||
target: Optional[Handler] = None, | ||
flushOnClose: bool = True, | ||
period: float = 5.0, | ||
reactor: Optional[IReactorCore] = None, | ||
) -> None: | ||
""" | ||
period: the period between automatic flushes | ||
reactor: if specified, a custom reactor to use. If not specifies, | ||
defaults to the globally-installed reactor. | ||
Log entries will be flushed immediately until this reactor has | ||
started. | ||
""" | ||
super().__init__(capacity, flushLevel, target, flushOnClose) | ||
|
||
self._flush_period: float = period | ||
self._active: bool = True | ||
self._reactor_started = False | ||
|
||
self._flushing_thread: Thread = Thread( | ||
name="PeriodicallyFlushingMemoryHandler flushing thread", | ||
target=self._flush_periodically, | ||
) | ||
self._flushing_thread.start() | ||
|
||
def on_reactor_running(): | ||
self._reactor_started = True | ||
|
||
reactor_to_use: IReactorCore | ||
if reactor is None: | ||
from twisted.internet import reactor as global_reactor | ||
|
||
reactor_to_use = global_reactor # type: ignore[assignment] | ||
else: | ||
reactor_to_use = reactor | ||
|
||
# call our hook when the reactor start up | ||
reactor_to_use.callWhenRunning(on_reactor_running) | ||
|
||
def shouldFlush(self, record: LogRecord) -> bool: | ||
""" | ||
Before reactor start-up, log everything immediately. | ||
Otherwise, fall back to original behaviour of waiting for the buffer to fill. | ||
""" | ||
|
||
if self._reactor_started: | ||
return super().shouldFlush(record) | ||
else: | ||
return True | ||
|
||
def _flush_periodically(self): | ||
""" | ||
Whilst this handler is active, flush the handler periodically. | ||
""" | ||
|
||
while self._active: | ||
# flush is thread-safe; it acquires and releases the lock internally | ||
self.flush() | ||
time.sleep(self._flush_period) | ||
|
||
def close(self) -> None: | ||
self._active = False | ||
super().close() |