_observer.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  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 zope.interface import Interface, implementer
  8. from twisted.python.failure import Failure
  9. from ._logger import Logger
  10. OBSERVER_DISABLED = (
  11. "Temporarily disabling observer {observer} due to exception: {log_failure}"
  12. )
  13. class ILogObserver(Interface):
  14. """
  15. An observer which can handle log events.
  16. Unlike most interfaces within Twisted, an L{ILogObserver} I{must be
  17. thread-safe}. Log observers may be called indiscriminately from many
  18. different threads, as any thread may wish to log a message at any time.
  19. """
  20. def __call__(event):
  21. """
  22. Log an event.
  23. @type event: C{dict} with (native) C{str} keys.
  24. @param event: A dictionary with arbitrary keys as defined by the
  25. application emitting logging events, as well as keys added by the
  26. logging system. The logging system reserves the right to set any
  27. key beginning with the prefix C{"log_"}; applications should not
  28. use any key so named. Currently, the following keys are used by
  29. the logging system in some way, if they are present (they are all
  30. optional):
  31. - C{"log_format"}: a PEP-3101-style format string which draws
  32. upon the keys in the event as its values, used to format the
  33. event for human consumption.
  34. - C{"log_flattened"}: a dictionary mapping keys derived from
  35. the names and format values used in the C{"log_format"}
  36. string to their values. This is used to preserve some
  37. structured information for use with
  38. L{twisted.logger.extractField}.
  39. - C{"log_trace"}: A L{list} designed to capture information
  40. about which L{LogPublisher}s have observed the event.
  41. - C{"log_level"}: a L{log level
  42. <twisted.logger.LogLevel>} constant, indicating the
  43. importance of and audience for this event.
  44. - C{"log_namespace"}: a namespace for the emitter of the event,
  45. given as a unicode string.
  46. - C{"log_system"}: a string indicating the network event or
  47. method call which resulted in the message being logged.
  48. """
  49. @implementer(ILogObserver)
  50. class LogPublisher(object):
  51. """
  52. I{ILogObserver} that fans out events to other observers.
  53. Keeps track of a set of L{ILogObserver} objects and forwards
  54. events to each.
  55. """
  56. def __init__(self, *observers):
  57. self._observers = list(observers)
  58. self.log = Logger(observer=self)
  59. def addObserver(self, observer):
  60. """
  61. Registers an observer with this publisher.
  62. @param observer: An L{ILogObserver} to add.
  63. """
  64. if not callable(observer):
  65. raise TypeError("Observer is not callable: {0!r}".format(observer))
  66. if observer not in self._observers:
  67. self._observers.append(observer)
  68. def removeObserver(self, observer):
  69. """
  70. Unregisters an observer with this publisher.
  71. @param observer: An L{ILogObserver} to remove.
  72. """
  73. try:
  74. self._observers.remove(observer)
  75. except ValueError:
  76. pass
  77. def __call__(self, event):
  78. """
  79. Forward events to contained observers.
  80. """
  81. if "log_trace" in event:
  82. def trace(observer):
  83. """
  84. Add tracing information for an observer.
  85. @param observer: an observer being forwarded to
  86. @type observer: L{ILogObserver}
  87. """
  88. event["log_trace"].append((self, observer))
  89. else:
  90. trace = None
  91. brokenObservers = []
  92. for observer in self._observers:
  93. if trace is not None:
  94. trace(observer)
  95. try:
  96. observer(event)
  97. except Exception:
  98. brokenObservers.append((observer, Failure()))
  99. for brokenObserver, failure in brokenObservers:
  100. errorLogger = self._errorLoggerForObserver(brokenObserver)
  101. errorLogger.failure(
  102. OBSERVER_DISABLED,
  103. failure=failure,
  104. observer=brokenObserver,
  105. )
  106. def _errorLoggerForObserver(self, observer):
  107. """
  108. Create an error-logger based on this logger, which does not contain the
  109. given bad observer.
  110. @param observer: The observer which previously had an error.
  111. @type observer: L{ILogObserver}
  112. @return: L{None}
  113. """
  114. errorPublisher = LogPublisher(*[
  115. obs for obs in self._observers
  116. if obs is not observer
  117. ])
  118. return Logger(observer=errorPublisher)