~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/trace.py

  • Committer: Vincent Ladeuil
  • Date: 2008-01-03 08:49:38 UTC
  • mfrom: (3111.1.31 175524)
  • mto: This revision was merged to the branch mainline in revision 3158.
  • Revision ID: v.ladeuil+lp@free.fr-20080103084938-7kvurk5uvde2ui54
Fix bug #175524, http test servers are 1.1 compliant

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
2
2
#
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
50
50
# is quite expensive, even when the message is not printed by any handlers.
51
51
# We should perhaps change back to just simply doing it here.
52
52
 
 
53
import codecs
 
54
import logging
53
55
import os
54
56
import sys
55
57
import re
56
58
 
57
59
from bzrlib.lazy_import import lazy_import
58
60
lazy_import(globals(), """
 
61
from cStringIO import StringIO
59
62
import errno
60
 
import logging
 
63
import locale
 
64
import traceback
61
65
""")
62
66
 
63
67
import bzrlib
64
 
from bzrlib.symbol_versioning import (deprecated_function,
65
 
        zero_nine,
66
 
        )
67
68
 
68
69
lazy_import(globals(), """
69
 
from bzrlib import debug
 
70
from bzrlib import (
 
71
    debug,
 
72
    errors,
 
73
    osutils,
 
74
    plugin,
 
75
    )
70
76
""")
71
77
 
72
78
_file_handler = None
73
79
_stderr_handler = None
74
 
_stderr_quiet = False
 
80
_verbosity_level = 0
75
81
_trace_file = None
76
82
_trace_depth = 0
77
83
_bzr_log_file = None
 
84
_bzr_log_filename = None
78
85
 
79
86
 
80
87
# configure convenient aliases for output routines
81
88
 
82
89
_bzr_logger = logging.getLogger('bzr')
83
90
 
 
91
 
84
92
def note(*args, **kwargs):
85
93
    # FIXME note always emits utf-8, regardless of the terminal encoding
86
94
    import bzrlib.ui
124
132
    #_trace_file.flush()
125
133
 
126
134
 
 
135
def mutter_callsite(stacklevel, fmt, *args):
 
136
    """Perform a mutter of fmt and args, logging the call trace.
 
137
 
 
138
    :param stacklevel: The number of frames to show. None will show all
 
139
        frames.
 
140
    :param fmt: The format string to pass to mutter.
 
141
    :param args: A list of substitution variables.
 
142
    """
 
143
    outf = StringIO()
 
144
    traceback.print_stack(limit=stacklevel + 1, file=outf)
 
145
    formatted_lines = outf.getvalue().splitlines()
 
146
    formatted_stack = '\n'.join(formatted_lines[:-2])
 
147
    mutter(fmt + "\nCalled from:\n%s", *(args + (formatted_stack,)))
 
148
 
 
149
 
127
150
def _rollover_trace_maybe(trace_fname):
128
151
    import stat
129
152
    try:
131
154
        if size <= 4 << 20:
132
155
            return
133
156
        old_fname = trace_fname + '.old'
134
 
        from osutils import rename
135
 
        rename(trace_fname, old_fname)
 
157
        osutils.rename(trace_fname, old_fname)
136
158
    except OSError:
137
159
        return
138
160
 
141
163
    # Messages are always written to here, so that we have some
142
164
    # information if something goes wrong.  In a future version this
143
165
    # file will be removed on successful completion.
144
 
    global _file_handler, _bzr_log_file
 
166
    global _file_handler, _bzr_log_file, _bzr_log_filename
145
167
    import codecs
146
168
 
147
169
    if tracefilename is None:
150
172
            home = win32utils.get_home_location()
151
173
        else:
152
174
            home = os.path.expanduser('~')
153
 
        tracefilename = os.path.join(home, '.bzr.log')
 
175
        _bzr_log_filename = os.path.join(home, '.bzr.log')
 
176
    else:
 
177
        _bzr_log_filename = tracefilename
154
178
 
155
 
    trace_fname = os.path.expanduser(tracefilename)
156
 
    _rollover_trace_maybe(trace_fname)
 
179
    _bzr_log_filename = os.path.expanduser(_bzr_log_filename)
 
180
    _rollover_trace_maybe(_bzr_log_filename)
157
181
    try:
158
182
        LINE_BUFFERED = 1
