1
# Copyright (C) 2005-2010 Canonical Ltd
1
# Copyright (C) 2005-2011 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
54
54
# increased cost of logging.py is not so bad, and we could standardize on
64
62
from bzrlib.lazy_import import lazy_import
65
63
lazy_import(globals(), """
66
64
from cStringIO import StringIO
74
from bzrlib.symbol_versioning import (
79
73
lazy_import(globals(), """
80
74
from bzrlib import (
121
# FIXME note always emits utf-8, regardless of the terminal encoding
123
113
# FIXME: clearing the ui and then going through the abstract logging
124
114
# framework is whack; we should probably have a logging Handler that
125
115
# deals with terminal output if needed.
127
bzrlib.ui.ui_factory.clear_term()
116
ui.ui_factory.clear_term()
128
117
_bzr_logger.info(*args, **kwargs)
131
120
def warning(*args, **kwargs):
133
bzrlib.ui.ui_factory.clear_term()
121
ui.ui_factory.clear_term()
134
122
_bzr_logger.warning(*args, **kwargs)
137
@deprecated_function(deprecated_in((2, 1, 0)))
138
def info(*args, **kwargs):
139
"""Deprecated: use trace.note instead."""
140
note(*args, **kwargs)
143
@deprecated_function(deprecated_in((2, 1, 0)))
144
def log_error(*args, **kwargs):
145
"""Deprecated: use bzrlib.trace.show_error instead"""
146
_bzr_logger.error(*args, **kwargs)
149
@deprecated_function(deprecated_in((2, 1, 0)))
150
def error(*args, **kwargs):
151
"""Deprecated: use bzrlib.trace.show_error instead"""
152
_bzr_logger.error(*args, **kwargs)
155
125
def show_error(*args, **kwargs):
156
126
"""Show an error message to the user.
313
283
start_time = osutils.format_local_date(_bzr_log_start_time,
314
284
timezone='local')
315
# create encoded wrapper around stderr
316
285
bzr_log_file = _open_bzr_log()
317
286
if bzr_log_file is not None:
318
287
bzr_log_file.write(start_time.encode('utf-8') + '\n')
321
290
r'%Y-%m-%d %H:%M:%S')
322
291
# after hooking output into bzr_log, we also need to attach a stderr
323
292
# handler, writing only at level info and with encoding
324
term_encoding = osutils.get_terminal_encoding()
325
writer_factory = codecs.getwriter(term_encoding)
326
encoded_stderr = writer_factory(sys.stderr, errors='replace')
327
stderr_handler = logging.StreamHandler(encoded_stderr)
328
stderr_handler.setLevel(logging.INFO)
293
stderr_handler = EncodedStreamHandler(sys.stderr,
294
osutils.get_terminal_encoding(), 'replace', level=logging.INFO)
329
295
logging.getLogger('bzr').addHandler(stderr_handler)
336
302
:param to_file: A file-like object to which messages will be sent.
338
304
:returns: A memento that should be passed to _pop_log_file to restore the
339
previously active logging.
305
previously active logging.
341
307
global _trace_file
342
308
# make a new handler
343
new_handler = logging.StreamHandler(to_file)
344
new_handler.setLevel(logging.DEBUG)
309
new_handler = EncodedStreamHandler(to_file, "utf-8", level=logging.DEBUG)
345
310
if log_format is None:
346
311
log_format = '%(levelname)8s %(message)s'
347
312
new_handler.setFormatter(logging.Formatter(log_format, date_format))
490
455
elif fd is not None:
459
def _qualified_exception_name(eclass, unqualified_bzrlib_errors=False):
460
"""Give name of error class including module for non-builtin exceptions
462
If `unqualified_bzrlib_errors` is True, errors specific to bzrlib will
463
also omit the module prefix.
465
class_name = eclass.__name__
466
module_name = eclass.__module__
467
if module_name in ("exceptions", "__main__") or (
468
unqualified_bzrlib_errors and module_name == "bzrlib.errors"):
470
return "%s.%s" % (module_name, class_name)
493
473
def report_exception(exc_info, err_file):
494
474
"""Report an exception to err_file (typically stderr) and to .bzr.log.
503
483
print_exception(exc_info, err_file)
504
484
return errors.EXIT_ERROR
505
485
exc_type, exc_object, exc_tb = exc_info
506
if (isinstance(exc_object, IOError)
507
and getattr(exc_object, 'errno', None) == errno.EPIPE):
508
err_file.write("bzr: broken pipe\n")
509
return errors.EXIT_ERROR
510
elif isinstance(exc_object, KeyboardInterrupt):
486
if isinstance(exc_object, KeyboardInterrupt):
511
487
err_file.write("bzr: interrupted\n")
512
488
return errors.EXIT_ERROR
513
489
elif isinstance(exc_object, MemoryError):
525
501
elif not getattr(exc_object, 'internal_error', True):
526
502
report_user_error(exc_info, err_file)
527
503
return errors.EXIT_ERROR
528
elif isinstance(exc_object, (OSError, IOError)) or (
529
# GZ 2010-05-20: Like (exc_type is pywintypes.error) but avoid import
530
exc_type.__name__ == "error" and exc_type.__module__ == "pywintypes"):
504
elif osutils.is_environment_error(exc_object):
505
if getattr(exc_object, 'errno', None) == errno.EPIPE:
506
err_file.write("bzr: broken pipe\n")
507
return errors.EXIT_ERROR
531
508
# Might be nice to catch all of these and show them as something more
532
509
# specific, but there are too many cases at the moment.
533
510
report_user_error(exc_info, err_file)
586
567
_trace_file.flush()
570
class EncodedStreamHandler(logging.Handler):
571
"""Robustly write logging events to a stream using the specified encoding
573
Messages are expected to be formatted to unicode, but UTF-8 byte strings
574
are also accepted. An error during formatting or a str message in another
575
encoding will be quitely noted as an error in the Bazaar log file.
577
The stream is not closed so sys.stdout or sys.stderr may be passed.
580
def __init__(self, stream, encoding=None, errors='strict', level=0):
581
logging.Handler.__init__(self, level)
584
encoding = getattr(stream, "encoding", "ascii")
585
self.encoding = encoding
589
flush = getattr(self.stream, "flush", None)
590
if flush is not None:
593
def emit(self, record):
595
line = self.format(record)
596
if not isinstance(line, unicode):
597
line = line.decode("utf-8")
598
self.stream.write(line.encode(self.encoding, self.errors) + "\n")
600
log_exception_quietly()
601
# Try saving the details that would have been logged in some form
602
msg = args = "<Unformattable>"
604
msg = repr(record.msg).encode("ascii")
605
args = repr(record.args).encode("ascii")
608
# Using mutter() bypasses the logging module and writes directly
609
# to the file so there's no danger of getting into a loop here.
610
mutter("Logging record unformattable: %s %% %s", msg, args)
589
613
class Config(object):
590
614
"""Configuration of message tracing in bzrlib.