test_structured.py 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. # -*- coding: utf-8 -*-
  2. # Copyright 2019 The Matrix.org Foundation C.I.C.
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. import os
  16. import os.path
  17. import shutil
  18. import sys
  19. import textwrap
  20. from twisted.logger import Logger, eventAsText, eventsFromJSONLogFile
  21. from synapse.config.logger import setup_logging
  22. from synapse.logging._structured import setup_structured_logging
  23. from synapse.logging.context import LoggingContext
  24. from tests.unittest import DEBUG, HomeserverTestCase
  25. class FakeBeginner(object):
  26. def beginLoggingTo(self, observers, **kwargs):
  27. self.observers = observers
  28. class StructuredLoggingTestCase(HomeserverTestCase):
  29. """
  30. Tests for Synapse's structured logging support.
  31. """
  32. def test_output_to_json_round_trip(self):
  33. """
  34. Synapse logs can be outputted to JSON and then read back again.
  35. """
  36. temp_dir = self.mktemp()
  37. os.mkdir(temp_dir)
  38. self.addCleanup(shutil.rmtree, temp_dir)
  39. json_log_file = os.path.abspath(os.path.join(temp_dir, "out.json"))
  40. log_config = {
  41. "drains": {"jsonfile": {"type": "file_json", "location": json_log_file}}
  42. }
  43. # Begin the logger with our config
  44. beginner = FakeBeginner()
  45. setup_structured_logging(
  46. self.hs, self.hs.config, log_config, logBeginner=beginner
  47. )
  48. # Make a logger and send an event
  49. logger = Logger(
  50. namespace="tests.logging.test_structured", observer=beginner.observers[0]
  51. )
  52. logger.info("Hello there, {name}!", name="wally")
  53. # Read the log file and check it has the event we sent
  54. with open(json_log_file, "r") as f:
  55. logged_events = list(eventsFromJSONLogFile(f))
  56. self.assertEqual(len(logged_events), 1)
  57. # The event pulled from the file should render fine
  58. self.assertEqual(
  59. eventAsText(logged_events[0], includeTimestamp=False),
  60. "[tests.logging.test_structured#info] Hello there, wally!",
  61. )
  62. def test_output_to_text(self):
  63. """
  64. Synapse logs can be outputted to text.
  65. """
  66. temp_dir = self.mktemp()
  67. os.mkdir(temp_dir)
  68. self.addCleanup(shutil.rmtree, temp_dir)
  69. log_file = os.path.abspath(os.path.join(temp_dir, "out.log"))
  70. log_config = {"drains": {"file": {"type": "file", "location": log_file}}}
  71. # Begin the logger with our config
  72. beginner = FakeBeginner()
  73. setup_structured_logging(
  74. self.hs, self.hs.config, log_config, logBeginner=beginner
  75. )
  76. # Make a logger and send an event
  77. logger = Logger(
  78. namespace="tests.logging.test_structured", observer=beginner.observers[0]
  79. )
  80. logger.info("Hello there, {name}!", name="wally")
  81. # Read the log file and check it has the event we sent
  82. with open(log_file, "r") as f:
  83. logged_events = f.read().strip().split("\n")
  84. self.assertEqual(len(logged_events), 1)
  85. # The event pulled from the file should render fine
  86. self.assertTrue(
  87. logged_events[0].endswith(
  88. " - tests.logging.test_structured - INFO - None - Hello there, wally!"
  89. )
  90. )
  91. def test_collects_logcontext(self):
  92. """
  93. Test that log outputs have the attached logging context.
  94. """
  95. log_config = {"drains": {}}
  96. # Begin the logger with our config
  97. beginner = FakeBeginner()
  98. publisher = setup_structured_logging(
  99. self.hs, self.hs.config, log_config, logBeginner=beginner
  100. )
  101. logs = []
  102. publisher.addObserver(logs.append)
  103. # Make a logger and send an event
  104. logger = Logger(
  105. namespace="tests.logging.test_structured", observer=beginner.observers[0]
  106. )
  107. with LoggingContext("testcontext", request="somereq"):
  108. logger.info("Hello there, {name}!", name="steve")
  109. self.assertEqual(len(logs), 1)
  110. self.assertEqual(logs[0]["request"], "somereq")
  111. class StructuredLoggingConfigurationFileTestCase(HomeserverTestCase):
  112. def make_homeserver(self, reactor, clock):
  113. tempdir = self.mktemp()
  114. os.mkdir(tempdir)
  115. log_config_file = os.path.abspath(os.path.join(tempdir, "log.config.yaml"))
  116. self.homeserver_log = os.path.abspath(os.path.join(tempdir, "homeserver.log"))
  117. config = self.default_config()
  118. config["log_config"] = log_config_file
  119. with open(log_config_file, "w") as f:
  120. f.write(
  121. textwrap.dedent(
  122. """\
  123. structured: true
  124. drains:
  125. file:
  126. type: file_json
  127. location: %s
  128. """
  129. % (self.homeserver_log,)
  130. )
  131. )
  132. self.addCleanup(self._sys_cleanup)
  133. return self.setup_test_homeserver(config=config)
  134. def _sys_cleanup(self):
  135. sys.stdout = sys.__stdout__
  136. sys.stderr = sys.__stderr__
  137. # Do not remove! We need the logging system to be set other than WARNING.
  138. @DEBUG
  139. def test_log_output(self):
  140. """
  141. When a structured logging config is given, Synapse will use it.
  142. """
  143. setup_logging(self.hs, self.hs.config)
  144. # Make a logger and send an event
  145. logger = Logger(namespace="tests.logging.test_structured")
  146. with LoggingContext("testcontext", request="somereq"):
  147. logger.info("Hello there, {name}!", name="steve")
  148. with open(self.homeserver_log, "r") as f:
  149. logged_events = [
  150. eventAsText(x, includeTimestamp=False) for x in eventsFromJSONLogFile(f)
  151. ]
  152. logs = "\n".join(logged_events)
  153. self.assertTrue("***** STARTING SERVER *****" in logs)
  154. self.assertTrue("Hello there, steve!" in logs)