~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/trace.py

Merge from integration.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# This program is free software; you can redistribute it and/or modify
2
 
# it under the terms of the GNU General Public License as published by
3
 
# the Free Software Foundation; either version 2 of the License, or
4
 
# (at your option) any later version.
5
 
 
6
 
# This program is distributed in the hope that it will be useful,
7
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
8
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
9
 
# GNU General Public License for more details.
10
 
 
11
 
# You should have received a copy of the GNU General Public License
12
 
# along with this program; if not, write to the Free Software
13
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
14
 
 
 
1
# Copyright (C) 2005, Canonical Ltd
15
2
 
16
3
"""Messages and logging for bazaar-ng.
17
4
 
27
14
those two places to another location.
28
15
 
29
16
~/.bzr.log gets all messages, and full tracebacks for uncaught exceptions.
 
17
This trace file is always in UTF-8, regardless of the user's default encoding,
 
18
so that we can always rely on writing any message.
30
19
 
31
20
Output to stderr depends on the mode chosen by the user.  By default, messages
32
21
of info and above are sent out, which results in progress messages such as the
43
32
form.
44
33
"""
45
34
 
46
 
 
47
35
# TODO: in debug mode, stderr should get full tracebacks and also
48
36
# debug messages.  (Is this really needed?)
49
37
 
50
 
# TODO: When running the test suites, we should add an additional
51
 
# logger that sends messages into the test log file.
52
 
 
53
38
# FIXME: Unfortunately it turns out that python's logging module
54
39
# is quite expensive, even when the message is not printed by any handlers.
55
40
# We should perhaps change back to just simply doing it here.
60
45
import logging
61
46
 
62
47
import bzrlib
63
 
from bzrlib.errors import BzrNewError
 
48
from bzrlib.errors import BzrError, BzrNewError
64
49
 
65
50
 
66
51
_file_handler = None
67
52
_stderr_handler = None
 
53
_stderr_quiet = False
 
54
_trace_file = None
 
55
_bzr_log_file = None
 
56
 
68
57
 
69
58
class QuietFormatter(logging.Formatter):
70
59
    """Formatter that supresses the details of errors.
85
74
            s += '\n' + format_exception_short(record.exc_info)
86
75
        return s
87
76
        
88
 
 
89
 
 
90
 
 
91
 
################
92
77
# configure convenient aliases for output routines
93
78
 
94
79
_bzr_logger = logging.getLogger('bzr')
97
82
warning =   _bzr_logger.warning
98
83
log_error = _bzr_logger.error
99
84
error =     _bzr_logger.error
100
 
mutter =    _bzr_logger.debug
101
 
debug =     _bzr_logger.debug
 
85
 
 
86
 
 
87
def mutter(fmt, *args):
 
88
    if _trace_file is None:
 
89
        return
 
90
    if hasattr(_trace_file, 'closed') and _trace_file.closed:
 
91
        return
 
92
    if len(args) > 0:
 
93
        out = fmt % args
 
94
    else:
 
95
        out = fmt
 
96
    out += '\n'
 
97
    _trace_file.write(out)
 
98
debug = mutter
102
99
 
103
100
 
104
101
def _rollover_trace_maybe(trace_fname):
118
115
    # Messages are always written to here, so that we have some
119
116
    # information if something goes wrong.  In a future version this
120
117
    # file will be removed on successful completion.
121
 
    global _file_handler
 
118
    global _file_handler, _bzr_log_file
122
119
    import stat, codecs
123
120
 
124
121
    trace_fname = os.path.join(os.path.expanduser(tracefilename))
126
123
    try:
127
124
        LINE_BUFFERED = 1
128
125
        tf = codecs.open(trace_fname, 'at', 'utf8', buffering=LINE_BUFFERED)
129
 
 
130
 
        if os.fstat(tf.fileno())[stat.ST_SIZE] == 0:
 
126
        _bzr_log_file = tf
 
127
        if tf.tell() == 0:
131
128
            tf.write("\nthis is a debug log for diagnosing/reporting problems in bzr\n")
132
129
            tf.write("you can delete or truncate this file, or include sections in\n")
133
130
            tf.write("bug reports to bazaar-ng@lists.canonical.com\n\n")
134
 
        
135
131
        _file_handler = logging.StreamHandler(tf)
136
132
        fmt = r'[%(process)5d] %(asctime)s.%(msecs)03d %(levelname)s: %(message)s'
137
133
        datefmt = r'%a %H:%M:%S'
143
139
 
144
140
 
145
141
def log_startup(argv):
146
 
    debug('bzr %s invoked on python %s (%s)',
 
142
    debug('\n\nbzr %s invoked on python %s (%s)',
147
143
          bzrlib.__version__,
148
144
          '.'.join(map(str, sys.version_info)),
149
145
          sys.platform)
157
153
    The exception string representation is used as the error
158
154
    summary, unless msg is given.
159
155
    """
