48
from __future__ import absolute_import
48
50
# FIXME: Unfortunately it turns out that python's logging module
49
51
# is quite expensive, even when the message is not printed by any handlers.
50
52
# We should perhaps change back to just simply doing it here.
114
# FIXME note always emits utf-8, regardless of the terminal encoding
116
115
# FIXME: clearing the ui and then going through the abstract logging
117
116
# framework is whack; we should probably have a logging Handler that
118
117
# deals with terminal output if needed.
286
285
start_time = osutils.format_local_date(_bzr_log_start_time,
287
286
timezone='local')
288
# create encoded wrapper around stderr
289
287
bzr_log_file = _open_bzr_log()
290
288
if bzr_log_file is not None:
291
289
bzr_log_file.write(start_time.encode('utf-8') + '\n')
294
292
r'%Y-%m-%d %H:%M:%S')
295
293
# after hooking output into bzr_log, we also need to attach a stderr
296
294
# handler, writing only at level info and with encoding
297
term_encoding = osutils.get_terminal_encoding()
298
writer_factory = codecs.getwriter(term_encoding)
299
encoded_stderr = writer_factory(sys.stderr, errors='replace')
300
stderr_handler = logging.StreamHandler(encoded_stderr)
301
stderr_handler.setLevel(logging.INFO)
295
stderr_handler = EncodedStreamHandler(sys.stderr,
296
osutils.get_terminal_encoding(), 'replace', level=logging.INFO)
302
297
logging.getLogger('bzr').addHandler(stderr_handler)
314
309
global _trace_file
315
310
# make a new handler
316
new_handler = logging.StreamHandler(to_file)
317
new_handler.setLevel(logging.DEBUG)
311
new_handler = EncodedStreamHandler(to_file, "utf-8", level=logging.DEBUG)
318
312
if log_format is None:
319
313
log_format = '%(levelname)8s %(message)s'
320
314
new_handler.setFormatter(logging.Formatter(log_format, date_format))
491
485
print_exception(exc_info, err_file)
492
486
return errors.EXIT_ERROR
493
487
exc_type, exc_object, exc_tb = exc_info
494
if (isinstance(exc_object, IOError)
495
and getattr(exc_object, 'errno', None) == errno.EPIPE):
496
err_file.write("bzr: broken pipe\n")
497
return errors.EXIT_ERROR
498
elif isinstance(exc_object, KeyboardInterrupt):
488
if isinstance(exc_object, KeyboardInterrupt):
499
489
err_file.write("bzr: interrupted\n")
500
490
return errors.EXIT_ERROR
501
491
elif isinstance(exc_object, MemoryError):
513
503
elif not getattr(exc_object, 'internal_error', True):
514
504
report_user_error(exc_info, err_file)
515
505
return errors.EXIT_ERROR
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"):
506
elif osutils.is_environment_error(exc_object):
507
if getattr(exc_object, 'errno', None) == errno.EPIPE:
508
err_file.write("bzr: broken pipe\n")
509
return errors.EXIT_ERROR
519
510
# Might be nice to catch all of these and show them as something more
520
511
# specific, but there are too many cases at the moment.
521
512
report_user_error(exc_info, err_file)
574
569
_trace_file.flush()
572
class EncodedStreamHandler(logging.Handler):
573
"""Robustly write logging events to a stream using the specified encoding
575
Messages are expected to be formatted to unicode, but UTF-8 byte strings
576
are also accepted. An error during formatting or a str message in another
577
encoding will be quitely noted as an error in the Bazaar log file.
579
The stream is not closed so sys.stdout or sys.stderr may be passed.
582
def __init__(self, stream, encoding=None, errors='strict', level=0):
583
logging.Handler.__init__(self, level)
586
encoding = getattr(stream, "encoding", "ascii")
587
self.encoding = encoding
591
flush = getattr(self.stream, "flush", None)
592
if flush is not None:
595
def emit(self, record):
597
line = self.format(record)
598
if not isinstance(line, unicode):
599
line = line.decode("utf-8")
600
self.stream.write(line.encode(self.encoding, self.errors) + "\n")
602
log_exception_quietly()
603
# Try saving the details that would have been logged in some form
604
msg = args = "<Unformattable>"
606
msg = repr(record.msg).encode("ascii")
607
args = repr(record.args).encode("ascii")
610
# Using mutter() bypasses the logging module and writes directly
611
# to the file so there's no danger of getting into a loop here.
612
mutter("Logging record unformattable: %s %% %s", msg, args)
577
615
class Config(object):
578
616
"""Configuration of message tracing in bzrlib.