Merge lp:~mabac/svammel/extra-verbose-option into lp:svammel

Proposed by Mattias Backman
Status: Merged
Approved by: James Westby
Approved revision: 96
Merged at revision: 92
Proposed branch: lp:~mabac/svammel/extra-verbose-option
Merge into: lp:svammel
Diff against target: 501 lines (+91/-55)
7 files modified
bug_logging.py (+6/-4)
bug_reporting.py (+9/-6)
build_log.py (+12/-1)
config.py (+8/-12)
data_parsing.py (+33/-16)
file-failures.py (+23/-15)
tests/test_fail_filer.py (+0/-1)
To merge this branch: bzr merge lp:~mabac/svammel/extra-verbose-option
Reviewer Review Type Date Requested Status
James Westby (community) Approve
Matthias Klose Pending
Review via email: mp+57120@code.launchpad.net

Description of the change

Hi,

This branch adds an --extra-verbose option which makes the script print out detailed information about steps taken. This was requested by Matthias Klose to make troubleshooting easier.

Thanks,

Mattias

To post a comment you must log in.
Revision history for this message
Guilherme Salgado (salgado) wrote :

I think it would make sense to use python's logging module with its various types of messages (e.g. info, debug, warning, etc) instead. Has that been considered?

Revision history for this message
Mattias Backman (mabac) wrote :

> I think it would make sense to use python's logging module with its various
> types of messages (e.g. info, debug, warning, etc) instead. Has that been
> considered?

Actually, no I didn't spend enough time on this to consider looking for that module. But you're right it makes sense, a lot for free with little effort to change to logging.

Revision history for this message
Mattias Backman (mabac) wrote :

Perhaps there also should be a nice formatter, or is a format like the example below ok?

   INFO:Svammel:There's already a bug report for qtiplot version 0.9.8.2-1ubuntu4.

Revision history for this message
James Westby (james-w) wrote :

202 parser.add_argument(
203 + '-v2', '--debug', action='store_true', dest='verbose2',
204 + help='Enable debug messages.')

I think I would prefer just having -v be an option that you can specify
multiple times. -vv would then be the debug option.

I think there is an example of doing this in the documentation.

Otherwise this looks good.

Thanks,

James

review: Approve
97. By Mattias Backman

Remove -v2 option and change -v to count how many times it was passed.

98. By Mattias Backman

