test_logger.py 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. # Copyright (c) Twisted Matrix Laboratories.
  2. # See LICENSE for details.
  3. """
  4. Test cases for L{twisted.logger._logger}.
  5. """
  6. from twisted.trial import unittest
  7. from .._levels import InvalidLogLevelError
  8. from .._levels import LogLevel
  9. from .._format import formatEvent
  10. from .._logger import Logger
  11. from .._global import globalLogPublisher
  12. class TestLogger(Logger):
  13. """
  14. L{Logger} with an overridden C{emit} method that keeps track of received
  15. events.
  16. """
  17. def emit(self, level, format=None, **kwargs):
  18. def observer(event):
  19. self.event = event
  20. globalLogPublisher.addObserver(observer)
  21. try:
  22. Logger.emit(self, level, format, **kwargs)
  23. finally:
  24. globalLogPublisher.removeObserver(observer)
  25. self.emitted = {
  26. "level": level,
  27. "format": format,
  28. "kwargs": kwargs,
  29. }
  30. class LogComposedObject(object):
  31. """
  32. A regular object, with a logger attached.
  33. """
  34. log = TestLogger()
  35. def __init__(self, state=None):
  36. self.state = state
  37. def __str__(self):
  38. return "<LogComposedObject {state}>".format(state=self.state)
  39. class LoggerTests(unittest.TestCase):
  40. """
  41. Tests for L{Logger}.
  42. """
  43. def test_repr(self):
  44. """
  45. repr() on Logger
  46. """
  47. namespace = "bleargh"
  48. log = Logger(namespace)
  49. self.assertEqual(repr(log), "<Logger {0}>".format(repr(namespace)))
  50. def test_namespaceDefault(self):
  51. """
  52. Default namespace is module name.
  53. """
  54. log = Logger()
  55. self.assertEqual(log.namespace, __name__)
  56. def test_namespaceAttribute(self):
  57. """
  58. Default namespace for classes using L{Logger} as a descriptor is the
  59. class name they were retrieved from.
  60. """
  61. obj = LogComposedObject()
  62. expectedNamespace = "{0}.{1}".format(
  63. obj.__module__,
  64. obj.__class__.__name__,
  65. )
  66. self.assertEqual(obj.log.namespace, expectedNamespace)
  67. self.assertEqual(LogComposedObject.log.namespace, expectedNamespace)
  68. self.assertIs(LogComposedObject.log.source, LogComposedObject)
  69. self.assertIs(obj.log.source, obj)
  70. self.assertIsNone(Logger().source)
  71. def test_descriptorObserver(self):
  72. """
  73. When used as a descriptor, the observer is propagated.
  74. """
  75. observed = []
  76. class MyObject(object):
  77. log = Logger(observer=observed.append)
  78. MyObject.log.info("hello")
  79. self.assertEqual(len(observed), 1)
  80. self.assertEqual(observed[0]['log_format'], "hello")
  81. def test_sourceAvailableForFormatting(self):
  82. """
  83. On instances that have a L{Logger} class attribute, the C{log_source}
  84. key is available to format strings.
  85. """
  86. obj = LogComposedObject("hello")
  87. log = obj.log
  88. log.error("Hello, {log_source}.")
  89. self.assertIn("log_source", log.event)
  90. self.assertEqual(log.event["log_source"], obj)
  91. stuff = formatEvent(log.event)
  92. self.assertIn("Hello, <LogComposedObject hello>.", stuff)
  93. def test_basicLogger(self):
  94. """
  95. Test that log levels and messages are emitted correctly for
  96. Logger.
  97. """
  98. log = TestLogger()
  99. for level in LogLevel.iterconstants():
  100. format = "This is a {level_name} message"
  101. message = format.format(level_name=level.name)
  102. logMethod = getattr(log, level.name)
  103. logMethod(format, junk=message, level_name=level.name)
  104. # Ensure that test_emit got called with expected arguments
  105. self.assertEqual(log.emitted["level"], level)
  106. self.assertEqual(log.emitted["format"], format)
  107. self.assertEqual(log.emitted["kwargs"]["junk"], message)
  108. self.assertTrue(hasattr(log, "event"), "No event observed.")
  109. self.assertEqual(log.event["log_format"], format)
  110. self.assertEqual(log.event["log_level"], level)
  111. self.assertEqual(log.event["log_namespace"], __name__)
  112. self.assertIsNone(log.event["log_source"])
  113. self.assertEqual(log.event["junk"], message)
  114. self.assertEqual(formatEvent(log.event), message)
  115. def test_sourceOnClass(self):
  116. """
  117. C{log_source} event key refers to the class.
  118. """
  119. def observer(event):
  120. self.assertEqual(event["log_source"], Thingo)
  121. class Thingo(object):
  122. log = TestLogger(observer=observer)
  123. Thingo.log.info()
  124. def test_sourceOnInstance(self):
  125. """
  126. C{log_source} event key refers to the instance.
  127. """
  128. def observer(event):
  129. self.assertEqual(event["log_source"], thingo)
  130. class Thingo(object):
  131. log = TestLogger(observer=observer)
  132. thingo = Thingo()
  133. thingo.log.info()
  134. def test_sourceUnbound(self):
  135. """
  136. C{log_source} event key is L{None}.
  137. """
  138. def observer(event):
  139. self.assertIsNone(event["log_source"])
  140. log = TestLogger(observer=observer)
  141. log.info()
  142. def test_defaultFailure(self):
  143. """
  144. Test that log.failure() emits the right data.
  145. """
  146. log = TestLogger()
  147. try:
  148. raise RuntimeError("baloney!")
  149. except RuntimeError:
  150. log.failure("Whoops")
  151. errors = self.flushLoggedErrors(RuntimeError)
  152. self.assertEqual(len(errors), 1)
  153. self.assertEqual(log.emitted["level"], LogLevel.critical)
  154. self.assertEqual(log.emitted["format"], "Whoops")
  155. def test_conflictingKwargs(self):
  156. """
  157. Make sure that kwargs conflicting with args don't pass through.
  158. """
  159. log = TestLogger()
  160. log.warn(
  161. u"*",
  162. log_format="#",
  163. log_level=LogLevel.error,
  164. log_namespace="*namespace*",
  165. log_source="*source*",
  166. )
  167. self.assertEqual(log.event["log_format"], u"*")
  168. self.assertEqual(log.event["log_level"], LogLevel.warn)
  169. self.assertEqual(log.event["log_namespace"], log.namespace)
  170. self.assertIsNone(log.event["log_source"])
  171. def test_logInvalidLogLevel(self):
  172. """
  173. Test passing in a bogus log level to C{emit()}.
  174. """
  175. log = TestLogger()
  176. log.emit("*bogus*")
  177. errors = self.flushLoggedErrors(InvalidLogLevelError)
  178. self.assertEqual(len(errors), 1)
  179. def test_trace(self):
  180. """
  181. Tracing keeps track of forwarding to the publisher.
  182. """
  183. def publisher(event):
  184. observer(event)
  185. def observer(event):
  186. self.assertEqual(event["log_trace"], [(log, publisher)])
  187. log = TestLogger(observer=publisher)
  188. log.info("Hello.", log_trace=[])