Merge ~jfguedez/juju-lint:refactor-header-logging into juju-lint: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)
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

To post a comment you must log in.
Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote :

This merge proposal is being monitored by mergebot. Change the status to Approved to merge.

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 bd261d5c562a1e3d32b68bd59b0b3739807ab693

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/jujulint/lint.py b/jujulint/lint.py
2index 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+ )
571diff --git a/jujulint/logging.py b/jujulint/logging.py
572index 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)

Subscribers

People subscribed via source and target branches