159
183
        #tf = codecs.open(trace_fname, 'at', 'utf8', buffering=LINE_BUFFERED)
160
 
        tf = open(trace_fname, 'at', LINE_BUFFERED)
 
184
        tf = open(_bzr_log_filename, 'at', LINE_BUFFERED)
161
185
        _bzr_log_file = tf
162
186
        # tf.tell() on windows always return 0 until some writing done
163
187
        tf.write('\n')
164
188
        if tf.tell() <= 2:
165
189
            tf.write("this is a debug log for diagnosing/reporting problems in bzr\n")
166
190
            tf.write("you can delete or truncate this file, or include sections in\n")
167
 
            tf.write("bug reports to bazaar@lists.canonical.com\n\n")
 
191
            tf.write("bug reports to https://bugs.launchpad.net/bzr/+filebug\n\n")
168
192
        _file_handler = logging.StreamHandler(tf)
169
193
        fmt = r'[%(process)5d] %(asctime)s.%(msecs)03d %(levelname)s: %(message)s'
170
 
        datefmt = r'%a %H:%M:%S'
 
194
        datefmt = r'%Y-%m-%d %H:%M:%S'
171
195
        _file_handler.setFormatter(logging.Formatter(fmt, datefmt))
172
196
        _file_handler.setLevel(logging.DEBUG)
173
197
        logging.getLogger('').addHandler(_file_handler)
175
199
        warning("failed to open trace file: %s" % (e))
176
200
 
177
201
 
178
 
@deprecated_function(zero_nine)
179
 
def log_exception(msg=None):
180
 
    """Log the last exception to stderr and the trace file.
181
 
 
182
 
    The exception string representation is used as the error
183
 
    summary, unless msg is given.
184
 
 
185
 
    Please see log_exception_quietly() for the replacement API.
186
 
    """
187
 
    if msg:
188
 
        error(msg)
189
 
    log_exception_quietly()
190
 
 
191
 
 
192
202
def log_exception_quietly():
193
203
    """Log the last exception to the trace file only.
194
204
 
204
214
    """Configure default logging to stderr and .bzr.log"""
205
215
    # FIXME: if this is run twice, things get confused
206
216
    global _stderr_handler, _file_handler, _trace_file, _bzr_log_file
207
 
    _stderr_handler = logging.StreamHandler()
 
217
    # create encoded wrapper around stderr
 
218
    stderr = codecs.getwriter(osutils.get_terminal_encoding())(sys.stderr,
 
219
        errors='replace')
 
220
    _stderr_handler = logging.StreamHandler(stderr)
208
221
    logging.getLogger('').addHandler(_stderr_handler)
209
222
    _stderr_handler.setLevel(logging.INFO)
210
223
    if not _file_handler:
215
228
    _bzr_logger.setLevel(logging.DEBUG)
216
229
 
217
230
 
 
231
def set_verbosity_level(level):
 
232
    """Set the verbosity level.
 
233
 
 
234
    :param level: -ve for quiet, 0 for normal, +ve for verbose
 
235
    """
 
236
    global _verbosity_level
 
237
    _verbosity_level = level
 
238
    _update_logging_level(level < 0)
 
239
 
 
240
 
 
241
def get_verbosity_level():
 
242
    """Get the verbosity level.
 
243
 
 
244
    See set_verbosity_level() for values.
 
245
    """
 
246
    return _verbosity_level
 
247
 
 
248
 
218
249
def be_quiet(quiet=True):
219
 
    global _stderr_handler, _stderr_quiet
220
 
    
221
 
    _stderr_quiet = quiet
 
250
    # Perhaps this could be deprecated now ...
 
251
    if quiet:
 
252
        set_verbosity_level(-1)
 
253
    else:
 
254
        set_verbosity_level(0)
 
255
 
 
256
 
 
257
def _update_logging_level(quiet=True):
 
258
    """Hide INFO messages if quiet."""
222
259
    if quiet:
223
260
        _stderr_handler.setLevel(logging.WARNING)
224
261
    else:
226
263
 
227
264
 
228
265
def is_quiet():
229
 
    global _stderr_quiet
230
 
    return _stderr_quiet
 
266
    """Is the verbosity level negative?"""
 
267
    return _verbosity_level < 0
 
268
 
 
269
 
 
270
def is_verbose():
 
271
    """Is the verbosity level positive?"""
 
272
    return _verbosity_level > 0
231
273
 
232
274
 
233
275
def disable_default_logging():
276
318
 
277
319
 
278
320
def report_exception(exc_info, err_file):
 
321
    """Report an exception to err_file (typically stderr) and to .bzr.log.
 