160
 
    exc_str = format_exception_short(sys.exc_info())
161
156
    if msg:
162
 
        _bzr_logger.exception(msg)
163
 
    _bzr_logger.error(exc_str)
 
157
        error(msg)
 
158
    else:
 
159
        exc_str = format_exception_short(sys.exc_info())
 
160
        error(exc_str)
 
161
    log_exception_quietly()
164
162
 
165
163
 
166
164
def log_exception_quietly():
176
174
 
177
175
def enable_default_logging():
178
176
    """Configure default logging to stderr and .bzr.log"""
179
 
    global _stderr_handler, _file_handler
180
 
 
 
177
    # FIXME: if this is run twice, things get confused
 
178
    global _stderr_handler, _file_handler, _trace_file, _bzr_log_file
181
179
    _stderr_handler = logging.StreamHandler()
182
180
    _stderr_handler.setFormatter(QuietFormatter())
183
181
    logging.getLogger('').addHandler(_stderr_handler)
184
 
 
185
 
    if os.environ.get('BZR_DEBUG'):
186
 
        level = logging.DEBUG
187
 
    else:
188
 
        level = logging.INFO
189
 
 
190
182
    _stderr_handler.setLevel(logging.INFO)
191
 
 
192
183
    if not _file_handler:
193
184
        open_tracefile()
194
 
 
 
185
    _trace_file = _bzr_log_file
195
186
    if _file_handler:
196
 
        _file_handler.setLevel(level)
197
 
 
198
 
    _bzr_logger.setLevel(level) 
 
187
        _file_handler.setLevel(logging.DEBUG)
 
188
    _bzr_logger.setLevel(logging.DEBUG) 
 
189
 
 
190
 
 
191
 
 
192
def be_quiet(quiet=True):
 
193
    global _stderr_handler, _stderr_quiet
 
194
    
 
195
    _stderr_quiet = quiet
 
196
    if quiet:
 
197
        _stderr_handler.setLevel(logging.WARNING)
 
198
    else:
 
199
        _stderr_handler.setLevel(logging.INFO)
 
200
 
 
201
 
 
202
def is_quiet():
 
203
    global _stderr_quiet
 
204
    return _stderr_quiet
 
205
 
199
206
 
200
207
def disable_default_logging():
201
208
    """Turn off default log handlers.
208
215
    l.removeHandler(_stderr_handler)
209
216
    if _file_handler:
210
217
        l.removeHandler(_file_handler)
 
218
    _trace_file = None
211
219
 
212
220
 
213
221
def enable_test_log(to_file):
214
222
    """Redirect logging to a temporary file for a test"""
215
223
    disable_default_logging()
216
 
    global _test_log_hdlr
 
224
    global _test_log_hdlr, _trace_file
217
225
    hdlr = logging.StreamHandler(to_file)
218
226
    hdlr.setLevel(logging.DEBUG)
219
227
    hdlr.setFormatter(logging.Formatter('%(levelname)8s  %(message)s'))
220
 
    logging.getLogger('').addHandler(hdlr)
221
 
    logging.getLogger('').setLevel(logging.DEBUG)
 
228
    _bzr_logger.addHandler(hdlr)
 
229
    _bzr_logger.setLevel(logging.DEBUG)
222
230
    _test_log_hdlr = hdlr
 
231
    _trace_file = to_file
223
232
 
224
233
 
225
234
def disable_test_log():
226
 
    logging.getLogger('').removeHandler(_test_log_hdlr)
 
235
    _bzr_logger.removeHandler(_test_log_hdlr)
 
236
    _trace_file = None
227
237
    enable_default_logging()
228
238
 
229
239
 
241
251
    try:
242
252
        if exc_type is None:
243
253
            return '(no exception)'
244
 
        if isinstance(exc_object, BzrNewError):
 
254
        if isinstance(exc_object, (BzrError, BzrNewError)):
245
255
            return str(exc_object)
246
256
        else:
247
257
            import traceback
252
262
            if tb:
253
263
                msg += '\n  at %s line %d\n  in %s' % (tb[-1][:3])
254
264
            return msg
255
 
    except:
256
 
        return '(error formatting exception of type %s)' % exc_type
 
265
    except Exception, formatting_exc:
 
266
        # XXX: is this really better than just letting it run up?
 
267
        return '(error formatting exception of type %s: %s)' \
 
268
                % (exc_type, formatting_exc)