235 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			Python
		
	
	
			
		
		
	
	
			235 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			Python
		
	
	
| # -*- coding: utf-8 -*-
 | |
| # Copyright 2019 The Matrix.org Foundation C.I.C.
 | |
| #
 | |
| # Licensed under the Apache License, Version 2.0 (the "License");
 | |
| # you may not use this file except in compliance with the License.
 | |
| # You may obtain a copy of the License at
 | |
| #
 | |
| #     http://www.apache.org/licenses/LICENSE-2.0
 | |
| #
 | |
| # Unless required by applicable law or agreed to in writing, software
 | |
| # distributed under the License is distributed on an "AS IS" BASIS,
 | |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
| # See the License for the specific language governing permissions and
 | |
| # limitations under the License.
 | |
| 
 | |
| import json
 | |
| from collections import Counter
 | |
| 
 | |
| from twisted.logger import Logger
 | |
| 
 | |
| from synapse.logging._structured import setup_structured_logging
 | |
| 
 | |
| from tests.server import connect_client
 | |
| from tests.unittest import HomeserverTestCase
 | |
| 
 | |
| from .test_structured import FakeBeginner, StructuredLoggingTestBase
 | |
| 
 | |
| 
 | |
| class TerseJSONTCPTestCase(StructuredLoggingTestBase, HomeserverTestCase):
 | |
|     def test_log_output(self):
 | |
|         """
 | |
|         The Terse JSON outputter delivers simplified structured logs over TCP.
 | |
|         """
 | |
