Merge ~jfguedez/juju-lint:refactor-header-logging into juju-lint:master
- Git
- lp:~jfguedez/juju-lint
- refactor-header-logging
- Merge into master
Proposed by
Jose Guedez
Status: | Merged |
---|---|
Approved by: | James Troup |
Approved revision: | d219e5ebd839c1ff6b635a07b1b689a4cdbc378a |
Merged at revision: | bd261d5c562a1e3d32b68bd59b0b3739807ab693 |
Proposed branch: | ~jfguedez/juju-lint:refactor-header-logging |
Merge into: | juju-lint:master |
Diff against target: |
582 lines (+95/-247) 2 files modified
jujulint/lint.py (+91/-247) jujulint/logging.py (+4/-0) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Juju Lint maintainers | Pending | ||
Review via email: mp+408783@code.launchpad.net |
Commit message
Refactor logging to make code more readable
Description of the change
To post a comment you must log in.
Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote : | # |
Revision history for this message
James Troup (elmo) wrote : | # |
Nice cleanup, thanks.
Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote : | # |
Change successfully merged at revision bd261d5c562a1e3
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/jujulint/lint.py b/jujulint/lint.py |
2 | index 38dfc00..68fef78 100755 |
3 | --- a/jujulint/lint.py |
4 | +++ b/jujulint/lint.py |
5 | @@ -21,6 +21,7 @@ |
6 | import collections |
7 | from datetime import datetime, timezone |
8 | import json |
9 | +import logging |
10 | import os.path |
11 | import pprint |
12 | import re |
13 | @@ -126,27 +127,16 @@ class Linter: |
14 | if self.overrides: |
15 | for override in self.overrides.split("#"): |
16 | (name, where) = override.split(":") |
17 | - self.logger.info( |
18 | - "[{}] [{}/{}] Overriding {} with {}".format( |
19 | - self.cloud_name, |
20 | - self.controller_name, |
21 | - self.model_name, |
22 | - name, |
23 | - where, |
24 | - ) |
25 | + self._log_with_header( |
26 | + "Overriding {} with {}".format(name, where), level=logging.INFO |
27 | ) |
28 | self.lint_rules["subordinates"][name] = dict(where=where) |
29 | |
30 | # Flatten all entries (to account for nesting due to YAML anchors (templating) |
31 | self.lint_rules = {k: flatten_list(v) for k, v in self.lint_rules.items()} |
32 | |
33 | - self.logger.debug( |
34 | - "[{}] [{}/{}] Lint Rules: {}".format( |
35 | - self.cloud_name, |
36 | - self.controller_name, |
37 | - self.model_name, |
38 | - pprint.pformat(self.lint_rules), |
39 | - ) |
40 | + self._log_with_header( |
41 | + "Lint Rules: {}".format(pprint.pformat(self.lint_rules)) |
42 | ) |
43 | return True |
44 | self.logger.error("Rules file {} does not exist.".format(self.filename)) |
45 | @@ -165,15 +155,7 @@ class Linter: |
46 | subordinates = [i.split("/")[0] for i in subordinates] |
47 | else: |
48 | subordinates = [] |
49 | - self.logger.debug( |
50 | - "[{}] [{}/{}] {}: {}".format( |
51 | - self.cloud_name, |
52 | - self.controller_name, |
53 | - self.model_name, |
54 | - unit, |
55 | - subordinates, |
56 | - ) |
57 | - ) |
58 | + self._log_with_header("{}: {}".format(unit, subordinates)) |
59 | machine = app_d["units"][unit]["machine"] |
60 | self.model.subs_on_machines.setdefault(machine, set()) |
61 | for sub in subordinates: |
62 | @@ -214,11 +196,8 @@ class Linter: |
63 | """Check if value is set per rule constraints.""" |
64 | if rule in config: |
65 | if check_value is True: |
66 | - self.logger.debug( |
67 | - "[{}] [{}/{}] (PASS) Application {} correctly has config for '{}': {}.".format( |
68 | - self.cloud_name, |
69 | - self.controller_name, |
70 | - self.model_name, |
71 | + self._log_with_header( |
72 | + "(PASS) Application {} correctly has config for '{}': {}.".format( |
73 | name, |
74 | rule, |
75 | config[rule], |
76 | @@ -241,11 +220,8 @@ class Linter: |
77 | ) |
78 | return False |
79 | elif check_value is False: |
80 | - self.logger.debug( |
81 | - "[{}] [{}/{}] (PASS) Application {} correctly had no config for '{}'.".format( |
82 | - self.cloud_name, |
83 | - self.controller_name, |
84 | - self.model_name, |
85 | + self._log_with_header( |
86 | + "(PASS) Application {} correctly had no config for '{}'.".format( |
87 | name, |
88 | rule, |
89 | ) |
90 | @@ -313,22 +289,16 @@ class Linter: |
91 | self, operator, app_name, check_value, config_key, app_config |
92 | ): |
93 | """Apply the provided config operator to the configuration.""" |
94 | - model_header = "[{}] [{}/{}]".format( |
95 | - self.cloud_name, |
96 | - self.controller_name, |
97 | - self.model_name, |
98 | - ) |
99 | - |
100 | # First check if the config key is present |
101 | if config_key not in app_config: |
102 | - self.logger.warn( |
103 | - "{} Application {} has no config for '{}', cannot determine if {} {}.".format( |
104 | - model_header, |
105 | + self._log_with_header( |
106 | + "Application {} has no config for '{}', cannot determine if {} {}.".format( |
107 | app_name, |
108 | config_key, |
109 | operator.repr, |
110 | repr(check_value), |
111 | - ) |
112 | + ), |
113 | + level=logging.WARN, |
114 | ) |
115 | return False |
116 | |
117 | @@ -336,9 +306,8 @@ class Linter: |
118 | |
119 | # Apply the check callable and handle the possible cases |
120 | if operator.check(check_value, actual_value): |
121 | - self.logger.debug( |
122 | - "{} Application {} has a valid config for '{}': {} ({} {})".format( |
123 | - model_header, |
124 | + self._log_with_header( |
125 | + "Application {} has a valid config for '{}': {} ({} {})".format( |
126 | app_name, |
127 | config_key, |
128 | repr(check_value), |
129 | @@ -373,26 +342,20 @@ class Linter: |
130 | """Check application against provided rules.""" |
131 | rules = dict(rules) |
132 | for rule in rules: |
133 | - self.logger.debug( |
134 | - "[{}] [{}/{}] Checking {} for configuration {}".format( |
135 | - self.cloud_name, self.controller_name, self.model_name, name, rule |
136 | - ) |
137 | - ) |
138 | + self._log_with_header("Checking {} for configuration {}".format(name, rule)) |
139 | for check_op, check_value in rules[rule].items(): |
140 | # check_op should be the operator name, e.g. (eq, neq, gte, isset) |
141 | if check_op in VALID_CONFIG_CHECKS: |
142 | check_method = getattr(self, check_op) |
143 | check_method(name, check_value, rule, config) |
144 | else: |
145 | - self.logger.warn( |
146 | - "[{}] [{}/{}] Application {} has unknown check operation for {}: {}.".format( |
147 | - self.cloud_name, |
148 | - self.controller_name, |
149 | - self.model_name, |
150 | + self._log_with_header( |
151 | + "Application {} has unknown check operation for {}: {}.".format( |
152 | name, |
153 | rule, |
154 | check_op, |
155 | - ) |
156 | + ), |
157 | + level=logging.WARN, |
158 | ) |
159 | |
160 | def check_configuration(self, applications): |
161 | @@ -401,13 +364,11 @@ class Linter: |
162 | # look for config rules for this application |
163 | lint_rules = [] |
164 | if "charm" not in applications[application]: |
165 | - self.logger.warn( |
166 | - "[{}] [{}/{}] Application {} has no charm.".format( |
167 | - self.cloud_name, |
168 | - self.controller_name, |
169 | - self.model_name, |
170 | + self._log_with_header( |
171 | + "Application {} has no charm.".format( |
172 | application, |
173 | - ) |
174 | + ), |
175 | + level=logging.WARN, |
176 | ) |
177 | continue |
178 | |
179 | @@ -442,103 +403,51 @@ class Linter: |
180 | for required_sub in self.lint_rules["subordinates"]: |
181 | self.model.missing_subs.setdefault(required_sub, set()) |
182 | self.model.extraneous_subs.setdefault(required_sub, set()) |
183 | - self.logger.debug( |
184 | - "[{}] [{}/{}] Checking for sub {}".format( |
185 | - self.cloud_name, self.controller_name, self.model_name, required_sub |
186 | - ) |
187 | - ) |
188 | + self._log_with_header("Checking for sub {}".format(required_sub)) |
189 | where = self.lint_rules["subordinates"][required_sub]["where"] |
190 | for machine in self.model.subs_on_machines: |
191 | - self.logger.debug( |
192 | - "[{}] [{}/{}] Checking on {}".format( |
193 | - self.cloud_name, self.controller_name, self.model_name, machine |
194 | - ) |
195 | - ) |
196 | + self._log_with_header("Checking on {}".format(machine)) |
197 | present_subs = self.model.subs_on_machines[machine] |
198 | apps = self.model.apps_on_machines[machine] |
199 | if where.startswith("on "): # only on specific apps |
200 | required_on = where[3:] |
201 | - self.logger.debug( |
202 | - "[{}] [{}/{}] Requirement {} is = from...".format( |
203 | - self.cloud_name, |
204 | - self.controller_name, |
205 | - self.model_name, |
206 | - required_on, |
207 | - ) |
208 | + self._log_with_header( |
209 | + "Requirement {} is = from...".format(required_on) |
210 | ) |
211 | if required_on not in apps: |
212 | - self.logger.debug( |
213 | - "[{}] [{}/{}] ... NOT matched".format( |
214 | - self.cloud_name, self.controller_name, self.model_name |
215 | - ) |
216 | - ) |
217 | + self._log_with_header("... NOT matched") |
218 | continue |
219 | - self.logger.debug("[{}] [{}/{}] ... matched") |
220 | + self._log_with_header("... matched") |
221 | # TODO this needs to be not just one app, but a list |
222 | elif where.startswith("all except "): # not next to this app |
223 | - self.logger.debug( |
224 | - "[{}] [{}/{}] requirement is != form...".format( |
225 | - self.cloud_name, self.controller_name, self.model_name |
226 | - ) |
227 | - ) |
228 | + self._log_with_header("requirement is != form...") |
229 | not_on = where[11:] |
230 | if not_on in apps: |
231 | - self.logger.debug( |
232 | - "[{}] [{}/{}] ... matched, not wanted on this host".format( |
233 | - self.cloud_name, self.controller_name, self.model_name |
234 | - ) |
235 | - ) |
236 | + self._log_with_header("... matched, not wanted on this host") |
237 | continue |
238 | elif where == "host only": |
239 | - self.logger.debug( |
240 | - "[{}] [{}/{}] requirement is 'host only' form....".format( |
241 | - self.cloud_name, self.controller_name, self.model_name |
242 | - ) |
243 | + self._log_with_header( |
244 | + "requirement is 'host only' form...." |
245 | ) |
246 | if is_container(machine): |
247 | - self.logger.debug( |
248 | - "[{}] [{}/{}] ... and we are a container, checking".format( |
249 | - self.cloud_name, self.controller_name, self.model_name |
250 | - ) |
251 | - ) |
252 | + self._log_with_header("... and we are a container, checking") |
253 | # XXX check alternate names? |
254 | if required_sub in present_subs: |
255 | - self.logger.debug( |
256 | - "[{}] [{}/{}] ... found extraneous sub".format( |
257 | - self.cloud_name, |
258 | - self.controller_name, |
259 | - self.model_name, |
260 | - ) |
261 | - ) |
262 | + self._log_with_header("... found extraneous sub") |
263 | for app in self.model.apps_on_machines[machine]: |
264 | self.model.extraneous_subs[required_sub].add(app) |
265 | continue |
266 | - self.logger.debug( |
267 | - "[{}] [{}/{}] ... and we are a host, will fallthrough".format( |
268 | - self.cloud_name, |
269 | - self.controller_name, |
270 | - self.model_name, |
271 | - ) |
272 | - ) |
273 | + self._log_with_header("... and we are a host, will fallthrough") |
274 | + |
275 | elif where == "all or nothing" and required_sub not in all_or_nothing: |
276 | - self.logger.debug( |
277 | - "[{}] [{}/{}] requirement is 'all or nothing' and was 'nothing'.".format( |
278 | - self.cloud_name, |
279 | - self.controller_name, |
280 | - self.model_name, |
281 | - ) |
282 | + self._log_with_header( |
283 | + "requirement is 'all or nothing' and was 'nothing'." |
284 | ) |
285 | continue |
286 | # At this point we know we require the subordinate - we might just |
287 | # need to change the name we expect to see it as |
288 | elif where == "container aware": |
289 | - self.logger.debug( |
290 | - "[{}] [{}/{}] requirement is 'container aware'.".format( |
291 | - self.cloud_name, |
292 | - self.controller_name, |
293 | - self.model_name, |
294 | - ) |
295 | - ) |
296 | + self._log_with_header("requirement is 'container aware'.") |
297 | if is_container(machine): |
298 | suffixes = self.lint_rules["subordinates"][required_sub][ |
299 | "container-suffixes" |
300 | @@ -547,38 +456,17 @@ class Linter: |
301 | suffixes = self.lint_rules["subordinates"][required_sub][ |
302 | "host-suffixes" |
303 | ] |
304 | - self.logger.debug( |
305 | - "[{}] [{}/{}] -> suffixes == {}".format( |
306 | - self.cloud_name, |
307 | - self.controller_name, |
308 | - self.model_name, |
309 | - suffixes, |
310 | - ) |
311 | - ) |
312 | + self._log_with_header("-> suffixes == {}".format(suffixes)) |
313 | exceptions = [] |
314 | if "exceptions" in self.lint_rules["subordinates"][required_sub]: |
315 | exceptions = self.lint_rules["subordinates"][required_sub][ |
316 | "exceptions" |
317 | ] |
318 | - self.logger.debug( |
319 | - "[{}] [{}/{}] -> exceptions == {}".format( |
320 | - self.cloud_name, |
321 | - self.controller_name, |
322 | - self.model_name, |
323 | - exceptions, |
324 | - ) |
325 | - ) |
326 | + self._log_with_header("-> exceptions == {}".format(exceptions)) |
327 | found = False |
328 | for suffix in suffixes: |
329 | looking_for = "{}-{}".format(required_sub, suffix) |
330 | - self.logger.debug( |
331 | - "[{}] [{}/{}] -> Looking for {}".format( |
332 | - self.cloud_name, |
333 | - self.controller_name, |
334 | - self.model_name, |
335 | - looking_for, |
336 | - ) |
337 | - ) |
338 | + self._log_with_header("-> Looking for {}".format(looking_for)) |
339 | if looking_for in present_subs: |
340 | self.logger.debug("-> FOUND!!!") |
341 | self.cloud_name, |
342 | @@ -588,44 +476,24 @@ class Linter: |
343 | if not found: |
344 | for sub in present_subs: |
345 | if self.model.app_to_charm[sub] == required_sub: |
346 | - self.logger.debug( |
347 | - "[{}] [{}/{}] Winner winner, chicken dinner! 🍗 {}".format( |
348 | - self.cloud_name, |
349 | - self.controller_name, |
350 | - self.model_name, |
351 | - sub, |
352 | - ) |
353 | + self._log_with_header( |
354 | + "Winner winner, chicken dinner! 🍗 {}".format(sub) |
355 | ) |
356 | found = True |
357 | if not found: |
358 | for exception in exceptions: |
359 | if exception in apps: |
360 | - self.logger.debug( |
361 | - "[{}] [{}/{}]-> continuing as found exception: {}".format( |
362 | - self.cloud_name, |
363 | - self.controller_name, |
364 | - self.model_name, |
365 | - exception, |
366 | + self._log_with_header( |
367 | + "continuing as found exception: {}".format( |
368 | + exception |
369 | ) |
370 | ) |
371 | found = True |
372 | if not found: |
373 | - self.logger.debug( |
374 | - "[{}] [{}/{}] -> NOT FOUND".format( |
375 | - self.cloud_name, |
376 | - self.controller_name, |
377 | - self.model_name, |
378 | - ) |
379 | - ) |
380 | + self._log_with_header("-> NOT FOUND") |
381 | for app in self.model.apps_on_machines[machine]: |
382 | self.model.missing_subs[required_sub].add(app) |
383 | - self.logger.debug( |
384 | - "[{}] [{}/{}] -> continue-ing back out...".format( |
385 | - self.cloud_name, |
386 | - self.controller_name, |
387 | - self.model_name, |
388 | - ) |
389 | - ) |
390 | + self._log_with_header("-> continue-ing back out...") |
391 | continue |
392 | elif where not in ["all", "all or nothing"]: |
393 | self.logger.fubar( |
394 | @@ -637,30 +505,15 @@ class Linter: |
395 | required_sub, |
396 | ) |
397 | ) |
398 | - self.logger.debug( |
399 | - "[{}] [{}/{}] requirement is 'all' OR we fell through.".format( |
400 | - self.cloud_name, |
401 | - self.controller_name, |
402 | - self.model_name, |
403 | - ) |
404 | - ) |
405 | + self._log_with_header("requirement is 'all' OR we fell through.") |
406 | if required_sub not in present_subs: |
407 | for sub in present_subs: |
408 | if self.model.app_to_charm[sub] == required_sub: |
409 | - self.logger.debug( |
410 | + self._log_with_header( |
411 | "Winner winner, chicken dinner! 🍗 {}".format(sub) |
412 | ) |
413 | - self.cloud_name, |
414 | - self.controller_name, |
415 | - self.model_name, |
416 | continue |
417 | - self.logger.debug( |
418 | - "[{}] [{}/{}] not found.".format( |
419 | - self.cloud_name, |
420 | - self.controller_name, |
421 | - self.model_name, |
422 | - ) |
423 | - ) |
424 | + self._log_with_header("not found.") |
425 | for app in self.model.apps_on_machines[machine]: |
426 | self.model.missing_subs[required_sub].add(app) |
427 | |
428 | @@ -904,10 +757,9 @@ class Linter: |
429 | """Map machines in the model to their availability zone.""" |
430 | for machine in machines: |
431 | if "hardware" not in machines[machine]: |
432 | - self.logger.warn( |
433 | - "[{}] [{}/{}] Machine {} has no hardware info; skipping.".format( |
434 | - self.cloud_name, self.controller_name, self.model_name, machine |
435 | - ) |
436 | + self._log_with_header( |
437 | + "Machine {} has no hardware info; skipping.".format(machine), |
438 | + level=logging.WARN, |
439 | ) |
440 | continue |
441 | |
442 | @@ -920,10 +772,11 @@ class Linter: |
443 | self.model.machines_to_az[machine] = az |
444 | break |
445 | if not found_az: |
446 | - self.logger.warn( |
447 | - "[{}] [{}/{}] Machine {} has no availability-zone info in hardware field; skipping.".format( |
448 | - self.cloud_name, self.controller_name, self.model_name, machine |
449 | - ) |
450 | + self._log_with_header( |
451 | + "Machine {} has no availability-zone info in hardware field; skipping.".format( |
452 | + machine |
453 | + ), |
454 | + level=logging.WARN, |
455 | ) |
456 | |
457 | def check_status(self, what, status, expected): |
458 | @@ -987,19 +840,16 @@ class Linter: |
459 | expected=juju_expected, |
460 | ) |
461 | else: |
462 | - self.logger.warn( |
463 | - "[{}] [{}/{}] Could not determine Juju status for {}.".format( |
464 | - self.cloud_name, self.controller_name, self.model_name, name |
465 | - ) |
466 | + self._log_with_header( |
467 | + "Could not determine Juju status for {}.".format(name), |
468 | + level=logging.WARN, |
469 | ) |
470 | else: |
471 | - self.logger.warn( |
472 | - "[{}] [{}/{}] Could not determine appropriate status key for {}.".format( |
473 | - self.cloud_name, |
474 | - self.controller_name, |
475 | - self.model_name, |
476 | + self._log_with_header( |
477 | + "Could not determine appropriate status key for {}.".format( |
478 | name, |
479 | - ) |
480 | + ), |
481 | + level=logging.WARN, |
482 | ) |
483 | |
484 | def check_statuses(self, juju_status, applications): |
485 | @@ -1073,14 +923,12 @@ class Linter: |
486 | machine = applications[app_name]["units"][unit]["machine"] |
487 | machine = machine.split("/")[0] |
488 | if machine not in self.model.machines_to_az: |
489 | - self.logger.error( |
490 | - "[{}] [{}/{}] {}: Can't find machine {} in machine to AZ mapping data".format( |
491 | - self.cloud_name, |
492 | - self.controller_name, |
493 | - self.model_name, |
494 | + self._log_with_header( |
495 | + "{}: Can't find machine {} in machine to AZ mapping data".format( |
496 | app_name, |
497 | machine, |
498 | - ) |
499 | + ), |
500 | + level=logging.ERROR, |
501 | ) |
502 | continue |
503 | az_counter[self.model.machines_to_az[machine]] += 1 |
504 | @@ -1133,25 +981,14 @@ class Linter: |
505 | self.check_azs(parsed_yaml[applications]) |
506 | self.check_statuses(parsed_yaml, applications) |
507 | else: |
508 | - self.logger.warn( |
509 | - ( |
510 | - "[{}] [{}/{}] Relations data found; assuming a bundle and " |
511 | - "skipping AZ and status checks." |
512 | - ).format( |
513 | - self.cloud_name, |
514 | - self.model_name, |
515 | - self.controller_name, |
516 | - ) |
517 | + self._log_with_header( |
518 | + "Relations data found; assuming a bundle and skipping AZ and status checks." |
519 | ) |
520 | |
521 | self.results() |
522 | else: |
523 | - self.logger.warn( |
524 | - "[{}] [{}/{}] Model contains no applications, skipping.".format( |
525 | - self.cloud_name, |
526 | - self.controller_name, |
527 | - self.model_name, |
528 | - ) |
529 | + self._log_with_header( |
530 | + "Model contains no applications, skipping.", level=logging.WARN |
531 | ) |
532 | |
533 | def collect(self, error): |
534 | @@ -1160,11 +997,7 @@ class Linter: |
535 | |
536 | def handle_error(self, error): |
537 | """Collect an error and add it to the collector.""" |
538 | - self.logger.error( |
539 | - "[{}] [{}/{}] {}.".format( |
540 | - self.cloud_name, self.controller_name, self.model_name, error["message"] |
541 | - ) |
542 | - ) |
543 | + self._log_with_header(error["message"], level=logging.ERROR) |
544 | if self.collect_errors: |
545 | self.collect(error) |
546 | |
547 | @@ -1184,7 +1017,9 @@ class Linter: |
548 | try: |
549 | _, rel_path = line.split() |
550 | except ValueError: |
551 | - self.logger.warn("invalid include in rules, ignored: '{}'".format(line)) |
552 | + self.logger.warn( |
553 | + "invalid include in rules, ignored: '{}'".format(line) |
554 | + ) |
555 | continue |
556 | |
557 | include_path = os.path.join(os.path.dirname(self.filename), rel_path) |
558 | @@ -1196,3 +1031,12 @@ class Linter: |
559 | collector.append(line) |
560 | |
561 | return yaml.safe_load("\n".join(collector)) |
562 | + |
563 | + def _log_with_header(self, msg, level=logging.DEBUG): |
564 | + """Log a message with the cloud/controller/model header.""" |
565 | + self.logger.log( |
566 | + "[{}] [{}/{}] {}".format( |
567 | + self.cloud_name, self.controller_name, self.model_name, msg |
568 | + ), |
569 | + level=level, |
570 | + ) |
571 | diff --git a/jujulint/logging.py b/jujulint/logging.py |
572 | index 47eaa88..e3c42f8 100644 |
573 | --- a/jujulint/logging.py |
574 | +++ b/jujulint/logging.py |
575 | @@ -105,3 +105,7 @@ class Logger: |
576 | def error(self, message): |
577 | """Log a message with warn loglevel.""" |
578 | self.logger.error(message) |
579 | + |
580 | + def log(self, message, level=logging.DEBUG): |
581 | + """Log a message with arbitrary loglevel.""" |
582 | + self.logger.log(level, message) |
This merge proposal is being monitored by mergebot. Change the status to Approved to merge.