Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 
 
 
 

90 lignes
2.7 KiB

  1. import logging
  2. import time
  3. from logging import Handler, LogRecord
  4. from logging.handlers import MemoryHandler
  5. from threading import Thread
  6. from typing import Optional, cast
  7. from twisted.internet.interfaces import IReactorCore
  8. class PeriodicallyFlushingMemoryHandler(MemoryHandler):
  9. """
  10. This is a subclass of MemoryHandler that additionally spawns a background
  11. thread to periodically flush the buffer.
  12. This prevents messages from being buffered for too long.
  13. Additionally, all messages will be immediately flushed if the reactor has
  14. not yet been started.
  15. """
  16. def __init__(
  17. self,
  18. capacity: int,
  19. flushLevel: int = logging.ERROR,
  20. target: Optional[Handler] = None,
  21. flushOnClose: bool = True,
  22. period: float = 5.0,
  23. reactor: Optional[IReactorCore] = None,
  24. ) -> None:
  25. """
  26. period: the period between automatic flushes
  27. reactor: if specified, a custom reactor to use. If not specifies,
  28. defaults to the globally-installed reactor.
  29. Log entries will be flushed immediately until this reactor has
  30. started.
  31. """
  32. super().__init__(capacity, flushLevel, target, flushOnClose)
  33. self._flush_period: float = period
  34. self._active: bool = True
  35. self._reactor_started = False
  36. self._flushing_thread: Thread = Thread(
  37. name="PeriodicallyFlushingMemoryHandler flushing thread",
  38. target=self._flush_periodically,
  39. daemon=True,
  40. )
  41. self._flushing_thread.start()
  42. def on_reactor_running() -> None:
  43. self._reactor_started = True
  44. reactor_to_use: IReactorCore
  45. if reactor is None:
  46. from twisted.internet import reactor as global_reactor
  47. reactor_to_use = cast(IReactorCore, global_reactor)
  48. else:
  49. reactor_to_use = reactor
  50. # call our hook when the reactor start up
  51. reactor_to_use.callWhenRunning(on_reactor_running)
  52. def shouldFlush(self, record: LogRecord) -> bool:
  53. """
  54. Before reactor start-up, log everything immediately.
  55. Otherwise, fall back to original behaviour of waiting for the buffer to fill.
  56. """
  57. if self._reactor_started:
  58. return super().shouldFlush(record)
  59. else:
  60. return True
  61. def _flush_periodically(self) -> None:
  62. """
  63. Whilst this handler is active, flush the handler periodically.
  64. """
  65. while self._active:
  66. # flush is thread-safe; it acquires and releases the lock internally
  67. self.flush()
  68. time.sleep(self._flush_period)
  69. def close(self) -> None:
  70. self._active = False
  71. super().close()