Merge lp:~sseman/juju-chaos-monkey/logging into lp:juju-chaos-monkey
- logging
- Merge into trunk
Proposed by
Seman
Status: | Merged |
---|---|
Merged at revision: | 3 |
Proposed branch: | lp:~sseman/juju-chaos-monkey/logging |
Merge into: | lp:juju-chaos-monkey |
Diff against target: |
289 lines (+115/-23) 5 files modified
chaos/kill.py (+6/-5) chaos/net.py (+14/-13) runner.py (+11/-1) tests/test_utility.py (+67/-1) utility.py (+17/-3) |
To merge this branch: | bzr merge lp:~sseman/juju-chaos-monkey/logging |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
John George (community) | Approve | ||
Review via email: mp+257917@code.launchpad.net |
Commit message
Description of the change
Added logging and timestamp.
To post a comment you must log in.
Revision history for this message
John George (jog) : | # |
Revision history for this message
Seman (sseman) wrote : | # |
Thank you John for the review. I removed log() and log_error(). Also, see inline comments.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'chaos/kill.py' | |||
2 | --- chaos/kill.py 2015-04-29 19:57:39 +0000 | |||
3 | +++ chaos/kill.py 2015-04-30 22:20:24 +0000 | |||
4 | @@ -1,9 +1,10 @@ | |||
5 | 1 | import logging | ||
6 | 2 | |||
7 | 1 | from chaos_monkey_base import ( | 3 | from chaos_monkey_base import ( |
8 | 2 | Chaos, | 4 | Chaos, |
9 | 3 | ChaosMonkeyBase, | 5 | ChaosMonkeyBase, |
10 | 4 | ) | 6 | ) |
11 | 5 | from utility import ( | 7 | from utility import ( |
12 | 6 | log, | ||
13 | 7 | NotFound, | 8 | NotFound, |
14 | 8 | run_shell_command, | 9 | run_shell_command, |
15 | 9 | ) | 10 | ) |
16 | @@ -29,20 +30,20 @@ | |||
17 | 29 | return pids.split(' ') | 30 | return pids.split(' ') |
18 | 30 | 31 | ||
19 | 31 | def kill_jujud(self, quiet_mode=True): | 32 | def kill_jujud(self, quiet_mode=True): |
21 | 32 | log("Kill.kill_jujud") | 33 | logging.info("Kill.kill_jujud") |
22 | 33 | pids = self.get_pids('jujud') | 34 | pids = self.get_pids('jujud') |
23 | 34 | if not pids: | 35 | if not pids: |
25 | 35 | log("Jujud process ID not found") | 36 | logging.error("Jujud process ID not found") |
26 | 36 | if not quiet_mode: | 37 | if not quiet_mode: |
27 | 37 | raise NotFound('Process id not found') | 38 | raise NotFound('Process id not found') |
28 | 38 | return | 39 | return |
29 | 39 | run_shell_command('kill -s SIGKILL ' + pids[0]) | 40 | run_shell_command('kill -s SIGKILL ' + pids[0]) |
30 | 40 | 41 | ||
31 | 41 | def kill_mongodb(self, quiet_mode=True): | 42 | def kill_mongodb(self, quiet_mode=True): |
33 | 42 | log("Kill.kill_mongod") | 43 | logging.info("Kill.kill_mongod") |
34 | 43 | pids = self.get_pids('mongod') | 44 | pids = self.get_pids('mongod') |
35 | 44 | if not pids: | 45 | if not pids: |
37 | 45 | log("MongoDB process ID not found") | 46 | logging.error("MongoDB process ID not found") |
38 | 46 | if not quiet_mode: | 47 | if not quiet_mode: |
39 | 47 | raise NotFound('Process id not found') | 48 | raise NotFound('Process id not found') |
40 | 48 | return | 49 | return |
41 | 49 | 50 | ||
42 | === modified file 'chaos/net.py' | |||
43 | --- chaos/net.py 2015-04-29 19:57:39 +0000 | |||
44 | +++ chaos/net.py 2015-04-30 22:20:24 +0000 | |||
45 | @@ -1,9 +1,10 @@ | |||
46 | 1 | import logging | ||
47 | 2 | |||
48 | 1 | from chaos_monkey_base import ( | 3 | from chaos_monkey_base import ( |
49 | 2 | Chaos, | 4 | Chaos, |
50 | 3 | ChaosMonkeyBase, | 5 | ChaosMonkeyBase, |
51 | 4 | ) | 6 | ) |
52 | 5 | from utility import ( | 7 | from utility import ( |
53 | 6 | log, | ||
54 | 7 | run_shell_command, | 8 | run_shell_command, |
55 | 8 | ) | 9 | ) |
56 | 9 | 10 | ||
57 | @@ -22,62 +23,62 @@ | |||
58 | 22 | return cls() | 23 | return cls() |
59 | 23 | 24 | ||
60 | 24 | def reset(self): | 25 | def reset(self): |
62 | 25 | log("Net.reset ") | 26 | logging.info("Net.reset ") |
63 | 26 | cmd = 'ufw reset' | 27 | cmd = 'ufw reset' |
64 | 27 | run_shell_command(cmd) | 28 | run_shell_command(cmd) |
65 | 28 | 29 | ||
66 | 29 | def default_deny(self): | 30 | def default_deny(self): |
68 | 30 | log("Net.default_deny") | 31 | logging.info("Net.default_deny") |
69 | 31 | cmd = "ufw default deny" | 32 | cmd = "ufw default deny" |
70 | 32 | run_shell_command(cmd) | 33 | run_shell_command(cmd) |
71 | 33 | 34 | ||
72 | 34 | def default_allow(self): | 35 | def default_allow(self): |
74 | 35 | log("Net.default_allow") | 36 | logging.info("Net.default_allow") |
75 | 36 | cmd = "ufw default allow" | 37 | cmd = "ufw default allow" |
76 | 37 | run_shell_command(cmd) | 38 | run_shell_command(cmd) |
77 | 38 | 39 | ||
78 | 39 | def allow_ssh(self): | 40 | def allow_ssh(self): |
80 | 40 | log("Net.allow_ssh") | 41 | logging.info("Net.allow_ssh") |
81 | 41 | cmd = 'ufw allow ssh' | 42 | cmd = 'ufw allow ssh' |
82 | 42 | run_shell_command(cmd) | 43 | run_shell_command(cmd) |
83 | 43 | 44 | ||
84 | 44 | def deny_ssh(self): | 45 | def deny_ssh(self): |
86 | 45 | log("Net.deny_ssh") | 46 | logging.info("Net.deny_ssh") |
87 | 46 | cmd = 'ufw deny ssh' | 47 | cmd = 'ufw deny ssh' |
88 | 47 | run_shell_command(cmd) | 48 | run_shell_command(cmd) |
89 | 48 | 49 | ||
90 | 49 | def deny_all_incoming_and_outgoing_except_ssh(self): | 50 | def deny_all_incoming_and_outgoing_except_ssh(self): |
92 | 50 | log("Net.deny_all_incoming_and_outgoing_except_ssh") | 51 | logging.info("Net.deny_all_incoming_and_outgoing_except_ssh") |
93 | 51 | self.deny_all_incoming_except_ssh() | 52 | self.deny_all_incoming_except_ssh() |
94 | 52 | self.deny_all_outgoing_except_ssh() | 53 | self.deny_all_outgoing_except_ssh() |
95 | 53 | 54 | ||
96 | 54 | def allow_all_incoming_and_outgoing(self): | 55 | def allow_all_incoming_and_outgoing(self): |
98 | 55 | log("Net.allow_all_incoming_and_outgoing") | 56 | logging.info("Net.allow_all_incoming_and_outgoing") |
99 | 56 | self.allow_all_incoming() | 57 | self.allow_all_incoming() |
100 | 57 | self.allow_all_outgoing() | 58 | self.allow_all_outgoing() |
101 | 58 | 59 | ||
102 | 59 | def deny_all_incoming_except_ssh(self): | 60 | def deny_all_incoming_except_ssh(self): |
104 | 60 | log("Net.deny_all_incoming_except_ssh") | 61 | logging.info("Net.deny_all_incoming_except_ssh") |
105 | 61 | self.allow_ssh() | 62 | self.allow_ssh() |
106 | 62 | self.default_deny() | 63 | self.default_deny() |
107 | 63 | 64 | ||
108 | 64 | def allow_all_incoming(self): | 65 | def allow_all_incoming(self): |
110 | 65 | log("Net.allow_all_incoming") | 66 | logging.info("Net.allow_all_incoming") |
111 | 66 | self.default_allow() | 67 | self.default_allow() |
112 | 67 | 68 | ||
113 | 68 | def deny_all_outgoing_except_ssh(self): | 69 | def deny_all_outgoing_except_ssh(self): |
115 | 69 | log("Net.deny_all_outgoing_except_ssh") | 70 | logging.info("Net.deny_all_outgoing_except_ssh") |
116 | 70 | self.allow_ssh() | 71 | self.allow_ssh() |
117 | 71 | cmd = 'ufw deny out to any' | 72 | cmd = 'ufw deny out to any' |
118 | 72 | run_shell_command(cmd) | 73 | run_shell_command(cmd) |
119 | 73 | 74 | ||
120 | 74 | def allow_all_outgoing(self): | 75 | def allow_all_outgoing(self): |
122 | 75 | log("Net.allow_all_outgoing") | 76 | logging.info("Net.allow_all_outgoing") |
123 | 76 | cmd = 'ufw delete deny out to any' | 77 | cmd = 'ufw delete deny out to any' |
124 | 77 | run_shell_command(cmd) | 78 | run_shell_command(cmd) |
125 | 78 | 79 | ||
126 | 79 | def deny_port(self, port=8080): | 80 | def deny_port(self, port=8080): |
128 | 80 | log("Net.deny_port port=%s" % port) | 81 | logging.info("Net.deny_port port=%s" % port) |
129 | 81 | cmd = 'ufw deny ' + str(port) | 82 | cmd = 'ufw deny ' + str(port) |
130 | 82 | run_shell_command(cmd) | 83 | run_shell_command(cmd) |
131 | 83 | 84 | ||
132 | 84 | 85 | ||
133 | === modified file 'runner.py' | |||
134 | --- runner.py 2015-04-29 19:57:39 +0000 | |||
135 | +++ runner.py 2015-04-30 22:20:24 +0000 | |||
136 | @@ -1,8 +1,12 @@ | |||
137 | 1 | from argparse import ArgumentParser | 1 | from argparse import ArgumentParser |
138 | 2 | import logging | ||
139 | 2 | from time import time | 3 | from time import time |
140 | 3 | 4 | ||
141 | 4 | from chaos_monkey import ChaosMonkey | 5 | from chaos_monkey import ChaosMonkey |
143 | 5 | from utility import BadRequest | 6 | from utility import ( |
144 | 7 | BadRequest, | ||
145 | 8 | setup_logging, | ||
146 | 9 | ) | ||
147 | 6 | 10 | ||
148 | 7 | 11 | ||
149 | 8 | def random(run_timeout, enablement_timeout): | 12 | def random(run_timeout, enablement_timeout): |
150 | @@ -35,6 +39,12 @@ | |||
151 | 35 | parser.add_argument( | 39 | parser.add_argument( |
152 | 36 | '-tt', '--total-timeout', default=60, type=int, | 40 | '-tt', '--total-timeout', default=60, type=int, |
153 | 37 | help="Total timeout in seconds") | 41 | help="Total timeout in seconds") |
154 | 42 | parser.add_argument('-lp', '--log-path', help='Where to write logs.', | ||
155 | 43 | default='log/results.log') | ||
156 | 44 | parser.add_argument('-lc', '--log-count', default=2, type=int, | ||
157 | 45 | help='The number of backups to keep.') | ||
158 | 38 | args = parser.parse_args() | 46 | args = parser.parse_args() |
159 | 47 | setup_logging(log_path=args.log_path, log_count=args.log_count) | ||
160 | 48 | logging.info('Chaos monkey started') | ||
161 | 39 | random(run_timeout=args.total_timeout, | 49 | random(run_timeout=args.total_timeout, |
162 | 40 | enablement_timeout=args.enablement_timeout) | 50 | enablement_timeout=args.enablement_timeout) |
163 | 41 | 51 | ||
164 | === modified file 'tests/test_utility.py' | |||
165 | --- tests/test_utility.py 2015-04-29 19:57:39 +0000 | |||
166 | +++ tests/test_utility.py 2015-04-30 22:20:24 +0000 | |||
167 | @@ -1,9 +1,15 @@ | |||
168 | 1 | import logging | ||
169 | 2 | from logging.handlers import RotatingFileHandler | ||
170 | 1 | from subprocess import CalledProcessError | 3 | from subprocess import CalledProcessError |
171 | 4 | from tempfile import NamedTemporaryFile | ||
172 | 2 | from unittest import TestCase | 5 | from unittest import TestCase |
173 | 3 | 6 | ||
174 | 4 | from mock import patch | 7 | from mock import patch |
175 | 5 | 8 | ||
177 | 6 | from utility import run_shell_command | 9 | from utility import ( |
178 | 10 | run_shell_command, | ||
179 | 11 | setup_logging, | ||
180 | 12 | ) | ||
181 | 7 | 13 | ||
182 | 8 | 14 | ||
183 | 9 | class TestUtility(TestCase): | 15 | class TestUtility(TestCase): |
184 | @@ -20,3 +26,63 @@ | |||
185 | 20 | def test_run_shell_command_output(self): | 26 | def test_run_shell_command_output(self): |
186 | 21 | output = run_shell_command('echo "hello"') | 27 | output = run_shell_command('echo "hello"') |
187 | 22 | self.assertEqual(output, '"hello"\n') | 28 | self.assertEqual(output, '"hello"\n') |
188 | 29 | |||
189 | 30 | def test_setup_logging(self): | ||
190 | 31 | with NamedTemporaryFile() as temp_file: | ||
191 | 32 | setup_logging(temp_file.name, log_count=1) | ||
192 | 33 | logger = logging.getLogger() | ||
193 | 34 | self.assertEqual(logger.level, logging.DEBUG) | ||
194 | 35 | self.assertEqual(logger.name, 'root') | ||
195 | 36 | handlers = logger.handlers | ||
196 | 37 | self.assertIn( | ||
197 | 38 | type(handlers[0]), [RotatingFileHandler, logging.StreamHandler]) | ||
198 | 39 | self.assertIn( | ||
199 | 40 | type(handlers[1]), [RotatingFileHandler, logging.StreamHandler]) | ||
200 | 41 | |||
201 | 42 | def test_setup_logging_formatter(self): | ||
202 | 43 | log_count = 1 | ||
203 | 44 | with NamedTemporaryFile() as temp_file: | ||
204 | 45 | with patch('logging.Formatter') as l_mock: | ||
205 | 46 | setup_logging(temp_file.name, log_count=log_count) | ||
206 | 47 | logger = logging.getLogger() | ||
207 | 48 | self.assertEqual(logger.name, 'root') | ||
208 | 49 | l_mock.assert_called_once_with( | ||
209 | 50 | '%(asctime)s %(levelname)s %(message)s', '%Y-%m-%d %H:%M:%S') | ||
210 | 51 | |||
211 | 52 | def test_setup_logging_rotating_file_handler(self): | ||
212 | 53 | log_count = 1 | ||
213 | 54 | with NamedTemporaryFile() as temp_file: | ||
214 | 55 | with patch('utility.RotatingFileHandler') as mock: | ||
215 | 56 | setup_logging(temp_file.name, log_count=log_count) | ||
216 | 57 | mock.assert_called_once_with( | ||
217 | 58 | temp_file.name, maxBytes=1024 * 1024 * 512, backupCount=log_count) | ||
218 | 59 | |||
219 | 60 | def test_log(self): | ||
220 | 61 | with NamedTemporaryFile() as temp_file: | ||
221 | 62 | setup_logging(temp_file.name, log_count=1) | ||
222 | 63 | logging.info('testing123') | ||
223 | 64 | with open(temp_file.name, 'r') as file_reader: | ||
224 | 65 | content = file_reader.read() | ||
225 | 66 | # log format: 2015-04-29 14:03:02 INFO testing123 | ||
226 | 67 | match = content.split(' ', 2)[2] | ||
227 | 68 | self.assertEqual(match, 'INFO testing123\n') | ||
228 | 69 | |||
229 | 70 | def test_log_debug(self): | ||
230 | 71 | with NamedTemporaryFile() as temp_file: | ||
231 | 72 | setup_logging(temp_file.name, log_count=1) | ||
232 | 73 | logging.debug("testing123") | ||
233 | 74 | with open(temp_file.name, 'r') as file_reader: | ||
234 | 75 | content = file_reader.read() | ||
235 | 76 | # log format: 2015-04-29 14:03:02 INFO testing123 | ||
236 | 77 | match = content.split(' ', 2)[2] | ||
237 | 78 | self.assertEqual(match, 'DEBUG testing123\n') | ||
238 | 79 | |||
239 | 80 | def test_log_error(self): | ||
240 | 81 | with NamedTemporaryFile() as temp_file: | ||
241 | 82 | setup_logging(temp_file.name, log_count=1) | ||
242 | 83 | logging.error("testing123") | ||
243 | 84 | with open(temp_file.name, 'r') as file_reader: | ||
244 | 85 | content = file_reader.read() | ||
245 | 86 | # log format: 2015-04-29 14:03:02 INFO testing123 | ||
246 | 87 | match = content.split(' ', 2)[2] | ||
247 | 88 | self.assertEqual(match, 'ERROR testing123\n') | ||
248 | 23 | 89 | ||
249 | === modified file 'utility.py' | |||
250 | --- utility.py 2015-04-29 19:57:39 +0000 | |||
251 | +++ utility.py 2015-04-30 22:20:24 +0000 | |||
252 | @@ -1,5 +1,8 @@ | |||
253 | 1 | from __future__ import print_function | 1 | from __future__ import print_function |
254 | 2 | 2 | ||
255 | 3 | import logging | ||
256 | 4 | from logging.handlers import RotatingFileHandler | ||
257 | 5 | |||
258 | 3 | from subprocess import ( | 6 | from subprocess import ( |
259 | 4 | CalledProcessError, | 7 | CalledProcessError, |
260 | 5 | check_output, | 8 | check_output, |
261 | @@ -12,14 +15,25 @@ | |||
262 | 12 | try: | 15 | try: |
263 | 13 | output = check_output(shell_cmd) | 16 | output = check_output(shell_cmd) |
264 | 14 | except CalledProcessError: | 17 | except CalledProcessError: |
266 | 15 | log("Command generated error: %s " % cmd) | 18 | logging.error("Command generated error: %s " % cmd) |
267 | 16 | if not quiet_mode: | 19 | if not quiet_mode: |
268 | 17 | raise | 20 | raise |
269 | 18 | return output | 21 | return output |
270 | 19 | 22 | ||
271 | 20 | 23 | ||
274 | 21 | def log(log_str): | 24 | def setup_logging(log_path, log_count): |
275 | 22 | print(log_str) | 25 | """Install log handlers to output to file and stream.""" |
276 | 26 | formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s', | ||
277 | 27 | '%Y-%m-%d %H:%M:%S') | ||
278 | 28 | root_logger = logging.getLogger() | ||
279 | 29 | rf_handler = RotatingFileHandler( | ||
280 | 30 | log_path, maxBytes=1024 * 1024 * 512, backupCount=log_count) | ||
281 | 31 | rf_handler.setFormatter(formatter) | ||
282 | 32 | root_logger.addHandler(rf_handler) | ||
283 | 33 | s_handler = logging.StreamHandler() | ||
284 | 34 | s_handler.setFormatter(formatter) | ||
285 | 35 | root_logger.addHandler(s_handler) | ||
286 | 36 | root_logger.setLevel(logging.DEBUG) | ||
287 | 23 | 37 | ||
288 | 24 | 38 | ||
289 | 25 | class NotFound(Exception): | 39 | class NotFound(Exception): |
You would not need to provide and maintain the helper log() and log_error() functions if only the root logger was used. Then to write to the log each file would just need to import the logger module and call logger.info(), logger.warning, logger.error(), logger,critical(), logger.debug or logger.exception().