test_terse_json.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  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 json
  16. import logging
  17. from io import StringIO
  18. from synapse.logging._terse_json import JsonFormatter, TerseJsonFormatter
  19. from synapse.logging.context import LoggingContext, LoggingContextFilter
  20. from tests.logging import LoggerCleanupMixin
  21. from tests.unittest import TestCase
  22. class TerseJsonTestCase(LoggerCleanupMixin, TestCase):
  23. def setUp(self):
  24. self.output = StringIO()
  25. def get_log_line(self):
  26. # One log message, with a single trailing newline.
  27. data = self.output.getvalue()
  28. logs = data.splitlines()
  29. self.assertEqual(len(logs), 1)
  30. self.assertEqual(data.count("\n"), 1)
  31. return json.loads(logs[0])
  32. def test_terse_json_output(self):
  33. """
  34. The Terse JSON formatter converts log messages to JSON.
  35. """
  36. handler = logging.StreamHandler(self.output)
  37. handler.setFormatter(TerseJsonFormatter())
  38. logger = self.get_logger(handler)
  39. logger.info("Hello there, %s!", "wally")
  40. log = self.get_log_line()
  41. # The terse logger should give us these keys.
  42. expected_log_keys = [
  43. "log",
  44. "time",
  45. "level",
  46. "namespace",
  47. ]
  48. self.assertCountEqual(log.keys(), expected_log_keys)
  49. self.assertEqual(log["log"], "Hello there, wally!")
  50. def test_extra_data(self):
  51. """
  52. Additional information can be included in the structured logging.
  53. """
  54. handler = logging.StreamHandler(self.output)
  55. handler.setFormatter(TerseJsonFormatter())
  56. logger = self.get_logger(handler)
  57. logger.info(
  58. "Hello there, %s!", "wally", extra={"foo": "bar", "int": 3, "bool": True}
  59. )
  60. log = self.get_log_line()
  61. # The terse logger should give us these keys.
  62. expected_log_keys = [
  63. "log",
  64. "time",
  65. "level",
  66. "namespace",
  67. # The additional keys given via extra.
  68. "foo",
  69. "int",
  70. "bool",
  71. ]
  72. self.assertCountEqual(log.keys(), expected_log_keys)
  73. # Check the values of the extra fields.
  74. self.assertEqual(log["foo"], "bar")
  75. self.assertEqual(log["int"], 3)
  76. self.assertIs(log["bool"], True)
  77. def test_json_output(self):
  78. """
  79. The Terse JSON formatter converts log messages to JSON.
  80. """
  81. handler = logging.StreamHandler(self.output)
  82. handler.setFormatter(JsonFormatter())
  83. logger = self.get_logger(handler)
  84. logger.info("Hello there, %s!", "wally")
  85. log = self.get_log_line()
  86. # The terse logger should give us these keys.
  87. expected_log_keys = [
  88. "log",
  89. "level",
  90. "namespace",
  91. ]
  92. self.assertCountEqual(log.keys(), expected_log_keys)
  93. self.assertEqual(log["log"], "Hello there, wally!")
  94. def test_with_context(self):
  95. """
  96. The logging context should be added to the JSON response.
  97. """
  98. handler = logging.StreamHandler(self.output)
  99. handler.setFormatter(JsonFormatter())
  100. handler.addFilter(LoggingContextFilter())
  101. logger = self.get_logger(handler)
  102. with LoggingContext(request="test"):
  103. logger.info("Hello there, %s!", "wally")
  104. log = self.get_log_line()
  105. # The terse logger should give us these keys.
  106. expected_log_keys = [
  107. "log",
  108. "level",
  109. "namespace",
  110. "request",
  111. ]
  112. self.assertCountEqual(log.keys(), expected_log_keys)
  113. self.assertEqual(log["log"], "Hello there, wally!")
  114. self.assertEqual(log["request"], "test")