1
# Copyright (C) 2005, 2006 Canonical Ltd
1
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
50
50
# is quite expensive, even when the message is not printed by any handlers.
51
51
# We should perhaps change back to just simply doing it here.
57
59
from bzrlib.lazy_import import lazy_import
58
60
lazy_import(globals(), """
61
from cStringIO import StringIO
64
from bzrlib.symbol_versioning import (deprecated_function,
68
69
lazy_import(globals(), """
69
from bzrlib import debug
72
78
_file_handler = None
73
79
_stderr_handler = None
77
83
_bzr_log_file = None
84
_bzr_log_filename = None
80
87
# configure convenient aliases for output routines
82
89
_bzr_logger = logging.getLogger('bzr')
84
92
def note(*args, **kwargs):
85
93
# FIXME note always emits utf-8, regardless of the terminal encoding
124
132
#_trace_file.flush()
135
def mutter_callsite(stacklevel, fmt, *args):
136
"""Perform a mutter of fmt and args, logging the call trace.
138
:param stacklevel: The number of frames to show. None will show all
140
:param fmt: The format string to pass to mutter.
141
:param args: A list of substitution variables.
144
traceback.print_stack(limit=stacklevel + 1, file=outf)
145
formatted_lines = outf.getvalue().splitlines()
146
formatted_stack = '\n'.join(formatted_lines[:-2])
147
mutter(fmt + "\nCalled from:\n%s", *(args + (formatted_stack,)))
127
150
def _rollover_trace_maybe(trace_fname):
141
163
# Messages are always written to here, so that we have some
142
164
# information if something goes wrong. In a future version this
143
165
# file will be removed on successful completion.
144
global _file_handler, _bzr_log_file
166
global _file_handler, _bzr_log_file, _bzr_log_filename
147
169
if tracefilename is None:
150
172
home = win32utils.get_home_location()
152
174
home = os.path.expanduser('~')
153
tracefilename = os.path.join(home, '.bzr.log')
175
_bzr_log_filename = os.path.join(home, '.bzr.log')
177
_bzr_log_filename = tracefilename
155
trace_fname = os.path.expanduser(tracefilename)
156
_rollover_trace_maybe(trace_fname)
179
_bzr_log_filename = os.path.expanduser(_bzr_log_filename)
180
_rollover_trace_maybe(_bzr_log_filename)
158
182
LINE_BUFFERED = 1
159
183
#tf = codecs.open(trace_fname, 'at', 'utf8', buffering=LINE_BUFFERED)
160
tf = open(trace_fname, 'at', LINE_BUFFERED)
184
tf = open(_bzr_log_filename, 'at', LINE_BUFFERED)
161
185
_bzr_log_file = tf
162
186
# tf.tell() on windows always return 0 until some writing done
164
188
if tf.tell() <= 2:
165
189
tf.write("this is a debug log for diagnosing/reporting problems in bzr\n")
166
190
tf.write("you can delete or truncate this file, or include sections in\n")
167
tf.write("bug reports to bazaar@lists.canonical.com\n\n")
191
tf.write("bug reports to https://bugs.launchpad.net/bzr/+filebug\n\n")
168
192
_file_handler = logging.StreamHandler(tf)
169
193
fmt = r'[%(process)5d] %(asctime)s.%(msecs)03d %(levelname)s: %(message)s'
170
datefmt = r'%a %H:%M:%S'
194
datefmt = r'%Y-%m-%d %H:%M:%S'
171
195
_file_handler.setFormatter(logging.Formatter(fmt, datefmt))
172
196
_file_handler.setLevel(logging.DEBUG)
173
197
logging.getLogger('').addHandler(_file_handler)
175
199
warning("failed to open trace file: %s" % (e))
178
@deprecated_function(zero_nine)
179
def log_exception(msg=None):
180
"""Log the last exception to stderr and the trace file.
182
The exception string representation is used as the error
183
summary, unless msg is given.
185
Please see log_exception_quietly() for the replacement API.
189
log_exception_quietly()
192
202
def log_exception_quietly():
193
203
"""Log the last exception to the trace file only.
204
214
"""Configure default logging to stderr and .bzr.log"""
205
215
# FIXME: if this is run twice, things get confused
206
216
global _stderr_handler, _file_handler, _trace_file, _bzr_log_file
207
_stderr_handler = logging.StreamHandler()
217
# create encoded wrapper around stderr
218
stderr = codecs.getwriter(osutils.get_terminal_encoding())(sys.stderr,
220
_stderr_handler = logging.StreamHandler(stderr)
208
221
logging.getLogger('').addHandler(_stderr_handler)
209
222
_stderr_handler.setLevel(logging.INFO)
210
223
if not _file_handler:
215
228
_bzr_logger.setLevel(logging.DEBUG)
231
def set_verbosity_level(level):
232
"""Set the verbosity level.
234
:param level: -ve for quiet, 0 for normal, +ve for verbose
236
global _verbosity_level
237
_verbosity_level = level
238
_update_logging_level(level < 0)
241
def get_verbosity_level():
242
"""Get the verbosity level.
244
See set_verbosity_level() for values.
246
return _verbosity_level
218
249
def be_quiet(quiet=True):
219
global _stderr_handler, _stderr_quiet
221
_stderr_quiet = quiet
250
# Perhaps this could be deprecated now ...
252
set_verbosity_level(-1)
254
set_verbosity_level(0)
257
def _update_logging_level(quiet=True):
258
"""Hide INFO messages if quiet."""
223
260
_stderr_handler.setLevel(logging.WARNING)
266
"""Is the verbosity level negative?"""
267
return _verbosity_level < 0
271
"""Is the verbosity level positive?"""
272
return _verbosity_level > 0
233
275
def disable_default_logging():
278
320
def report_exception(exc_info, err_file):
321
"""Report an exception to err_file (typically stderr) and to .bzr.log.
323
This will show either a full traceback or a short message as appropriate.
325
:return: The appropriate exit code for this error.
279
327
exc_type, exc_object, exc_tb = exc_info
280
328
# Log the full traceback to ~/.bzr.log
281
329
log_exception_quietly()
282
330
if (isinstance(exc_object, IOError)
283
331
and getattr(exc_object, 'errno', None) == errno.EPIPE):
284
print >>err_file, "bzr: broken pipe"
332
err_file.write("bzr: broken pipe\n")
333
return errors.EXIT_ERROR
285
334
elif isinstance(exc_object, KeyboardInterrupt):
286
print >>err_file, "bzr: interrupted"
335
err_file.write("bzr: interrupted\n")
336
return errors.EXIT_ERROR
287
337
elif not getattr(exc_object, 'internal_error', True):
288
338
report_user_error(exc_info, err_file)
339
return errors.EXIT_ERROR
289
340
elif isinstance(exc_object, (OSError, IOError)):
290
341
# Might be nice to catch all of these and show them as something more
291
342
# specific, but there are too many cases at the moment.
292
343
report_user_error(exc_info, err_file)
344
return errors.EXIT_ERROR
294
346
report_bug(exc_info, err_file)
347
return errors.EXIT_INTERNAL_ERROR
297
350
# TODO: Should these be specially encoding the output?
303
356
if 'error' in debug.debug_flags:
304
357
report_bug(exc_info, err_file)
306
print >>err_file, "bzr: ERROR:", str(exc_info[1])
359
err_file.write("bzr: ERROR: %s\n" % (exc_info[1],))
309
362
def report_bug(exc_info, err_file):
310
363
"""Report an exception that probably indicates a bug in bzr"""
312
365
exc_type, exc_object, exc_tb = exc_info
313
print >>err_file, "bzr: ERROR: %s.%s: %s" % (
314
exc_type.__module__, exc_type.__name__, exc_object)
366
err_file.write("bzr: ERROR: %s.%s: %s\n" % (
367
exc_type.__module__, exc_type.__name__, exc_object))
316
369
traceback.print_exception(exc_type, exc_object, exc_tb, file=err_file)
318
print >>err_file, 'bzr %s on python %s (%s)' % \
371
err_file.write('bzr %s on python %s (%s)\n' % \
319
372
(bzrlib.__version__,
320
373
'.'.join(map(str, sys.version_info)),
322
print >>err_file, 'arguments: %r' % sys.argv
324
print >>err_file, "** please send this report to bazaar@lists.ubuntu.com"
375
err_file.write('arguments: %r\n' % sys.argv)
377
'encoding: %r, fsenc: %r, lang: %r\n' % (
378
osutils.get_user_encoding(), sys.getfilesystemencoding(),
379
os.environ.get('LANG')))
380
err_file.write("plugins:\n")
381
for name, a_plugin in sorted(plugin.plugins().items()):
382
err_file.write(" %-20s %s [%s]\n" %
383
(name, a_plugin.path(), a_plugin.__version__))
386
*** Bazaar has encountered an internal error.
387
Please report a bug at https://bugs.launchpad.net/bzr/+filebug
388
including this traceback, and a description of what you
389
were doing when the error occurred.