Add a PeriodicallyFlushingMemoryHandler to prevent logging silence (#10407)
Signed-off-by: Olivier Wilkinson (reivilibre) <olivier@librepush.net>
This commit is contained in:
parent
13944678c3
commit
e16eab29d6
|
@ -0,0 +1 @@
|
||||||
|
Add a buffered logging handler which periodically flushes itself.
|
|
@ -28,7 +28,7 @@ handlers:
|
||||||
# will be a delay for INFO/DEBUG logs to get written, but WARNING/ERROR
|
# will be a delay for INFO/DEBUG logs to get written, but WARNING/ERROR
|
||||||
# logs will still be flushed immediately.
|
# logs will still be flushed immediately.
|
||||||
buffer:
|
buffer:
|
||||||
class: logging.handlers.MemoryHandler
|
class: synapse.logging.handlers.PeriodicallyFlushingMemoryHandler
|
||||||
target: file
|
target: file
|
||||||
# The capacity is the number of log lines that are buffered before
|
# The capacity is the number of log lines that are buffered before
|
||||||
# being written to disk. Increasing this will lead to better
|
# being written to disk. Increasing this will lead to better
|
||||||
|
@ -36,6 +36,9 @@ handlers:
|
||||||
# be written to disk.
|
# be written to disk.
|
||||||
capacity: 10
|
capacity: 10
|
||||||
flushLevel: 30 # Flush for WARNING logs as well
|
flushLevel: 30 # Flush for WARNING logs as well
|
||||||
|
# The period of time, in seconds, between forced flushes.
|
||||||
|
# Messages will not be delayed for longer than this time.
|
||||||
|
period: 5
|
||||||
|
|
||||||
# A handler that writes logs to stderr. Unused by default, but can be used
|
# A handler that writes logs to stderr. Unused by default, but can be used
|
||||||
# instead of "buffer" and "file" in the logger handlers.
|
# instead of "buffer" and "file" in the logger handlers.
|
||||||
|
|
|
@ -71,7 +71,7 @@ handlers:
|
||||||
# will be a delay for INFO/DEBUG logs to get written, but WARNING/ERROR
|
# will be a delay for INFO/DEBUG logs to get written, but WARNING/ERROR
|
||||||
# logs will still be flushed immediately.
|
# logs will still be flushed immediately.
|
||||||
buffer:
|
buffer:
|
||||||
class: logging.handlers.MemoryHandler
|
class: synapse.logging.handlers.PeriodicallyFlushingMemoryHandler
|
||||||
target: file
|
target: file
|
||||||
# The capacity is the number of log lines that are buffered before
|
# The capacity is the number of log lines that are buffered before
|
||||||
# being written to disk. Increasing this will lead to better
|
# being written to disk. Increasing this will lead to better
|
||||||
|
@ -79,6 +79,9 @@ handlers:
|
||||||
# be written to disk.
|
# be written to disk.
|
||||||
capacity: 10
|
capacity: 10
|
||||||
flushLevel: 30 # Flush for WARNING logs as well
|
flushLevel: 30 # Flush for WARNING logs as well
|
||||||
|
# The period of time, in seconds, between forced flushes.
|
||||||
|
# Messages will not be delayed for longer than this time.
|
||||||
|
period: 5
|
||||||
|
|
||||||
# A handler that writes logs to stderr. Unused by default, but can be used
|
# A handler that writes logs to stderr. Unused by default, but can be used
|
||||||
# instead of "buffer" and "file" in the logger handlers.
|
# instead of "buffer" and "file" in the logger handlers.
|
||||||
|
|
|
@ -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()
|
Loading…
Reference in New Issue