28
42
Exceptions are reported in a brief form to stderr so as not to look scary.
29
43
BzrErrors are required to be able to format themselves into a properly
30
explanatory message. This is not true for builtin excexceptions such as
44
explanatory message. This is not true for builtin exceptions such as
31
45
KeyError, which typically just str to "0". They're printed in a different
35
# TODO: in debug mode, stderr should get full tracebacks and also
36
# debug messages. (Is this really needed?)
38
49
# FIXME: Unfortunately it turns out that python's logging module
39
50
# is quite expensive, even when the message is not printed by any handlers.
40
51
# We should perhaps change back to just simply doing it here.
57
from bzrlib.lazy_import import lazy_import
58
lazy_import(globals(), """
61
from cStringIO import StringIO
48
from bzrlib.errors import BzrError, BzrNewError
65
from bzrlib.symbol_versioning import (deprecated_function,
69
lazy_import(globals(), """
70
from bzrlib import debug
51
73
_file_handler = None
52
74
_stderr_handler = None
53
75
_stderr_quiet = False
55
78
_bzr_log_file = None
58
class QuietFormatter(logging.Formatter):
59
"""Formatter that supresses the details of errors.
61
This is used by default on stderr so as not to scare the user.
63
# At first I tried overriding formatException to suppress the
64
# exception details, but that has global effect: no loggers
65
# can get the exception details is we suppress them here.
67
def format(self, record):
68
if record.levelno >= logging.WARNING:
69
s = 'bzr: ' + record.levelname + ': '
72
s += record.getMessage()
74
s += '\n' + format_exception_short(record.exc_info)
77
82
# configure convenient aliases for output routines
79
84
_bzr_logger = logging.getLogger('bzr')
81
86
def note(*args, **kwargs):
87
# FIXME note always emits utf-8, regardless of the terminal encoding
83
89
bzrlib.ui.ui_factory.clear_term()
84
90
_bzr_logger.info(*args, **kwargs)
231
238
"""Redirect logging to a temporary file for a test
233
240
returns an opaque reference that should be passed to disable_test_log
234
after the test complete.
241
after the test completes.
236
243
disable_default_logging()
237
244
global _trace_file
238
246
hdlr = logging.StreamHandler(to_file)
239
247
hdlr.setLevel(logging.DEBUG)
240
248
hdlr.setFormatter(logging.Formatter('%(levelname)8s %(message)s'))
241
249
_bzr_logger.addHandler(hdlr)
242
250
_bzr_logger.setLevel(logging.DEBUG)
243
result = hdlr, _trace_file
251
result = hdlr, _trace_file, _trace_depth
244
252
_trace_file = to_file
248
def disable_test_log((test_log_hdlr, old_trace_file)):
257
def disable_test_log((test_log_hdlr, old_trace_file, old_trace_depth)):
249
258
_bzr_logger.removeHandler(test_log_hdlr)
250
259
test_log_hdlr.close()
251
262
_trace_file = old_trace_file
252
enable_default_logging()
255
def format_exception_short(exc_info):
256
"""Make a short string form of an exception.
258
This is used for display to stderr. It specially handles exception
259
classes without useful string methods.
261
The result has no trailing newline.
263
exc_info - typically an exception from sys.exc_info()
263
_trace_depth = old_trace_depth
265
enable_default_logging()
268
def report_exception(exc_info, err_file):
269
exc_type, exc_object, exc_tb = exc_info
270
# Log the full traceback to ~/.bzr.log
271
log_exception_quietly()
272
if (isinstance(exc_object, IOError)
273
and getattr(exc_object, 'errno', None) == errno.EPIPE):
274
print >>err_file, "bzr: broken pipe"
275
elif isinstance(exc_object, KeyboardInterrupt):
276
print >>err_file, "bzr: interrupted"
277
elif not getattr(exc_object, 'internal_error', True):
278
report_user_error(exc_info, err_file)
279
elif isinstance(exc_object, (OSError, IOError)):
280
# Might be nice to catch all of these and show them as something more
281
# specific, but there are too many cases at the moment.
282
report_user_error(exc_info, err_file)
284
return report_bug(exc_info, err_file)
287
# TODO: Should these be specially encoding the output?
288
def report_user_error(exc_info, err_file):
289
"""Report to err_file an error that's not an internal error.
291
These don't get a traceback unless -Derror was given.
265
exc_type, exc_object, exc_tb = exc_info
268
return '(no exception)'
269
if isinstance(exc_object, (BzrError, BzrNewError)):
270
return str(exc_object)
273
tb = traceback.extract_tb(exc_tb)
274
msg = '%s: %s' % (exc_type, exc_object)
278
msg += '\n at %s line %d\n in %s' % (tb[-1][:3])
280
except Exception, formatting_exc:
281
# XXX: is this really better than just letting it run up?
282
return '(error formatting exception of type %s: %s)' \
283
% (exc_type, formatting_exc)
293
if 'error' in debug.debug_flags:
294
report_bug(exc_info, err_file)
296
print >>err_file, "bzr: ERROR:", str(exc_info[1])
299
def report_bug(exc_info, err_file):
300
"""Report an exception that probably indicates a bug in bzr"""
301
# local import because its only needed here, and this is not a loop.
303
# local import because the other functions do it too.
305
# local import due to circular dependency
309
# detect apport presence.
311
import problem_report
313
# not present, dont use it.
316
# policy disabled, or not present, use the old ui.
317
return _old_report_bug(exc_info, err_file)
319
exc_type, exc_object, exc_tb = exc_info
321
"bzr: ERROR: %s.%s: %s\n" % (
322
exc_type.__module__, exc_type.__name__, exc_object)
324
report = problem_report.ProblemReport()
325
report_file, report_filename = tempfile.mkstemp(
326
suffix='.txt', prefix='bzr-crash-', dir='/tmp')
327
python_report_file = os.fdopen(report_file, 'w')
329
report['CommandLine'] = ' '.join(sys.argv)
330
# assume we are packaged as bzr.
331
apport_utils.report_add_package_info(report, 'bzr')
332
report['BzrPlugins'] = ' '.join(bzrlib.plugin.all_plugins())
334
traceback.print_exception(exc_type, exc_object, exc_tb, file=tb_file)
335
report['Traceback'] = tb_file.getvalue()
336
apport_utils.report_add_os_info(report)
337
report.write(python_report_file)
338
# give the user a pretty output.
341
'This is an unexpected error within bzr and we would appreciate a bug report.\n'
343
'bzr has written a crash report file that will assist our debugging of this\n'
346
'This is a plain text file, whose contents you can check if you have privacy\n'
347
'concerns. We gather the package data about bzr, your command line, plugins\n'
348
'And the backtrace from within bzr. If you had a password in the URL you\n'
349
'provided to bzr, you should edit that file to remove the password.\n'
351
'** To file a bug for this please visit our bugtracker at the URL \n'
352
'"https://launchpad.net/products/bzr/+filebug" and report a bug describing\n'
353
'what you were attempting and attach the bzr-crash file mentioned above.\n'
354
'Alternatively you can email bazaar-ng@lists.canonical.com with the same\n'
355
'description and attach the bzr-crash file to the email.\n' %
359
python_report_file.close()
360
return report, report_filename
362
def _old_report_bug(exc_info, err_file):
363
"""Write a synopsis of an exception that is probably a bug to err_file."""
365
exc_type, exc_object, exc_tb = exc_info
366
print >>err_file, "bzr: ERROR: %s.%s: %s" % (
367
exc_type.__module__, exc_type.__name__, exc_object)
369
traceback.print_exception(exc_type, exc_object, exc_tb, file=err_file)
371
print >>err_file, 'bzr %s on python %s (%s)' % \
373
'.'.join(map(str, sys.version_info)),
375
print >>err_file, 'arguments: %r' % sys.argv
377
print >>err_file, "** please send this report to bazaar-ng@lists.ubuntu.com"