|         log_config = {
 | |
|             "drains": {
 | |
|                 "tersejson": {
 | |
|                     "type": "network_json_terse",
 | |
|                     "host": "127.0.0.1",
 | |
|                     "port": 8000,
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         # Begin the logger with our config
 | |
|         beginner = FakeBeginner()
 | |
|         setup_structured_logging(
 | |
|             self.hs, self.hs.config, log_config, logBeginner=beginner
 | |
|         )
 | |
| 
 | |
|         logger = Logger(
 | |
|             namespace="tests.logging.test_terse_json", observer=beginner.observers[0]
 | |
|         )
 | |
|         logger.info("Hello there, {name}!", name="wally")
 | |
| 
 | |
|         # Trigger the connection
 | |
|         self.pump()
 | |
| 
 | |
|         _, server = connect_client(self.reactor, 0)
 | |
| 
 | |
|         # Trigger data being sent
 | |
|         self.pump()
 | |
| 
 | |
|         # One log message, with a single trailing newline
 | |
|         logs = server.data.decode("utf8").splitlines()
 | |
|         self.assertEqual(len(logs), 1)
 | |
|         self.assertEqual(server.data.count(b"\n"), 1)
 | |
| 
 | |
|         log = json.loads(logs[0])
 | |
| 
 | |
|         # The terse logger should give us these keys.
 | |
|         expected_log_keys = [
 | |
|             "log",
 | |
|             "time",
 | |
|             "level",
 | |
|             "log_namespace",
 | |
|             "request",
 | |
|             "scope",
 | |
|             "server_name",
 | |
|             "name",
 | |
|         ]
 | |
|         self.assertEqual(set(log.keys()), set(expected_log_keys))
 | |
| 
 | |
|         # It contains the data we expect.
 | |
|         self.assertEqual(log["name"], "wally")
 | |
| 
 | |
|     def test_log_backpressure_debug(self):
 | |
|         """
 | |
|         When backpressure is hit, DEBUG logs will be shed.
 | |
|         """
 | |
|         log_config = {
 | |
|             "loggers": {"synapse": {"level": "DEBUG"}},
 | |
|             "drains": {
 | |
|                 "tersejson": {
 | |
|                     "type": "network_json_terse",
 | |
|                     "host": "127.0.0.1",
 | |
|                     "port": 8000,
 | |
|                     "maximum_buffer": 10,
 | |
|                 }
 | |
|             },
 | |
|         }
 | |
| 
 | |
|         # Begin the logger with our config
 | |
|         beginner = FakeBeginner()
 | |
|         setup_structured_logging(
 | |
|             self.hs,
 | |
|             self.hs.config,
 | |
|             log_config,
 | |
|             logBeginner=beginner,
 | |
|             redirect_stdlib_logging=False,
 | |
|         )
 | |
| 
 | |
|         logger = Logger(
 | |
|             namespace="synapse.logging.test_terse_json", observer=beginner.observers[0]
 | |
|         )
 | |
| 
 | |
|         # Send some debug messages
 | |
|         for i in range(0, 3):
 | |
|             logger.debug("debug %s" % (i,))
 | |
| 
 | |
|         # Send a bunch of useful messages
 | |
|         for i in range(0, 7):
 | |
|             logger.info("test message %s" % (i,))
 | |
| 
 | |
|         # The last debug message pushes it past the maximum buffer
 | |
|         logger.debug("too much debug")
 | |
| 
 | |
|         # Allow the reconnection
 | |
|         _, server = connect_client(self.reactor, 0)
 | |
|         self.pump()
 | |
| 
 | |
|         # Only the 7 infos made it through, the debugs were elided
 | |
|         logs = server.data.splitlines()
 | |
|         self.assertEqual(len(logs), 7)
 | |
| 
 | |
|     def test_log_backpressure_info(self):
 | |
|         """
 | |
|         When backpressure is hit, DEBUG and INFO logs will be shed.
 | |
|         """
 | |
|         log_config = {
 | |
|             "loggers": {"synapse": {"level": "DEBUG"}},
 | |
|             "drains": {
 | |
|                 "tersejson": {
 | |
|                     "type": "network_json_terse",
 | |
|                     "host": "127.0.0.1",
 | |
|                     "port": 8000,
 | |
|                     "maximum_buffer": 10,
 | |
|                 }
 | |
|             },
 | |
|         }
 | |
| 
 | |
|         # Begin the logger with our config
 | |
|         beginner = FakeBeginner()
 | |
|         setup_structured_logging(
 | |
|             self.hs,
 | |
|             self.hs.config,
 | |
|             log_config,
 | |
|             logBeginner=beginner,
 | |
|             redirect_stdlib_logging=False,
 | |
|         )
 | |
| 
 | |
|         logger = Logger(
 | |
|             namespace="synapse.logging.test_terse_json", observer=beginner.observers[0]
 | |
|         )
 | |
| 
 | |
|         # Send some debug messages
 | |
|         for i in range(0, 3):
 | |
|             logger.debug("debug %s" % (i,))
 | |
| 
 | |
|         # Send a bunch of useful messages
 | |
|         for i in range(0, 10):
 | |
|             logger.warn("test warn %s" % (i,))
 | |
| 
 | |
|         # Send a bunch of info messages
 | |
|         for i in range(0, 3):
 | |
|             logger.info("test message %s" % (i,))
 | |
| 
 | |
|         # The last debug message pushes it past the maximum buffer
 | |
|         logger.debug("too much debug")
 | |
| 
 | |
|         # Allow the reconnection
 | |
|         client, server = connect_client(self.reactor, 0)
 | |
|         self.pump()
 | |
| 
 | |
|         # The 10 warnings made it through, the debugs and infos were elided
 | |
|         logs = list(map(json.loads, server.data.decode("utf8").splitlines()))
 | |
|         self.assertEqual(len(logs), 10)
 | |
| 
 | |
|         self.assertEqual(Counter([x["level"] for x in logs]), {"WARN": 10})
 | |
| 
 | |
|     def test_log_backpressure_cut_middle(self):
 | |
|         """
 | |
|         When backpressure is hit, and no more DEBUG and INFOs cannot be culled,
 | |
|         it will cut the middle messages out.
 | |
|         """
 | |
|         log_config = {
 | |
|             "loggers": {"synapse": {"level": "DEBUG"}},
 | |
|             "drains": {
 | |
|                 "tersejson": {
 | |
|                     "type": "network_json_terse",
 | |
|                     "host": "127.0.0.1",
 | |
|                     "port": 8000,
 | |
|                     "maximum_buffer": 10,
 | |
|                 }
 | |
|             },
 | |
|         }
 | |
| 
 | |
|         # Begin the logger with our config
 | |
|         beginner = FakeBeginner()
 | |
|         setup_structured_logging(
 | |
|             self.hs,
 | |
|             self.hs.config,
 | |
|             log_config,
 | |
|             logBeginner=beginner,
 | |
|             redirect_stdlib_logging=False,
 | |
|         )
 | |
| 
 | |
|         logger = Logger(
 | |
|             namespace="synapse.logging.test_terse_json", observer=beginner.observers[0]
 | |
|         )
 | |
| 
 | |
|         # Send a bunch of useful messages
 | |
|         for i in range(0, 20):
 | |
|             logger.warn("test warn", num=i)
 | |
| 
 | |
|         # Allow the reconnection
 | |
|         client, server = connect_client(self.reactor, 0)
 | |
|         self.pump()
 | |
| 
 | |
|         # The first five and last five warnings made it through, the debugs and
 | |
|         # infos were elided
 | |
|         logs = list(map(json.loads, server.data.decode("utf8").splitlines()))
 | |
|         self.assertEqual(len(logs), 10)
 | |
|         self.assertEqual(Counter([x["level"] for x in logs]), {"WARN": 10})
 | |
|         self.assertEqual([0, 1, 2, 3, 4, 15, 16, 17, 18, 19], [x["num"] for x in logs])
 |