[SEBA-314] Refactoring multistructlog
- Redid the way loggers are created to use vanilla structlog
- This fixes the incorrect line numbering and similar in SEBA-314
- Fix/rewrite unit tests
- Apply pep8 formatting
- Make compatible with Python 3.x, enable multi-version testing with tox
- Removed XOS specific code
Change-Id: I615bc6f32ba2592475e3a8cf22850c563001a3d4
diff --git a/tests/test_logger.py b/tests/test_logger.py
deleted file mode 100644
index 9e40ca7..0000000
--- a/tests/test_logger.py
+++ /dev/null
@@ -1,79 +0,0 @@
-
-# Copyright 2017-present Open Networking Foundation
-#
-# 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 unittest
-import mock
-import pdb
-import os, sys
-import multistructlog
-import logging
-
-class MockLogging:
- def debug(self, str):
- return str
-
-class TestMultiStructLog(unittest.TestCase):
- def setUp(self):
- self.logging_config= {
- 'version': 1,
- 'handlers': {
- 'default': {
- 'class': 'logging.StreamHandler',
- },
- },
-
- 'loggers': {
- '': {
- 'handlers': ['default'],
- 'level': 'INFO',
- 'propagate': True
- },
- }
- }
-
- self.config = self.logging_config
-
- @mock.patch('multistructlog.logging')
- def test_reload(self, mock_logging):
- logger = multistructlog.create_logger({'version':1, 'foo':'bar'})
- logger0 = multistructlog.create_logger({'version':1, 'foo':'bar'})
- logger2 = multistructlog.create_logger({'version':1, 'foo':'notbar'})
- self.assertEqual(logger, logger0)
- self.assertNotEqual(logger,logger2)
-
- # "Starting" is only printed once
- self.assertEqual(mock_logging.StreamHandler.call_count, 2)
-
- @mock.patch('multistructlog.logging')
- def test_level(self, mock_logging):
- logger = multistructlog.create_logger({'version':1, 'foo':'x'})
- logger.info('Test 1')
- logger.debug('Test 2')
-
- # Default level is INFO
- self.assertEqual(mock_logging.StreamHandler.call_count, 1)
-
- @mock.patch('multistructlog.logging')
- def test_override_level(self, mock_logging):
- logger = multistructlog.create_logger(self.config, level='DEBUG')
-
- logger.info('Test 1')
- logger.debug('Test 2')
-
- self.assertEqual(mock_logging.StreamHandler.call_count, 2)
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/tests/test_multistructlog.py b/tests/test_multistructlog.py
new file mode 100644
index 0000000..2e7171a
--- /dev/null
+++ b/tests/test_multistructlog.py
@@ -0,0 +1,133 @@
+# Copyright 2017-present Open Networking Foundation
+#
+# 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 multistructlog
+import os
+import logging
+import logging.config
+import shutil
+import json
+import unittest
+import structlog
+
+# test directory, cleared and left around after testing
+test_scratch = "msl_test_scratch"
+
+
+class TestMultiStructLog(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(clsG):
+ # delete and recreate test_scratch directory if it exists
+ if os.path.isdir(test_scratch):
+ shutil.rmtree(test_scratch)
+
+ os.mkdir(test_scratch)
+
+ def tearDown(self):
+ structlog.reset_defaults()
+
+ def test_reload(self):
+ '''
+ Test that creating creatinging multiple identical loggers will reuse
+ existing loggers
+ '''
+
+ logger0 = multistructlog.create_logger({'version': 1, 'foo': 'bar'})
+ logger1 = multistructlog.create_logger({'version': 1, 'foo': 'bar'})
+ logger2 = multistructlog.create_logger()
+
+ self.assertEqual(logger0, logger1)
+ self.assertNotEqual(logger0, logger2)
+
+ def test_different_formatters(self):
+ '''
+ Test different formattters and levels to different output streams
+ NOTE: Only one test as logger has global state that is hard to reset
+ between tests without breaking other things.
+ '''
+
+ f1 = os.path.join(test_scratch, 'different_formatters_test_file1')
+ f2 = os.path.join(test_scratch, 'different_formatters_test_file2.json')
+
+ logging_config = {
+ 'version': 1,
+ 'handlers': {
+ 'file1': {
+ 'class': 'logging.handlers.RotatingFileHandler',
+ 'level': 'WARNING',
+ 'formatter': 'structured',
+ 'filename': f1,
+ },
+ 'file2': {
+ 'class': 'logging.handlers.RotatingFileHandler',
+ 'level': 'INFO',
+ 'formatter': 'json',
+ 'filename': f2,
+ },
+ },
+ 'formatters': {
+ 'json': {
+ '()': structlog.stdlib.ProcessorFormatter,
+ "processor": structlog.processors.JSONRenderer(),
+ },
+ 'structured': {
+ '()': structlog.stdlib.ProcessorFormatter,
+ 'processor': structlog.dev.ConsoleRenderer(colors=False),
+ },
+ },
+ 'loggers': {
+ '': {
+ 'handlers': ['file1', 'file2'],
+ 'level': 'WARNING',
+ 'propagate': True
+ },
+ }
+ }
+
+ # reset level to debug, overriding 'loggers' directive above
+ logger = multistructlog.create_logger(logging_config,
+ level=logging.DEBUG)
+
+ extra_data = {'number': 42}
+
+ logger.warning("should be in both files", extra=extra_data)
+
+ # filtered by file1 handler
+ logger.info("should only be in file2")
+
+ # filtered by both handlers, but not by loggers
+ logger.debug("should not be in either file")
+
+ # test new trace level
+ logger.trace("testing trace, shouldn't be in either file")
+
+ # check contents of file1
+ with open(f1) as f1fh:
+ f1_desired = '''should be in both files extra={'number': 42}
+'''
+ self.assertEqual(f1fh.read(), f1_desired)
+
+ # check contents of file2
+ f2_read = []
+ f2_desired = [
+ {"event": "should be in both files", "extra": {"number": 42}},
+ {"event": "should only be in file2"},
+ ]
+
+ with open(f2) as f2fh:
+ for line in f2fh:
+ f2_read.append(json.loads(line))
+
+ self.assertEqual(f2_read, f2_desired)