13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
"""Messages and logging for bazaar-ng.
17
"""Messages and logging.
19
19
Messages are supplied by callers as a string-formatting template, plus values
20
20
to be inserted into it. The actual %-formatting is deferred to the log
109
107
def note(*args, **kwargs):
108
"""Output a note to the user.
110
Takes the same parameters as logging.info.
110
114
# FIXME note always emits utf-8, regardless of the terminal encoding
112
116
# FIXME: clearing the ui and then going through the abstract logging
113
117
# framework is whack; we should probably have a logging Handler that
114
118
# deals with terminal output if needed.
116
bzrlib.ui.ui_factory.clear_term()
119
ui.ui_factory.clear_term()
117
120
_bzr_logger.info(*args, **kwargs)
120
123
def warning(*args, **kwargs):
122
bzrlib.ui.ui_factory.clear_term()
124
ui.ui_factory.clear_term()
123
125
_bzr_logger.warning(*args, **kwargs)
126
# configure convenient aliases for output routines
128
# TODO: deprecate them, have one name for each.
130
log_error = _bzr_logger.error
131
error = _bzr_logger.error
128
def show_error(*args, **kwargs):
129
"""Show an error message to the user.
131
Don't use this for exceptions, use report_exception instead.
133
_bzr_logger.error(*args, **kwargs)
134
136
def mutter(fmt, *args):
135
137
if _trace_file is None:
139
# XXX: Don't check this every time; instead anyone who closes the file
140
# ought to deregister it. We can tolerate None.
137
141
if (getattr(_trace_file, 'closed', None) is not None) and _trace_file.closed:
214
218
This sets the global _bzr_log_filename.
216
220
global _bzr_log_filename
222
def _open_or_create_log_file(filename):
223
"""Open existing log file, or create with ownership and permissions
225
It inherits the ownership and permissions (masked by umask) from
226
the containing directory to cope better with being run under sudo
227
with $HOME still set to the user's homedir.
229
flags = os.O_WRONLY | os.O_APPEND | osutils.O_TEXT
232
fd = os.open(filename, flags)
235
if e.errno != errno.ENOENT:
238
fd = os.open(filename, flags | os.O_CREAT | os.O_EXCL, 0666)
240
if e.errno != errno.EEXIST:
243
osutils.copy_ownership_from_path(filename)
245
return os.fdopen(fd, 'at', 0) # unbuffered
217
248
_bzr_log_filename = _get_bzr_log_filename()
218
249
_rollover_trace_maybe(_bzr_log_filename)
220
bzr_log_file = open(_bzr_log_filename, 'at', 1) # line buffered
221
# bzr_log_file.tell() on windows always return 0 until some writing done
251
bzr_log_file = _open_or_create_log_file(_bzr_log_filename)
222
252
bzr_log_file.write('\n')
223
253
if bzr_log_file.tell() <= 2:
224
254
bzr_log_file.write("this is a debug log for diagnosing/reporting problems in bzr\n")
225
255
bzr_log_file.write("you can delete or truncate this file, or include sections in\n")
226
256
bzr_log_file.write("bug reports to https://bugs.launchpad.net/bzr/+filebug\n\n")
227
258
return bzr_log_file
229
warning("failed to open trace file: %s" % (e))
260
except EnvironmentError, e:
261
# If we are failing to open the log, then most likely logging has not
262
# been set up yet. So we just write to stderr rather than using
263
# 'warning()'. If we using warning(), users get the unhelpful 'no
264
# handlers registered for "bzr"' when something goes wrong on the
265
# server. (bug #503886)
266
sys.stderr.write("failed to open trace file: %s\n" % (e,))
230
267
# TODO: What should happen if we fail to open the trace file? Maybe the
231
268
# objects should be pointed at /dev/null or the equivalent? Currently
232
269
# returns None which will cause failures later.
235
273
def enable_default_logging():
236
274
"""Configure default logging: messages to stderr and debug to .bzr.log
238
276
This should only be called once per process.
240
278
Non-command-line programs embedding bzrlib do not need to call this. They
241
279
can instead either pass a file to _push_log_file, or act directly on
242
280
logging.getLogger("bzr").
244
282
Output can be redirected away by calling _push_log_file.
284
:return: A memento from push_log_file for restoring the log state.
286
start_time = osutils.format_local_date(_bzr_log_start_time,
246
288
# create encoded wrapper around stderr
247
289
bzr_log_file = _open_bzr_log()
248
push_log_file(bzr_log_file,
290
if bzr_log_file is not None:
291
bzr_log_file.write(start_time.encode('utf-8') + '\n')
292
memento = push_log_file(bzr_log_file,
249
293
r'[%(process)5d] %(asctime)s.%(msecs)03d %(levelname)s: %(message)s',
250
294
r'%Y-%m-%d %H:%M:%S')
251
295
# after hooking output into bzr_log, we also need to attach a stderr
252
296
# handler, writing only at level info and with encoding
253
writer_factory = codecs.getwriter(osutils.get_terminal_encoding())
297
term_encoding = osutils.get_terminal_encoding()
298
writer_factory = codecs.getwriter(term_encoding)
254
299
encoded_stderr = writer_factory(sys.stderr, errors='replace')
255
300
stderr_handler = logging.StreamHandler(encoded_stderr)
256
301
stderr_handler.setLevel(logging.INFO)
257
302
logging.getLogger('bzr').addHandler(stderr_handler)
260
306
def push_log_file(to_file, log_format=None, date_format=None):
296
342
def pop_log_file((magic, old_handlers, new_handler, old_trace_file, new_trace_file)):
297
343
"""Undo changes to logging/tracing done by _push_log_file.
299
This flushes, but does not close the trace file.
345
This flushes, but does not close the trace file (so that anything that was
301
348
Takes the memento returned from _push_log_file."""
302
349
global _trace_file
303
350
_trace_file = old_trace_file
304
351
bzr_logger = logging.getLogger('bzr')
305
352
bzr_logger.removeHandler(new_handler)
306
# must be closed, otherwise logging will try to close it atexit, and the
353
# must be closed, otherwise logging will try to close it at exit, and the
307
354
# file will likely already be closed underneath.
308
355
new_handler.close()
309
356
bzr_logger.handlers = old_handlers
310
new_trace_file.flush()
313
@symbol_versioning.deprecated_function(symbol_versioning.one_two)
314
def enable_test_log(to_file):
315
"""Redirect logging to a temporary file for a test
317
:returns: an opaque reference that should be passed to disable_test_log
318
after the test completes.
320
return push_log_file(to_file)
323
@symbol_versioning.deprecated_function(symbol_versioning.one_two)
324
def disable_test_log(memento):
325
return pop_log_file(memento)
357
if new_trace_file is not None:
358
new_trace_file.flush()
328
361
def log_exception_quietly():
329
362
"""Log the last exception to the trace file only.
331
Used for exceptions that occur internally and that may be
332
interesting to developers but not to users. For example,
364
Used for exceptions that occur internally and that may be
365
interesting to developers but not to users. For example,
333
366
errors loading plugins.
335
368
mutter(traceback.format_exc())
379
412
return _verbosity_level > 0
382
@symbol_versioning.deprecated_function(symbol_versioning.one_two)
383
def disable_default_logging():
384
"""Turn off default log handlers.
386
Don't call this method, use _push_log_file and _pop_log_file instead.
391
_short_fields = ('VmPeak', 'VmSize', 'VmRSS')
393
415
def debug_memory(message='', short=True):
394
416
"""Write out a memory dump."""
417
if sys.platform == 'win32':
418
from bzrlib import win32utils
419
win32utils.debug_memory_win32api(message=message, short=short)
421
_debug_memory_proc(message=message, short=short)
424
_short_fields = ('VmPeak', 'VmSize', 'VmRSS')
426
def _debug_memory_proc(message='', short=True):
396
428
status_file = file('/proc/%s/status' % os.getpid(), 'rb')
446
def _dump_memory_usage(err_file):
449
fd, name = tempfile.mkstemp(prefix="bzr_memdump", suffix=".json")
450
dump_file = os.fdopen(fd, 'w')
451
from meliae import scanner
452
scanner.dump_gc_objects(dump_file)
453
err_file.write("Memory dumped to %s\n" % name)
455
err_file.write("Dumping memory requires meliae module.\n")
456
log_exception_quietly()
458
err_file.write("Exception while dumping memory.\n")
459
log_exception_quietly()
461
if dump_file is not None:
467
def _qualified_exception_name(eclass, unqualified_bzrlib_errors=False):
468
"""Give name of error class including module for non-builtin exceptions
470
If `unqualified_bzrlib_errors` is True, errors specific to bzrlib will
471
also omit the module prefix.
473
class_name = eclass.__name__
474
module_name = eclass.__module__
475
if module_name in ("exceptions", "__main__") or (
476
unqualified_bzrlib_errors and module_name == "bzrlib.errors"):
478
return "%s.%s" % (module_name, class_name)
415
481
def report_exception(exc_info, err_file):
416
482
"""Report an exception to err_file (typically stderr) and to .bzr.log.
420
486
:return: The appropriate exit code for this error.
422
exc_type, exc_object, exc_tb = exc_info
423
488
# Log the full traceback to ~/.bzr.log
424
489
log_exception_quietly()
490
if 'error' in debug.debug_flags:
491
print_exception(exc_info, err_file)
492
return errors.EXIT_ERROR
493
exc_type, exc_object, exc_tb = exc_info
425
494
if (isinstance(exc_object, IOError)
426
495
and getattr(exc_object, 'errno', None) == errno.EPIPE):
427
496
err_file.write("bzr: broken pipe\n")
429
498
elif isinstance(exc_object, KeyboardInterrupt):
430
499
err_file.write("bzr: interrupted\n")
431
500
return errors.EXIT_ERROR
501
elif isinstance(exc_object, MemoryError):
502
err_file.write("bzr: out of memory\n")
503
if 'mem_dump' in debug.debug_flags:
504
_dump_memory_usage(err_file)
506
err_file.write("Use -Dmem_dump to dump memory to a file.\n")
507
return errors.EXIT_ERROR
432
508
elif isinstance(exc_object, ImportError) \
433
509
and str(exc_object).startswith("No module named "):
434
510
report_user_error(exc_info, err_file,
437
513
elif not getattr(exc_object, 'internal_error', True):
438
514
report_user_error(exc_info, err_file)
439
515
return errors.EXIT_ERROR
440
elif isinstance(exc_object, (OSError, IOError)):
516
elif isinstance(exc_object, (OSError, IOError)) or (
517
# GZ 2010-05-20: Like (exc_type is pywintypes.error) but avoid import
518
exc_type.__name__ == "error" and exc_type.__module__ == "pywintypes"):
441
519
# Might be nice to catch all of these and show them as something more
442
520
# specific, but there are too many cases at the moment.
443
521
report_user_error(exc_info, err_file)
476
551
def report_bug(exc_info, err_file):
477
552
"""Report an exception that probably indicates a bug in bzr"""
478
print_exception(exc_info, err_file)
480
err_file.write('bzr %s on python %s (%s)\n' % \
482
bzrlib._format_version_tuple(sys.version_info),
484
err_file.write('arguments: %r\n' % sys.argv)
486
'encoding: %r, fsenc: %r, lang: %r\n' % (
487
osutils.get_user_encoding(), sys.getfilesystemencoding(),
488
os.environ.get('LANG')))
489
err_file.write("plugins:\n")
490
for name, a_plugin in sorted(plugin.plugins().items()):
491
err_file.write(" %-20s %s [%s]\n" %
492
(name, a_plugin.path(), a_plugin.__version__))
495
*** Bazaar has encountered an internal error.
496
Please report a bug at https://bugs.launchpad.net/bzr/+filebug
497
including this traceback, and a description of what you
498
were doing when the error occurred.
553
from bzrlib.crash import report_bug
554
report_bug(exc_info, err_file)
557
def _flush_stdout_stderr():
558
# called from the bzrlib library finalizer returned by bzrlib.initialize()
564
if e.errno in [errno.EINVAL, errno.EPIPE]:
571
# called from the bzrlib library finalizer returned by bzrlib.initialize()
577
class Config(object):
578
"""Configuration of message tracing in bzrlib.
580
This implements the context manager protocol and should manage any global
581
variables still used. The default config used is DefaultConfig, but
582
embedded uses of bzrlib may wish to use a custom manager.
586
return self # This is bound to the 'as' clause in a with statement.
588
def __exit__(self, exc_type, exc_val, exc_tb):
589
return False # propogate exceptions.
592
class DefaultConfig(Config):
593
"""A default configuration for tracing of messages in bzrlib.
595
This implements the context manager protocol.
599
self._original_filename = _bzr_log_filename
600
self._original_state = enable_default_logging()
601
return self # This is bound to the 'as' clause in a with statement.
603
def __exit__(self, exc_type, exc_val, exc_tb):
604
pop_log_file(self._original_state)
605
global _bzr_log_filename
606
_bzr_log_filename = self._original_filename
607
return False # propogate exceptions.