322
 
 
323
    This will show either a full traceback or a short message as appropriate.
 
324
 
 
325
    :return: The appropriate exit code for this error.
 
326
    """
279
327
    exc_type, exc_object, exc_tb = exc_info
280
328
    # Log the full traceback to ~/.bzr.log
281
329
    log_exception_quietly()
282
330
    if (isinstance(exc_object, IOError)
283
331
        and getattr(exc_object, 'errno', None) == errno.EPIPE):
284
 
        print >>err_file, "bzr: broken pipe"
 
332
        err_file.write("bzr: broken pipe\n")
 
333
        return errors.EXIT_ERROR
285
334
    elif isinstance(exc_object, KeyboardInterrupt):
286
 
        print >>err_file, "bzr: interrupted"
 
335
        err_file.write("bzr: interrupted\n")
 
336
        return errors.EXIT_ERROR
287
337
    elif not getattr(exc_object, 'internal_error', True):
288
338
        report_user_error(exc_info, err_file)
 
339
        return errors.EXIT_ERROR
289
340
    elif isinstance(exc_object, (OSError, IOError)):
290
341
        # Might be nice to catch all of these and show them as something more
291
342
        # specific, but there are too many cases at the moment.
292
343
        report_user_error(exc_info, err_file)
 
344
        return errors.EXIT_ERROR
293
345
    else:
294
346
        report_bug(exc_info, err_file)
 
347
        return errors.EXIT_INTERNAL_ERROR
295
348
 
296
349
 
297
350
# TODO: Should these be specially encoding the output?
303
356
    if 'error' in debug.debug_flags:
304
357
        report_bug(exc_info, err_file)
305
358
        return
306
 
    print >>err_file, "bzr: ERROR:", str(exc_info[1])
 
359
    err_file.write("bzr: ERROR: %s\n" % (exc_info[1],))
307
360
 
308
361
 
309
362
def report_bug(exc_info, err_file):
310
363
    """Report an exception that probably indicates a bug in bzr"""
311
364
    import traceback
312
365
    exc_type, exc_object, exc_tb = exc_info
313
 
    print >>err_file, "bzr: ERROR: %s.%s: %s" % (
314
 
        exc_type.__module__, exc_type.__name__, exc_object)
315
 
    print >>err_file
 
366
    err_file.write("bzr: ERROR: %s.%s: %s\n" % (
 
367
        exc_type.__module__, exc_type.__name__, exc_object))
 
368
    err_file.write('\n')
316
369
    traceback.print_exception(exc_type, exc_object, exc_tb, file=err_file)
317
 
    print >>err_file
318
 
    print >>err_file, 'bzr %s on python %s (%s)' % \
 
370
    err_file.write('\n')
 
371
    err_file.write('bzr %s on python %s (%s)\n' % \
319
372
                       (bzrlib.__version__,
320
373
                        '.'.join(map(str, sys.version_info)),
321
 
                        sys.platform)
322
 
    print >>err_file, 'arguments: %r' % sys.argv
323
 
    print >>err_file
324
 
    print >>err_file, "** please send this report to bazaar@lists.ubuntu.com"
 
374
                        sys.platform))
 
375
    err_file.write('arguments: %r\n' % sys.argv)
 
376
    err_file.write(
 
377
        'encoding: %r, fsenc: %r, lang: %r\n' % (
 
378
            osutils.get_user_encoding(), sys.getfilesystemencoding(),
 
379
            os.environ.get('LANG')))
 
380
    err_file.write("plugins:\n")
 
381
    for name, a_plugin in sorted(plugin.plugins().items()):
 
382
        err_file.write("  %-20s %s [%s]\n" %
 
383
            (name, a_plugin.path(), a_plugin.__version__))
 
384
    err_file.write(
 
385
"""\
 
386
*** Bazaar has encountered an internal error.
 
387
    Please report a bug at https://bugs.launchpad.net/bzr/+filebug
 
388
    including this traceback, and a description of what you
 
389
    were doing when the error occurred.
 
390
""")