Merge lp:~mabac/svammel/extra-verbose-option into lp:svammel
- extra-verbose-option
- Merge into trunk
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 | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
James Westby (community) | Approve | ||
Matthias Klose | Pending | ||
Review via email: mp+57120@code.launchpad.net |
Commit message
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
Guilherme Salgado (salgado) wrote : | # |
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.
Mattias Backman (mabac) wrote : | # |
Perhaps there also should be a nice formatter, or is a format like the example below ok?
INFO:
James Westby (james-w) wrote : | # |
202 parser.
203 + '-v2', '--debug', action=
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
- 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
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 |
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?