23
23
Messages are classified by severity levels: critical, error, warning, info,
26
They can be sent to two places: to stderr, and to ~/.bzr.log. For purposes
26
They can be sent to two places: to stderr, and to ~/.brz.log. For purposes
27
27
such as running the test suite, they can also be redirected away from both of
28
28
those two places to another location.
30
~/.bzr.log gets all messages, and full tracebacks for uncaught exceptions.
30
~/.brz.log gets all messages, and full tracebacks for uncaught exceptions.
31
31
This trace file is always in UTF-8, regardless of the user's default encoding,
32
32
so that we can always rely on writing any message.
92
92
# than push/pop_log_file.
95
# Absolute path for ~/.bzr.log. Not changed even if the log/trace output is
95
# Absolute path for ~/.brz.log. Not changed even if the log/trace output is
96
96
# redirected elsewhere. Used to show the location in --version.
97
_bzr_log_filename = None
97
_brz_log_filename = None
99
99
# The time the first message was written to the trace file, so that we can
100
100
# show relative times since startup.
101
_bzr_log_start_time = brzlib._start_time
101
_brz_log_start_time = brzlib._start_time
104
104
# held in a global for quick reference
105
_bzr_logger = logging.getLogger('bzr')
105
_brz_logger = logging.getLogger('brz')
108
108
def note(*args, **kwargs):
116
116
# framework is whack; we should probably have a logging Handler that
117
117
# deals with terminal output if needed.
118
118
ui.ui_factory.clear_term()
119
_bzr_logger.info(*args, **kwargs)
119
_brz_logger.info(*args, **kwargs)
122
122
def warning(*args, **kwargs):
123
123
ui.ui_factory.clear_term()
124
_bzr_logger.warning(*args, **kwargs)
124
_brz_logger.warning(*args, **kwargs)
127
127
def show_error(*args, **kwargs):
130
130
Don't use this for exceptions, use report_exception instead.
132
_bzr_logger.error(*args, **kwargs)
132
_brz_logger.error(*args, **kwargs)
135
135
def mutter(fmt, *args):
158
158
now = time.time()
159
timestamp = '%0.3f ' % (now - _bzr_log_start_time,)
159
timestamp = '%0.3f ' % (now - _brz_log_start_time,)
160
160
out = timestamp + out + '\n'
161
161
_trace_file.write(out)
162
162
# there's no explicit flushing; the file is typically line buffered.
196
def _get_bzr_log_filename():
197
bzr_log = osutils.path_from_environ('BRZ_LOG')
196
def _get_brz_log_filename():
197
brz_log = osutils.path_from_environ('BRZ_LOG')
200
200
home = osutils.path_from_environ('BRZ_HOME')
202
202
# GZ 2012-02-01: Logging to the home dir is bad, but XDG is unclear
203
203
# over what would be better. On windows, bug 240550
204
204
# suggests LOCALAPPDATA be used instead.
205
205
home = osutils._get_home_dir()
206
return os.path.join(home, '.bzr.log')
210
"""Open the .bzr.log trace file.
206
return os.path.join(home, '.brz.log')
210
"""Open the .brz.log trace file.
212
212
If the log is more than a particular length, the old file is renamed to
213
.bzr.log.old and a new file is started. Otherwise, we append to the
213
.brz.log.old and a new file is started. Otherwise, we append to the
216
This sets the global _bzr_log_filename.
216
This sets the global _brz_log_filename.
218
global _bzr_log_filename
218
global _brz_log_filename
220
220
def _open_or_create_log_file(filename):
221
221
"""Open existing log file, or create with ownership and permissions
241
241
osutils.copy_ownership_from_path(filename)
243
return os.fdopen(fd, 'at', 0) # unbuffered
246
_bzr_log_filename = _get_bzr_log_filename()
247
_rollover_trace_maybe(_bzr_log_filename)
243
return os.fdopen(fd, 'at', 0) # unbuffered
245
_brz_log_filename = _get_brz_log_filename()
246
_rollover_trace_maybe(_brz_log_filename)
249
bzr_log_file = _open_or_create_log_file(_bzr_log_filename)
250
bzr_log_file.write('\n')
251
if bzr_log_file.tell() <= 2:
252
bzr_log_file.write("this is a debug log for diagnosing/reporting problems in bzr\n")
253
bzr_log_file.write("you can delete or truncate this file, or include sections in\n")
254
bzr_log_file.write("bug reports to https://bugs.launchpad.net/bzr/+filebug\n\n")
248
brz_log_file = _open_or_create_log_file(_brz_log_filename)
249
brz_log_file.write('\n')
250
if brz_log_file.tell() <= 2:
251
brz_log_file.write("this is a debug log for diagnosing/reporting problems in brz\n")
252
brz_log_file.write("you can delete or truncate this file, or include sections in\n")
253
brz_log_file.write("bug reports to https://bugs.launchpad.net/brz/+filebug\n\n")
258
257
except EnvironmentError, e:
259
258
# If we are failing to open the log, then most likely logging has not
260
259
# been set up yet. So we just write to stderr rather than using
261
260
# 'warning()'. If we using warning(), users get the unhelpful 'no
262
# handlers registered for "bzr"' when something goes wrong on the
261
# handlers registered for "brz"' when something goes wrong on the
263
262
# server. (bug #503886)
264
263
sys.stderr.write("failed to open trace file: %s\n" % (e,))
265
264
# TODO: What should happen if we fail to open the trace file? Maybe the
271
270
def enable_default_logging():
272
"""Configure default logging: messages to stderr and debug to .bzr.log
271
"""Configure default logging: messages to stderr and debug to .brz.log
274
273
This should only be called once per process.
276
275
Non-command-line programs embedding brzlib do not need to call this. They
277
276
can instead either pass a file to _push_log_file, or act directly on
278
logging.getLogger("bzr").
277
logging.getLogger("brz").
280
279
Output can be redirected away by calling _push_log_file.
282
281
:return: A memento from push_log_file for restoring the log state.
284
start_time = osutils.format_local_date(_bzr_log_start_time,
283
start_time = osutils.format_local_date(_brz_log_start_time,
285
284
timezone='local')
286
bzr_log_file = _open_bzr_log()
287
if bzr_log_file is not None:
288
bzr_log_file.write(start_time.encode('utf-8') + '\n')
289
memento = push_log_file(bzr_log_file,
285
brz_log_file = _open_brz_log()
286
if brz_log_file is not None:
287
brz_log_file.write(start_time.encode('utf-8') + '\n')
288
memento = push_log_file(brz_log_file,
290
289
r'[%(process)5d] %(asctime)s.%(msecs)03d %(levelname)s: %(message)s',
291
290
r'%Y-%m-%d %H:%M:%S')
292
# after hooking output into bzr_log, we also need to attach a stderr
291
# after hooking output into brz_log, we also need to attach a stderr
293
292
# handler, writing only at level info and with encoding
294
293
stderr_handler = EncodedStreamHandler(sys.stderr,
295
294
osutils.get_terminal_encoding(), 'replace', level=logging.INFO)
296
logging.getLogger('bzr').addHandler(stderr_handler)
295
logging.getLogger('brz').addHandler(stderr_handler)
312
311
log_format = '%(levelname)8s %(message)s'
313
312
new_handler.setFormatter(logging.Formatter(log_format, date_format))
314
313
# save and remove any existing log handlers
315
bzr_logger = logging.getLogger('bzr')
316
old_handlers = bzr_logger.handlers[:]
317
del bzr_logger.handlers[:]
314
brz_logger = logging.getLogger('brz')
315
old_handlers = brz_logger.handlers[:]
316
del brz_logger.handlers[:]
318
317
# set that as the default logger
319
bzr_logger.addHandler(new_handler)
320
bzr_logger.setLevel(logging.DEBUG)
318
brz_logger.addHandler(new_handler)
319
brz_logger.setLevel(logging.DEBUG)
321
320
# TODO: check if any changes are needed to the root logger
323
# TODO: also probably need to save and restore the level on bzr_logger.
322
# TODO: also probably need to save and restore the level on brz_logger.
324
323
# but maybe we can avoid setting the logger level altogether, and just set
325
324
# the level on the handler?
341
340
Takes the memento returned from _push_log_file."""
342
341
global _trace_file
343
342
_trace_file = old_trace_file
344
bzr_logger = logging.getLogger('bzr')
345
bzr_logger.removeHandler(new_handler)
343
brz_logger = logging.getLogger('brz')
344
brz_logger.removeHandler(new_handler)
346
345
# must be closed, otherwise logging will try to close it at exit, and the
347
346
# file will likely already be closed underneath.
348
347
new_handler.close()
349
bzr_logger.handlers = old_handlers
348
brz_logger.handlers = old_handlers
350
349
if new_trace_file is not None:
351
350
new_trace_file.flush()
390
389
def _update_logging_level(quiet=True):
391
390
"""Hide INFO messages if quiet."""
393
_bzr_logger.setLevel(logging.WARNING)
392
_brz_logger.setLevel(logging.WARNING)
395
_bzr_logger.setLevel(logging.INFO)
394
_brz_logger.setLevel(logging.INFO)
439
438
def _dump_memory_usage(err_file):
442
fd, name = tempfile.mkstemp(prefix="bzr_memdump", suffix=".json")
441
fd, name = tempfile.mkstemp(prefix="brz_memdump", suffix=".json")
443
442
dump_file = os.fdopen(fd, 'w')
444
443
from meliae import scanner
445
444
scanner.dump_gc_objects(dump_file)
474
473
def report_exception(exc_info, err_file):
475
"""Report an exception to err_file (typically stderr) and to .bzr.log.
474
"""Report an exception to err_file (typically stderr) and to .brz.log.
477
476
This will show either a full traceback or a short message as appropriate.
479
478
:return: The appropriate exit code for this error.
481
# Log the full traceback to ~/.bzr.log
480
# Log the full traceback to ~/.brz.log
482
481
log_exception_quietly()
483
482
if 'error' in debug.debug_flags:
484
483
print_exception(exc_info, err_file)
541
540
def report_bug(exc_info, err_file):
542
"""Report an exception that probably indicates a bug in bzr"""
541
"""Report an exception that probably indicates a bug in brz"""
543
542
from brzlib.crash import report_bug
544
543
report_bug(exc_info, err_file)
635
634
def __enter__(self):
636
self._original_filename = _bzr_log_filename
635
self._original_filename = _brz_log_filename
637
636
self._original_state = enable_default_logging()
638
637
return self # This is bound to the 'as' clause in a with statement.
640
639
def __exit__(self, exc_type, exc_val, exc_tb):
641
640
pop_log_file(self._original_state)
642
global _bzr_log_filename
643
_bzr_log_filename = self._original_filename
641
global _brz_log_filename
642
_brz_log_filename = self._original_filename
644
643
return False # propogate exceptions.