~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/trace.py

[merge] bzr.dev

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
68
 
 
 
53
_stderr_quiet = False
 
54
_trace_file = None
 
55
_bzr_log_file = None
69
56
 
70
57
class QuietFormatter(logging.Formatter):
71
58
    """Formatter that supresses the details of errors.
86
73
            s += '\n' + format_exception_short(record.exc_info)
87
74
        return s
88
75
        
89
 
 
90
 
 
91
 
 
92
 
################
93
76
# configure convenient aliases for output routines
94
77
 
95
78
_bzr_logger = logging.getLogger('bzr')
98
81
warning =   _bzr_logger.warning
99
82
log_error = _bzr_logger.error
100
83
error =     _bzr_logger.error
101
 
mutter =    _bzr_logger.debug
102
 
debug =     _bzr_logger.debug
 
84
 
 
85
 
 
86
def mutter(fmt, *args):
 
87
    if _trace_file is None:
 
88
        return
 
89
    if hasattr(_trace_file, 'closed') and _trace_file.closed:
 
90
        return
 
91
    if len(args) > 0:
 
92
        out = fmt % args
 
93
    else:
 
94
        out = fmt
 
95
    out += '\n'
 
96
    if isinstance(out, unicode):
 
97
        out = out.encode('utf-8')
 
98
    _trace_file.write(out)
 
99
debug = mutter
103
100
 
104
101
 
105
102
def _rollover_trace_maybe(trace_fname):
119
116
    # Messages are always written to here, so that we have some
120
117
    # information if something goes wrong.  In a future version this
121
118
    # file will be removed on successful completion.
122
 
    global _file_handler
 
119
    global _file_handler, _bzr_log_file
123
120
    import stat, codecs
124
121
 
125
122
    trace_fname = os.path.join(os.path.expanduser(tracefilename))
127
124
    try:
128
125
        LINE_BUFFERED = 1
129
126
        tf = codecs.open(trace_fname, 'at', 'utf8', buffering=LINE_BUFFERED)
130
 
 
131
 
        if os.fstat(tf.fileno())[stat.ST_SIZE] == 0:
 
127
        _bzr_log_file = tf
 
128
        if tf.tell() == 0:
132
129
            tf.write("\nthis is a debug log for diagnosing/reporting problems in bzr\n")
133
130
            tf.write("you can delete or truncate this file, or include sections in\n")
134
131
            tf.write("bug reports to bazaar-ng@lists.canonical.com\n\n")
135
 
        
136
132
        _file_handler = logging.StreamHandler(tf)
137
133
        fmt = r'[%(process)5d] %(asctime)s.%(msecs)03d %(levelname)s: %(message)s'
138
134
        datefmt = r'%a %H:%M:%S'
144
140
 
145
141
 
146
142
def log_startup(argv):
147
 
    debug('bzr %s invoked on python %s (%s)',
 
143
    debug('\n\nbzr %s invoked on python %s (%s)',
148
144
          bzrlib.__version__,
149
145
          '.'.join(map(str, sys.version_info)),
150
146
          sys.platform)
158
154
    The exception string representation is used as the error
159
155
    summary, unless msg is given.
160
156
    """
161
 
    exc_str = format_exception_short(sys.exc_info())
162
157
    if msg:
163
 
        _bzr_logger.exception(msg)
164
 
    _bzr_logger.error(exc_str)
 
158
        error(msg)
 
159
    else:
 
160
        exc_str = format_exception_short(sys.exc_info())
 
161
        error(exc_str)
 
162
    log_exception_quietly()
165
163
 
166
164
 
167
165
def log_exception_quietly():
177
175
 
178
176
def enable_default_logging():
179
177
    """Configure default logging to stderr and .bzr.log"""
180
 
    global _stderr_handler, _file_handler
181
 
 
 
178
    # FIXME: if this is run twice, things get confused
 
179
    global _stderr_handler, _file_handler, _trace_file, _bzr_log_file
182
180
    _stderr_handler = logging.StreamHandler()
183
181
    _stderr_handler.setFormatter(QuietFormatter())
184
182
    logging.getLogger('').addHandler(_stderr_handler)
185
 
 
186
 
    if os.environ.get('BZR_DEBUG'):
187
 
        level = logging.DEBUG
188
 
    else:
189
 
        level = logging.INFO
190
 
 
191
183
    _stderr_handler.setLevel(logging.INFO)
192
 
 
193
184
    if not _file_handler:
194
185
        open_tracefile()
195
 
 
 
186
    _trace_file = _bzr_log_file
196
187
    if _file_handler:
197
 
        _file_handler.setLevel(level)
198
 
 
199
 
    _bzr_logger.setLevel(level) 
 
188
        _file_handler.setLevel(logging.DEBUG)
 
189
    _bzr_logger.setLevel(logging.DEBUG) 
 
190
 
 
191
 
 
192
 
 
193
def be_quiet(quiet=True):
 
194
    global _stderr_handler, _stderr_quiet
 
195
    
 
196
    _stderr_quiet = quiet
 
197
    if quiet:
 
198
        _stderr_handler.setLevel(logging.WARNING)
 
199
    else:
 
200
        _stderr_handler.setLevel(logging.INFO)
 
201
 
 
202
 
 
203
def is_quiet():
 
204
    global _stderr_quiet
 
205
    return _stderr_quiet
 
206
 
200
207
 
201
208
def disable_default_logging():
202
209
    """Turn off default log handlers.
209
216
    l.removeHandler(_stderr_handler)
210
217
    if _file_handler:
211
218
        l.removeHandler(_file_handler)
 
219
    _trace_file = None
212
220
 
213
221
 
214
222
def enable_test_log(to_file):
215
223
    """Redirect logging to a temporary file for a test"""
216
224
    disable_default_logging()
217
 
    global _test_log_hdlr
 
225
    global _test_log_hdlr, _trace_file
218
226
    hdlr = logging.StreamHandler(to_file)
219
227
    hdlr.setLevel(logging.DEBUG)
220
228
    hdlr.setFormatter(logging.Formatter('%(levelname)8s  %(message)s'))
221
 
    logging.getLogger('').addHandler(hdlr)
222
 
    logging.getLogger('').setLevel(logging.DEBUG)
 
229
    _bzr_logger.addHandler(hdlr)
 
230
    _bzr_logger.setLevel(logging.DEBUG)
223
231
    _test_log_hdlr = hdlr
 
232
    _trace_file = to_file
224
233
 
225
234
 
226
235
def disable_test_log():
227
 
    logging.getLogger('').removeHandler(_test_log_hdlr)
 
236
    _bzr_logger.removeHandler(_test_log_hdlr)
 
237
    _trace_file = None
228
238
    enable_default_logging()
229
239
 
230
240
 
242
252
    try:
243
253
        if exc_type is None:
244
254
            return '(no exception)'
245
 
        if isinstance(exc_object, BzrNewError):
 
255
        if isinstance(exc_object, (BzrError, BzrNewError)):
246
256
            return str(exc_object)
247
257
        else:
248
258
            import traceback
253
263
            if tb:
254
264
                msg += '\n  at %s line %d\n  in %s' % (tb[-1][:3])
255
265
            return msg
256
 
    except:
257
 
        return '(error formatting exception of type %s)' % exc_type
 
266
    except Exception, formatting_exc:
 
267
        # XXX: is this really better than just letting it run up?
 
268
        return '(error formatting exception of type %s: %s)' \
 
269
                % (exc_type, formatting_exc)