_observer.py 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. # -*- test-case-name: twisted.logger.test.test_observer -*-
  2. # Copyright (c) Twisted Matrix Laboratories.
  3. # See LICENSE for details.
  4. """
  5. Basic log observers.
  6. """
  7. from typing import Callable, Optional
  8. from zope.interface import implementer
  9. from twisted.python.failure import Failure
  10. from ._interfaces import ILogObserver, LogEvent
  11. from ._logger import Logger
  12. OBSERVER_DISABLED = (
  13. "Temporarily disabling observer {observer} due to exception: {log_failure}"
  14. )
  15. @implementer(ILogObserver)
  16. class LogPublisher:
  17. """
  18. I{ILogObserver} that fans out events to other observers.
  19. Keeps track of a set of L{ILogObserver} objects and forwards
  20. events to each.
  21. """
  22. def __init__(self, *observers: ILogObserver) -> None:
  23. self._observers = list(observers)
  24. self.log = Logger(observer=self)
  25. def addObserver(self, observer: ILogObserver) -> None:
  26. """
  27. Registers an observer with this publisher.
  28. @param observer: An L{ILogObserver} to add.
  29. """
  30. if not callable(observer):
  31. raise TypeError(f"Observer is not callable: {observer!r}")
  32. if observer not in self._observers:
  33. self._observers.append(observer)
  34. def removeObserver(self, observer: ILogObserver) -> None:
  35. """
  36. Unregisters an observer with this publisher.
  37. @param observer: An L{ILogObserver} to remove.
  38. """
  39. try:
  40. self._observers.remove(observer)
  41. except ValueError:
  42. pass
  43. def __call__(self, event: LogEvent) -> None:
  44. """
  45. Forward events to contained observers.
  46. """
  47. if "log_trace" not in event:
  48. trace: Optional[Callable[[ILogObserver], None]] = None
  49. else:
  50. def trace(observer: ILogObserver) -> None:
  51. """
  52. Add tracing information for an observer.
  53. @param observer: an observer being forwarded to
  54. """
  55. event["log_trace"].append((self, observer))
  56. brokenObservers = []
  57. for observer in self._observers:
  58. if trace is not None:
  59. trace(observer)
  60. try:
  61. observer(event)
  62. except Exception:
  63. brokenObservers.append((observer, Failure()))
  64. for brokenObserver, failure in brokenObservers:
  65. errorLogger = self._errorLoggerForObserver(brokenObserver)
  66. errorLogger.failure(
  67. OBSERVER_DISABLED,
  68. failure=failure,
  69. observer=brokenObserver,
  70. )
  71. def _errorLoggerForObserver(self, observer: ILogObserver) -> Logger:
  72. """
  73. Create an error-logger based on this logger, which does not contain the
  74. given bad observer.
  75. @param observer: The observer which previously had an error.
  76. @return: A L{Logger} without the given observer.
  77. """
  78. errorPublisher = LogPublisher(
  79. *(obs for obs in self._observers if obs is not observer)
  80. )
  81. return Logger(observer=errorPublisher)
  82. @implementer(ILogObserver)
  83. def bitbucketLogObserver(event: LogEvent) -> None:
  84. """
  85. I{ILogObserver} that does nothing with the events it sees.
  86. """