Remove Error: substring from .error logs.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'bug_logging.py'
2--- bug_logging.py 2011-04-08 14:04:36 +0000
3+++ bug_logging.py 2011-04-15 07:02:24 +0000
4@@ -17,12 +17,14 @@
5 # You should have received a copy of the GNU General Public License
6 # along with Linaro Svammel. If not, see <http://www.gnu.org/licenses/>.
7
8+import logging
9 from config import (
10- print_message,
11+ LOGGER_NAME,
12 )
13
14
15 LOG_ITEM_SEPARATOR = ';'
16+logger = logging.getLogger(LOGGER_NAME)
17
18
19 class Log(object):
20@@ -36,7 +38,7 @@
21 try:
22 self.log_file = open(self.log_file_name, 'a')
23 except IOError:
24- print 'Error: Could not open log file for writing.'
25+ logger.error('Could not open log file for writing.')
26 raise
27
28 def __del__(self):
29@@ -57,7 +59,7 @@
30 log_items[2], log_items[3])
31 self.add_entry(log_entry)
32 except IOError:
33- print_message("Log file '%s' does not exist." % \
34+ logger.warning("Log file '%s' does not exist." % \
35 self.log_file_name)
36 finally:
37 if log_file is not None:
38@@ -77,7 +79,7 @@
39 try:
40 self.log_file.write(log_string + '\n')
41 except IOError:
42- print 'Error: Could not write log to file.'
43+ logger.error('Could not write log to file.')
44 self.log_file.close()
45 raise
46
47
48=== modified file 'bug_reporting.py'
49--- bug_reporting.py 2011-04-08 14:04:36 +0000
50+++ bug_reporting.py 2011-04-15 07:02:24 +0000
51@@ -17,14 +17,16 @@
52 # You should have received a copy of the GNU General Public License
53 # along with Linaro Svammel. If not, see <http://www.gnu.org/licenses/>.
54
55+import logging
56 from config import (
57- print_message,
58+ LOGGER_NAME,
59 continue_on_error,
60 )
61
62
63 BUG_INVALID_STATUS = 'Invalid'
64 BUG_FIXED_STATUS = 'Fix Released'
65+logger = logging.getLogger(LOGGER_NAME)
66
67
68 def get_project_url(package_name, lp):
69@@ -44,11 +46,11 @@
70 target=target_url,
71 tags=tags)
72 if bug is not None:
73- print 'Filed bug %i: %s' % (bug.id, bug.web_link)
74+ logger.info('Filed bug %i: %s' % (bug.id, bug.web_link))
75 if importance is not None:
76 set_bugtask_importance(bug, importance)
77 else:
78- print "Error: unable to file bug '%s'." % title
79+ logger.error("Unable to file bug '%s'." % title)
80
81 return bug
82
83@@ -60,7 +62,8 @@
84 bug_task.lp_save()
85 except:
86 if continue_on_error():
87- print "Error: unable to set bug importance to '%s' but will proceed." % importance
88+ logger.error("Unable to set bug importance to '%s' but " \
89+ "will proceed." % importance)
90 else:
91 raise
92
93@@ -81,7 +84,7 @@
94 """ Close the bugtask by setting status to Invalid."""
95
96 bug_task = lp.load(url)
97- print_message(" Changing status for %s [%s ==> %s]" % \
98- (bug_task.title, bug_task.status, close_status))
99+ logger.info("Changing status for %s [%s ==> %s]" % \
100+ (bug_task.title, bug_task.status, close_status))
101 bug_task.status = close_status
102 bug_task.lp_save()
103
104=== modified file 'build_log.py'
105--- build_log.py 2011-04-08 14:04:36 +0000
106+++ build_log.py 2011-04-15 07:02:24 +0000
107@@ -21,9 +21,13 @@
108 import io
109 import gzip
110 import urllib
111+import logging
112 from StringIO import StringIO
113 from collections import deque
114-from config import print_message
115+from config import LOGGER_NAME
116+
117+
118+logger = logging.getLogger(LOGGER_NAME)
119
120
121 class MatcherError(Exception):
122@@ -37,6 +41,7 @@
123 def get_file_contents(gz_url):
124 """Return the contents of gzip file at url."""
125
126+ logger.debug('Downloading contents of %s', gz_url)
127 file = ''
128 if gz_url is not None:
129 file_handle = urllib.urlopen(gz_url)
130@@ -52,6 +57,8 @@
131 except:
132 # Log is not compressed, might be 404 error.
133 # TODO: should this be analyzed or just thrown away?
134+ logger.warning(
135+ 'Log is not compressed, might be 404 error.')
136 file = ''
137 file_io.close()
138 file_handle.close()
139@@ -102,6 +109,7 @@
140 '^Source-dependencies not satisfied'),
141 ]
142
143+ logger.debug('Applying log file matchers.')
144 history = deque([])
145 history_max = 20
146 skipped_lines = 0
147@@ -114,6 +122,9 @@
148 for matcher in matchers:
149 if isinstance(matcher, AbstractMatcher):
150 if matcher.match(line):
151+ logger.debug("Matcher '%s' triggered on line %d.",
152+ matcher.get_name(), skipped_lines +
153+ len(history))
154 return """This log snippet might be of interest, since it \
155 triggered the matcher '%s'.\nExcerpt %d lines into the build log:\n\n%s""" % \
156 (matcher.get_name(), skipped_lines, "".join(history))
157
158=== modified file 'config.py'
159--- config.py 2011-04-12 07:59:04 +0000
160+++ config.py 2011-04-15 07:02:24 +0000
161@@ -17,6 +17,7 @@
162 # You should have received a copy of the GNU General Public License
163 # along with Linaro Svammel. If not, see <http://www.gnu.org/licenses/>.
164
165+import logging
166 from launchpadlib.launchpad import Launchpad
167 import argparse
168
169@@ -44,6 +45,7 @@
170 lp = None
171 verbose = False
172 ignore_errors = False
173+LOGGER_NAME = 'Svammel'
174
175
176 def verify_inited():
177@@ -51,11 +53,6 @@
178 raise ConfigInitError('Global configuration is not initialized.')
179
180
181-def set_verbose_messages(state):
182- global verbose
183- verbose = state
184-
185-
186 def set_ignore_errors(state):
187 global ignore_errors
188 ignore_errors = state
189@@ -112,6 +109,9 @@
190 else:
191 raise ConfigInitError('Unsupported service_root: %s' % root)
192
193+ logging.getLogger(LOGGER_NAME).debug(
194+ "Logging in to Launchpad environment '%s' as application '%s' " \
195+ "using local cachedir '%s'.", service_root, appid, cachedir)
196 set_launchpad(service_root, appid, cachedir)
197 inited = True
198
199@@ -120,8 +120,9 @@
200 """Get the ArgumentParser for the arguments given on the command line."""
201 parser = argparse.ArgumentParser()
202 parser.add_argument(
203- '-v', '--verbose', action='store_true', dest='verbose',
204- help='Enable verbose messages.')
205+ '-v', '--verbose', action='count', dest='verbose',
206+ help='Enable verbose messages. Passing -v more than once increases ' \
207+ 'verbosity.')
208 parser.add_argument(
209 '--dryrun', action='store_true', dest='dryrun',
210 help='Disable bug filing and enable verbose messages.')
211@@ -169,8 +170,3 @@
212 'failure to set bug attributes.')
213
214 return parser
215-
216-
217-def print_message(message):
218- if verbose is True:
219- print message
220
221=== modified file 'data_parsing.py'
222--- data_parsing.py 2011-04-11 10:17:35 +0000
223+++ data_parsing.py 2011-04-15 07:02:24 +0000
224@@ -22,12 +22,13 @@
225
226 from launchpadlib.uris import *
227 import apt_pkg
228+import logging
229
230 from config import (
231 get_base_url,
232 get_base_api_url,
233 get_launchpad,
234- print_message,
235+ LOGGER_NAME,
236 )
237
238 apt_pkg.InitSystem()
239@@ -40,6 +41,7 @@
240 PRIMARY_ARCHIVE_NAME = 'primary'
241 FAILED_BUILD_STATE = 'Failed to build'
242 SUCCESS_BUILD_STATE = 'Successfully built'
243+logger = logging.getLogger(LOGGER_NAME)
244
245
246 class InvalidArchiveError(Exception):
247@@ -93,6 +95,8 @@
248
249 def get_build_list(archive, state, source=None):
250
251+ logger.debug("Getting '%s' builds for source '%s' from archive " \
252+ "'%s'" % (state, source, archive.name))
253 buildlist = archive.getBuildRecords(build_state=state, source_name=source)
254
255 # XXX mabac 2011-03-16: getBuildRecords filters on substrings
256@@ -104,6 +108,8 @@
257 csp = build.current_source_publication
258 if csp is not None and csp.source_package_name == source:
259 filter_list.append(build)
260+ logger.debug("Done getting '%s' builds for source '%s'" % \
261+ (state, source))
262 return filter_list
263
264 return buildlist
265@@ -129,7 +135,7 @@
266 def fetch_pkg_list(archives):
267 all_spph = dict()
268 for archive, archive_name in archives:
269- print_message("The following packages have failed to build " \
270+ logger.info("The following packages have failed to build " \
271 "in '%s'." % archive_name)
272
273 buildlist = get_build_list(archive, FAILED_BUILD_STATE)
274@@ -139,15 +145,20 @@
275 # Build record for an older version
276 continue
277
278- print_message(" Found build '%s'" % build.title)
279+ logger.info("Found build '%s'" % build.title)
280
281 stored_spph = all_spph.get(csp.source_package_name)
282 new_spph = SPPH(csp.source_package_name,
283 csp.source_package_version)
284 if new_spph.is_higher_version_than(stored_spph):
285+ logger.debug("Keeping new build record.")
286 all_spph[csp.source_package_name] = new_spph
287 new_spph.add_build_record(build)
288 else:
289+ logger.debug(
290+ "Newly found build is not higher version than " \
291+ "previously found build. Will store new build " \
292+ "record for same version.")
293 stored_spph.add_build_record(build)
294
295 return all_spph
296@@ -205,32 +216,34 @@
297 A subset of all_spph_list which represents the interesting packages.
298 """
299 filtered_list = []
300- print_message('Determining which build failures to file bugs for.')
301+ logger.info('Determining which build failures to file bugs for.')
302 for spph in all_spph_list.values():
303+ logger.debug(' - checking %s' % spph.package_name)
304 if not spph.is_ftbfs(fail_arch):
305- print_message(" Ignoring package '%s' version '%s' since it " \
306- "did not fail on architecture '%s'." % \
307- (spph.package_name, spph.version, fail_arch))
308+ logger.info("Ignoring package '%s' version '%s' since it " \
309+ "did not fail on architecture '%s'." % \
310+ (spph.package_name, spph.version, fail_arch))
311 continue
312 also_failed = next((arch for arch in ok_archs
313 if spph.is_ftbfs(arch)), None)
314 if also_failed is not None:
315- print_message(" Ignoring package '%s' version '%s' since it " \
316- "also failed to build on architecture '%s'." % \
317- (spph.package_name, spph.version, also_failed))
318+ logger.info("Ignoring package '%s' version '%s' since it " \
319+ "also failed to build on architecture '%s'." % \
320+ (spph.package_name, spph.version, also_failed))
321 continue
322
323 success_version = exists_successful_later_build(spph, archives,
324 fail_arch)
325 if success_version is not None:
326- print_message(" Ignoring package '%s' version '%s' since build " \
327- "of version '%s' succeded." % (spph.package_name,
328- spph.version,
329- success_version))
330+ logger.info("Ignoring package '%s' version '%s' since build " \
331+ "of version '%s' succeded." % (spph.package_name,
332+ spph.version,
333+ success_version))
334 continue
335
336 filtered_list.append(spph)
337
338+ logger.debug(' done filtering packages.')
339 return filtered_list
340
341
342@@ -246,8 +259,10 @@
343 """
344 archives = []
345 series = None
346+ logger.debug('Getting ubuntu distribution.')
347 ubuntu = get_launchpad().distributions['ubuntu']
348 for archive_name in archive_names:
349+ logger.debug('Getting archive %s.', archive_name)
350 try:
351 archive = None
352 archive_display_name = ''
353@@ -264,9 +279,10 @@
354 raise InvalidArchiveError(
355 'Error: %s is not a valid archive.' % archive_name)
356 archive_display_name = archive_display_name + archive.name
357+ logger.debug('Got archive %s.', archive_display_name)
358 archives.append((archive, archive_display_name))
359 except HTTPError:
360- print 'Error: %s is not a valid archive.' % archive_name
361+ logger.critical('Error: %s is not a valid archive.' % archive_name)
362 raise
363 return archives
364
365@@ -284,9 +300,10 @@
366 A list of SPPH objects representing source packages to file bugs against.
367 """
368
369+ logger.info('Getting archives.')
370 archives = get_archives(archive_names, series_name)
371
372- print_message("Will be getting builds from archives " + ", ".join(
373+ logger.info("Will be getting builds from archives " + ", ".join(
374 name for (archive, name) in archives))
375 spph_list = fetch_pkg_list(archives)
376 return filter_spph(spph_list, fail_platform, ok_platforms, archives)
377
378=== modified file 'file-failures.py'
379--- file-failures.py 2011-04-11 08:40:06 +0000
380+++ file-failures.py 2011-04-15 07:02:24 +0000
381@@ -18,6 +18,7 @@
382 # You should have received a copy of the GNU General Public License
383 # along with Linaro Svammel. If not, see <http://www.gnu.org/licenses/>.
384
385+import logging
386 from data_parsing import (
387 get_tags_for_fail,
388 get_build_status,
389@@ -36,9 +37,8 @@
390 init,
391 get_launchpad,
392 get_args_parser,
393- print_message,
394- set_verbose_messages,
395 set_ignore_errors,
396+ LOGGER_NAME,
397 )
398 from build_log import (
399 get_file_contents,
400@@ -60,10 +60,15 @@
401
402 LP_APP_NAME = 'Svammel bug filer'
403
404-
405+logging.basicConfig()
406+logger = logging.getLogger(LOGGER_NAME)
407 parser = get_args_parser()
408 args = parser.parse_args()
409-set_verbose_messages(args.verbose or args.dryrun)
410+if args.verbose == 1 or args.dryrun:
411+ logger.setLevel(logging.INFO)
412+if args.verbose > 1:
413+ logger.setLevel(logging.DEBUG)
414+logger.debug('Options: %s', args)
415 set_ignore_errors(args.ignoreerrors)
416 standard_bug_tags = ['ftbfs', 'arm-porting-queue']
417 if args.bugtag is not None:
418@@ -84,32 +89,35 @@
419 interesting_spph = get_build_status(args.archive, args.series,
420 fail_platform, ok_platforms)
421 except:
422- print 'Error: Could not get build status'
423+ logger.critical('Could not get build status.')
424 raise
425
426 bug_log = Log(args.logfile)
427 for spph in interesting_spph:
428
429 if bug_already_filed_by_url(spph.url, standard_bug_tags, get_launchpad()):
430- print_message("There's already a bug report for %s version %s." % \
431+ logger.info("There's already a bug report for %s version %s." % \
432 (spph.package_name, spph.version))
433 continue
434
435 if bug_log.get_entry(spph.package_name, spph.version,
436 fail_platform) is not None:
437- print_message("Bug already reported for %s version %s. Perhaps it " \
438+ logger.info("Bug already reported for %s version %s. Perhaps it " \
439 "has been closed or retargeted." % \
440 (spph.package_name, spph.version))
441 continue
442
443+ logger.debug("Getting build record for %s." % spph.package_name)
444 build_record = spph.get_arch(fail_platform)
445 if build_record is not None:
446+ logger.debug("Getting build log for %s." % spph.package_name)
447 build_log = get_file_contents(build_record.build_log_url)
448 else:
449- print "%s build record not available for %s version %s." % \
450- (fail_platform, spph.package_name, spph.version)
451+ logger.warning("%s build record not available for %s version %s.",
452+ fail_platform, spph.package_name, spph.version)
453 build_log = ''
454 bug_tags = standard_bug_tags[:]
455+ logger.debug("Processing build log for %s." % spph.package_name)
456 bug_tags.extend(get_tags_for_fail(build_log))
457 interesting_log_part = get_interesting_part(build_log)
458
459@@ -124,7 +132,7 @@
460 """ % (bug_title, build_record.web_link, build_record.build_log_url,
461 interesting_log_part)
462
463- print_message("Will file bug for %s version %s with tags: %s" % \
464+ logger.info("Will file bug for %s version %s with tags: %s" % \
465 (spph.package_name, spph.version, bug_tags))
466 if not args.dryrun:
467 bug = file_bug_by_url(spph.url, bug_description, bug_title,
468@@ -134,11 +142,11 @@
469 LogEntry(spph.package_name, spph.version, fail_platform,
470 bug.bug_tasks[0].self_link).to_string())
471 else:
472- print_message(" ... but didn't since dryrun is specified.")
473+ logger.info(" ... but didn't since dryrun is specified.")
474
475 archives = get_archives(args.archive, args.series)
476 for log_entry in bug_log.get_arch(fail_platform):
477- print_message("Checking for successful builds for package " \
478+ logger.info("Checking for successful builds for package " \
479 "%s version >= %s" % \
480 (log_entry.package_name, log_entry.package_version))
481 success_version = exists_successful_later_build(
482@@ -154,6 +162,6 @@
483 else:
484 bug = get_launchpad().load(log_entry.bugtask_url)
485 for bug_task in bug.bug_tasks:
486- print_message(" Had reason to close the bugtask %s but " \
487- "didn't since dryrun is specified." % \
488- bug_task.web_link)
489+ logger.info("Had reason to close the bugtask %s but " \
490+ "didn't since dryrun is specified." % \
491+ bug_task.web_link)
492
493=== modified file 'tests/test_fail_filer.py'
494--- tests/test_fail_filer.py 2011-04-12 10:43:36 +0000
495+++ tests/test_fail_filer.py 2011-04-15 07:02:24 +0000
496@@ -52,7 +52,6 @@
497 ConfigInitError,
498 LaunchpadError,
499 set_ignore_errors,
500- set_verbose_messages,
501 )
502
503

Subscribers

People subscribed via source and target branches