13
13
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16
__copyright__ = "Copyright (C) 2005 Canonical Ltd."
17
__author__ = "Martin Pool <mbp@canonical.com>"
20
16
"""Messages and logging for bazaar-ng
22
Nothing is actually logged unless you call bzrlib.open_tracefile().
18
Messages are sent out through the Python logging library.
20
They can be sent to two places: to stderr, and to ~/.bzr.log.
22
~/.bzr.log gets all messages, and tracebacks of all uncaught
25
Normally stderr only gets messages of level INFO and higher, and gets
26
only a summary of exceptions, not the traceback.
28
######################################################################
29
# messages and logging
31
global _tracefile, _starttime
34
# used to have % (os.environ['USER'], time.time(), os.getpid()), 'w')
37
# If false, notes also go to stdout; should replace this with --silent
42
# fix this if we ever fork within python
44
_logprefix = '[%d] ' % _mypid
47
def _write_trace(msg):
49
_tracefile.write(_logprefix + msg + '\n')
53
sys.stderr.write('bzr: warning: ' + msg + '\n')
54
_write_trace('warning: ' + msg)
61
b = '* ' + str(msg) + '\n'
64
_write_trace('note: ' + msg)
68
sys.stderr.write(msg + '\n')
30
# TODO: in debug mode, stderr should get full tracebacks and also
31
# debug messages. (Is this really needed?)
33
# TODO: When running the test suites, we should add an additional
34
# logger that sends messages into the test log file.
36
# FIXME: Unfortunately it turns out that python's logging module
37
# is quite expensive, even when the message is not printed by any handlers.
38
# We should perhaps change back to just simply doing it here.
48
_stderr_handler = None
51
class QuietFormatter(logging.Formatter):
52
"""Formatter that supresses the details of errors.
54
This is used by default on stderr so as not to scare the user.
56
# At first I tried overriding formatException to suppress the
57
# exception details, but that has global effect: no loggers
58
# can get the exception details is we suppress them here.
60
def format(self, record):
61
if record.levelno >= logging.WARNING:
62
s = 'bzr: ' + record.levelname + ': '
66
s += record.getMessage()
69
##s = textwrap.fill(s)
72
# give just a summary of the exception, not the whole thing
73
exinfo = traceback.extract_tb(record.exc_info[2])
74
# the format of this string matches one of the REs
76
s += (' at %s line %d, in %s()\n' % exinfo[-1][:3])
77
s += ' see ~/.bzr.log for debug information'
85
# configure convenient aliases for output routines
87
_bzr_logger = logging.getLogger('bzr')
89
info = note = _bzr_logger.info
90
warning = _bzr_logger.warning
91
log_error = _bzr_logger.error
92
error = _bzr_logger.error
93
mutter = _bzr_logger.debug
94
debug = _bzr_logger.debug
99
# we do the rollover using this code, rather than the default from python
100
# logging, because we only want to rollover at program startup, not on each
101
# message. maybe that's not a good enough reason.
72
103
def _rollover_trace_maybe(trace_fname):
78
109
old_fname = trace_fname + '.old'
81
# must remove before rename on windows
111
from osutils import rename
112
rename(trace_fname, old_fname)
87
# might fail if in use on windows
88
os.rename(trace_fname, old_fname)
96
def open_tracefile(argv=[], tracefilename='~/.bzr.log'):
119
def open_tracefile(tracefilename='~/.bzr.log'):
97
120
# Messages are always written to here, so that we have some
98
121
# information if something goes wrong. In a future version this
99
122
# file will be removed on successful completion.
100
global _starttime, _tracefile
101
124
import stat, codecs
103
_starttime = os.times()[4]
105
126
trace_fname = os.path.join(os.path.expanduser(tracefilename))
106
127
_rollover_trace_maybe(trace_fname)
108
129
# buffering=1 means line buffered
110
_tracefile = codecs.open(trace_fname, 'at', 'utf8', buffering=1)
113
if os.fstat(t.fileno())[stat.ST_SIZE] == 0:
114
t.write("\nthis is a debug log for diagnosing/reporting problems in bzr\n")
115
t.write("you can delete or truncate this file, or include sections in\n")
116
t.write("bug reports to bazaar-ng@lists.canonical.com\n\n")
119
_write_trace('bzr %s invoked on python %s (%s)'
120
% (bzrlib.__version__,
121
'.'.join(map(str, sys.version_info)),
124
_write_trace(' arguments: %r' % argv)
125
_write_trace(' working dir: ' + os.getcwdu())
131
tf = codecs.open(trace_fname, 'at', 'utf8', buffering=1)
133
if os.fstat(tf.fileno())[stat.ST_SIZE] == 0:
134
tf.write("\nthis is a debug log for diagnosing/reporting problems in bzr\n")
135
tf.write("you can delete or truncate this file, or include sections in\n")
136
tf.write("bug reports to bazaar-ng@lists.canonical.com\n\n")
138
_file_handler = logging.StreamHandler(tf)
139
fmt = r'[%(process)5d] %(asctime)s.%(msecs)03d %(levelname)s: %(message)s'
140
datefmt = r'%a %H:%M:%S'
141
_file_handler.setFormatter(logging.Formatter(fmt, datefmt))
142
_file_handler.setLevel(logging.DEBUG)
143
logging.getLogger('').addHandler(_file_handler)
126
145
except IOError, e:
127
146
warning("failed to open trace file: %s" % (e))
131
mutter("finished, %.3fu/%.3fs cpu, %.3fu/%.3fs cum, %.3f elapsed"
132
% (times[:4] + ((times[4] - _starttime),)))
137
"""Log the last exception into the trace file."""
139
s = cgitb.text(sys.exc_info())
140
for l in s.split('\n'):
149
def log_startup(argv):
152
debug('bzr %s invoked on python %s (%s)',
154
'.'.join(map(str, sys.version_info)),
157
debug(' arguments: %r', argv)
158
debug(' working dir: %r', os.getcwdu())
161
def log_exception(msg=None):
162
"""Log the last exception to stderr and the trace file.
164
The exception string representation is used as the error
165
summary, unless msg is given.
172
msg = "Error logging exception of type %s\n" % ei[1].__class__.__name__
177
if msg and (msg[-1] == '\n'):
179
msg += '\n command: %s' % ' '.join(repr(arg) for arg in sys.argv)
180
msg += '\n pwd: %r' % os.getcwdu()
181
msg += '\n error: %s' % ei[0] # exception type
182
_bzr_logger.exception(msg)
185
def log_exception_quietly():
186
"""Log the last exception to the trace file only.
188
Used for exceptions that occur internally and that may be
189
interesting to developers but not to users. For example,
190
errors loading plugins.
192
debug(traceback.format_exc())
195
def enable_default_logging():
196
"""Configure default logging to stderr and .bzr.log"""
197
global _stderr_handler, _file_handler
199
_stderr_handler = logging.StreamHandler()
200
_stderr_handler.setFormatter(QuietFormatter())
202
if not _file_handler:
205
if os.environ.get('BZR_DEBUG'):
206
level = logging.DEBUG
210
_stderr_handler.setLevel(logging.INFO)
211
_file_handler.setLevel(level)
212
_bzr_logger.setLevel(level)
214
logging.getLogger('').addHandler(_stderr_handler)
217
def disable_default_logging():
218
"""Turn off default log handlers.
220
This is intended to be used by the test framework, which doesn't
221
want leakage from the code-under-test into the main logs.
224
l = logging.getLogger('')
225
l.removeHandler(_stderr_handler)
227
l.removeHandler(_file_handler)