blob: 88814edeb8132cee3e07dc22dbb567c49fe0e316 [file] [log] [blame]
Zack Williams41132132018-10-19 12:14:45 -07001# Copyright 2017-present Open Networking Foundation
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15import multistructlog
16import os
17import logging
18import logging.config
19import shutil
20import json
21import unittest
22import structlog
23
24# test directory, cleared and left around after testing
25test_scratch = "msl_test_scratch"
26
27
28class TestMultiStructLog(unittest.TestCase):
29
30 @classmethod
31 def setUpClass(clsG):
32 # delete and recreate test_scratch directory if it exists
33 if os.path.isdir(test_scratch):
34 shutil.rmtree(test_scratch)
35
36 os.mkdir(test_scratch)
37
38 def tearDown(self):
39 structlog.reset_defaults()
40
41 def test_reload(self):
42 '''
43 Test that creating creatinging multiple identical loggers will reuse
44 existing loggers
45 '''
46
47 logger0 = multistructlog.create_logger({'version': 1, 'foo': 'bar'})
48 logger1 = multistructlog.create_logger({'version': 1, 'foo': 'bar'})
49 logger2 = multistructlog.create_logger()
50
51 self.assertEqual(logger0, logger1)
52 self.assertNotEqual(logger0, logger2)
53
54 def test_different_formatters(self):
55 '''
56 Test different formattters and levels to different output streams
57 NOTE: Only one test as logger has global state that is hard to reset
58 between tests without breaking other things.
59 '''
60
61 f1 = os.path.join(test_scratch, 'different_formatters_test_file1')
62 f2 = os.path.join(test_scratch, 'different_formatters_test_file2.json')
63
64 logging_config = {
65 'version': 1,
66 'handlers': {
67 'file1': {
68 'class': 'logging.handlers.RotatingFileHandler',
69 'level': 'WARNING',
70 'formatter': 'structured',
71 'filename': f1,
72 },
73 'file2': {
74 'class': 'logging.handlers.RotatingFileHandler',
75 'level': 'INFO',
76 'formatter': 'json',
77 'filename': f2,
78 },
79 },
80 'formatters': {
81 'json': {
82 '()': structlog.stdlib.ProcessorFormatter,
83 "processor": structlog.processors.JSONRenderer(),
84 },
85 'structured': {
86 '()': structlog.stdlib.ProcessorFormatter,
87 'processor': structlog.dev.ConsoleRenderer(colors=False),
88 },
89 },
90 'loggers': {
91 '': {
92 'handlers': ['file1', 'file2'],
93 'level': 'WARNING',
94 'propagate': True
95 },
96 }
97 }
98
99 # reset level to debug, overriding 'loggers' directive above
100 logger = multistructlog.create_logger(logging_config,
101 level=logging.DEBUG)
102
103 extra_data = {'number': 42}
104
105 logger.warning("should be in both files", extra=extra_data)
106
107 # filtered by file1 handler
108 logger.info("should only be in file2")
109
110 # filtered by both handlers, but not by loggers
111 logger.debug("should not be in either file")
112
113 # test new trace level
114 logger.trace("testing trace, shouldn't be in either file")
115
116 # check contents of file1
117 with open(f1) as f1fh:
Zack Williams940ff792018-11-02 11:11:20 -0700118
119 # regex at start should roughly match ISO8601 datetime
120 f1_desired = r"[\dTZ\-\.\:]+ \[warning \] should be in both files extra={'number': 42}"
121 self.assertRegexpMatches(f1fh.read(), f1_desired)
Zack Williams41132132018-10-19 12:14:45 -0700122
123 # check contents of file2
124 f2_read = []
125 f2_desired = [
Zack Williams940ff792018-11-02 11:11:20 -0700126 {"event": "should be in both files", "extra": {"number": 42},
127 "level": "warning", "timestamp": "removed"},
128 {"event": "should only be in file2",
129 "level": "info", "timestamp": "removed"},
Zack Williams41132132018-10-19 12:14:45 -0700130 ]
131
132 with open(f2) as f2fh:
133 for line in f2fh:
Zack Williams940ff792018-11-02 11:11:20 -0700134 jl = json.loads(line)
135
136 # assert there is a timestamp, and remove it as it changes
137 self.assertTrue(True if "timestamp" in jl else False)
138 jl['timestamp'] = "removed"
139
140 f2_read.append(jl)
Zack Williams41132132018-10-19 12:14:45 -0700141
142 self.assertEqual(f2_read, f2_desired)