~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/trace.py

  • Committer: Martin Pool
  • Date: 2005-08-24 00:34:21 UTC
  • Revision ID: mbp@sourcefrog.net-20050824003421-33dd8e5c739cad2a
- send trace messages out through python logging module

Show diffs side-by-side

added added

removed removed

Lines of Context:
13
13
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
14
14
 
15
15
 
16
 
 
17
 
# TODO: Could probably replace this with something based on Python logging
18
 
# module.
19
 
 
20
 
 
21
 
 
22
 
 
23
 
__copyright__ = "Copyright (C) 2005 Canonical Ltd."
24
 
__author__ = "Martin Pool <mbp@canonical.com>"
25
 
 
26
 
 
27
16
"""Messages and logging for bazaar-ng
28
17
 
29
 
Nothing is actually logged unless you call bzrlib.open_tracefile().
 
18
Messages are sent out through the Python logging library.
 
19
 
 
20
They can be sent to two places: to stderr, and to ~/.bzr.log.
 
21
 
 
22
~/.bzr.log gets all messages, and tracebacks of all uncaught
 
23
exceptions.
 
24
 
 
25
Normally stderr only gets messages of level INFO and higher, and gets
 
26
only a summary of exceptions, not the traceback.
30
27
"""
31
28
 
32
29
 
33
 
import sys, os
34
 
 
35
 
######################################################################
36
 
# messages and logging
37
 
 
38
 
global _tracefile, _starttime
39
 
_tracefile = None
40
 
 
41
 
# used to have % (os.environ['USER'], time.time(), os.getpid()), 'w')
42
 
_starttime = None
43
 
 
44
 
# If false, notes also go to stdout; should replace this with --silent
45
 
# at some point.
46
 
silent = False
47
 
 
48
 
 
49
 
# fix this if we ever fork within python
50
 
_mypid = os.getpid()
51
 
_logprefix = '[%d] ' % _mypid
52
 
 
53
 
 
54
 
def _write_trace(msg):
55
 
    if _tracefile:
56
 
        _tracefile.write(_logprefix + msg + '\n')
57
 
 
58
 
 
59
 
def warning(msg):
60
 
    sys.stderr.write('bzr: warning: ' + msg + '\n')
61
 
    _write_trace('warning: ' + msg)
62
 
 
63
 
 
64
 
mutter = _write_trace
65
 
 
66
 
 
67
 
def note(msg):
68
 
    b = '* ' + str(msg) + '\n'
69
 
    if not silent:
70
 
        sys.stderr.write(b)
71
 
    _write_trace('note: ' + msg)
72
 
 
73
 
 
74
 
def log_error(msg):
75
 
    sys.stderr.write(msg + '\n')
76
 
    _write_trace(msg)
77
 
 
 
30
# TODO: in debug mode, stderr should get full tracebacks and also
 
31
# debug messages.  (Is this really needed?)
 
32
 
 
33
# TODO: When running the test suites, we should add an additional
 
34
# logger that sends messages into the test log file.
 
35
 
 
36
 
 
37
import sys
 
38
import os
 
39
import logging
 
40
import traceback
 
41
 
 
42
 
 
43
class QuietFormatter(logging.Formatter):
 
44
    """Formatter that supresses the details of errors.
 
45
 
 
46
    This is used by default on stderr so as not to scare the user.
 
47
    """
 
48
    # At first I tried overriding formatException to suppress the
 
49
    # exception details, but that has global effect: no loggers
 
50
    # can get the exception details is we suppress them here.
 
51
 
 
52
    def format(self, record):
 
53
        s = 'bzr: '
 
54
        if record.levelno >= logging.WARNING:
 
55
            s += record.levelname + ': '
 
56
            
 
57
        s += record.getMessage() 
 
58
            
 
59
        if record.exc_info:
 
60
            # give just a summary of the exception, not the whole thing
 
61
            exinfo = traceback.extract_tb(record.exc_info[2])
 
62
            # the format of this string matches one of the REs
 
63
            s += '\n'
 
64
            s += ('  at %s line %d, in %s()\n' % exinfo[-1][:3])
 
65
            s += '  see ~/.bzr.log for debug information'
 
66
 
 
67
        return s
 
68
        
 
69
################
 
70
# configure default handler to stderr
 
71
 
 
72
_stderr_handler = logging.StreamHandler()
 
73
_stderr_handler.setFormatter(QuietFormatter())
 
74
 
 
75
if os.environ.get('BZR_DEBUG'):
 
76
    _stderr_handler.setLevel(logging.DEBUG)
 
77
else:
 
78
    _stderr_handler.setLevel(logging.INFO)
 
79
 
 
80
logging.getLogger('').addHandler(_stderr_handler)
 
81
 
 
82
 
 
83
 
 
84
################
 
85
# configure convenient aliases for output routines
 
86
 
 
87
_bzr_logger = logging.getLogger('bzr')
 
88
_bzr_logger.setLevel(logging.DEBUG) 
 
89
 
 
90
note =      _bzr_logger.info
 
91
warning =   _bzr_logger.warning
 
92
log_error = _bzr_logger.error
 
93
error =     _bzr_logger.error
 
94
mutter =    _bzr_logger.debug
 
95
debug =     _bzr_logger.debug
 
96
 
 
97
 
 
98
 
 
99
 
 
100
# we do the rollover using this code, rather than the default from python
 
101
# logging, because we only want to rollover at program startup, not on each
 
102
# message.  maybe that's not a good enough reason.
78
103
 
79
104
def _rollover_trace_maybe(trace_fname):
80
105
    import stat
104
129
    # Messages are always written to here, so that we have some
105
130
    # information if something goes wrong.  In a future version this
106
131
    # file will be removed on successful completion.
107
 
    global _starttime, _tracefile
 
132
    global _file_handler
108
133
    import stat, codecs
109
134
 
110
 
    _starttime = os.times()[4]
111
 
 
112
135
    trace_fname = os.path.join(os.path.expanduser(tracefilename))
113
136
    _rollover_trace_maybe(trace_fname)
114
137
 
115
138
    # buffering=1 means line buffered
116
139
    try:
117
 
        _tracefile = codecs.open(trace_fname, 'at', 'utf8', buffering=1)
118
 
        t = _tracefile
 
140
        tf = codecs.open(trace_fname, 'at', 'utf8', buffering=1)
119
141
 
120
 
        if os.fstat(t.fileno())[stat.ST_SIZE] == 0:
 
142
        if os.fstat(tf.fileno())[stat.ST_SIZE] == 0:
121
143
            t.write("\nthis is a debug log for diagnosing/reporting problems in bzr\n")
122
144
            t.write("you can delete or truncate this file, or include sections in\n")
123
145
            t.write("bug reports to bazaar-ng@lists.canonical.com\n\n")
 
146
        
 
147
        _file_handler = logging.StreamHandler(tf)
 
148
        fmt = r'[%(process)5d] %(asctime)s.%(msecs)03d %(levelname)s: %(message)s'
 
149
        datefmt = r'%a %H:%M:%S'
 
150
        _file_handler.setFormatter(logging.Formatter(fmt, datefmt))
 
151
        _file_handler.setLevel(logging.DEBUG)
 
152
        logging.getLogger('').addHandler(_file_handler)
124
153
 
125
154
        import bzrlib
126
 
        _write_trace('bzr %s invoked on python %s (%s)'
127
 
                     % (bzrlib.__version__,
128
 
                        '.'.join(map(str, sys.version_info)),
129
 
                        sys.platform))
 
155
        
 
156
        debug('bzr %s invoked on python %s (%s)'
 
157
              % (bzrlib.__version__,
 
158
                 '.'.join(map(str, sys.version_info)),
 
159
                 sys.platform))
130
160
 
131
 
        _write_trace('  arguments: %r' % argv)
132
 
        _write_trace('  working dir: ' + os.getcwdu())
 
161
        debug('  arguments: %r' % argv)
 
162
        debug('  working dir: ' + os.getcwdu())
133
163
    except IOError, e:
134
164
        warning("failed to open trace file: %s" % (e))
135
165
 
136
 
def close_trace():
137
 
    times = os.times()
138
 
    mutter("finished, %.3fu/%.3fs cpu, %.3fu/%.3fs cum, %.3f elapsed"
139
 
           % (times[:4] + ((times[4] - _starttime),)))
140
 
 
141
 
 
142
 
 
143
 
def log_exception():
144
 
    """Log the last exception into the trace file."""
145
 
    import cgitb
146
 
    s = cgitb.text(sys.exc_info())
147
 
    for l in s.split('\n'):
148
 
        _write_trace(l)
 
166
 
 
167
 
 
168
def log_exception(msg=None):
 
169
    """Log the last exception into the trace file.
 
170
 
 
171
    The exception string representation is used as the error
 
172
    summary, unless msg is given.
 
173
    """
 
174
    if msg == None:
 
175
        ei = sys.exc_info()
 
176
        s = str(ei[1])
 
177
        if s[-1] == '\n':
 
178
            s = s[:-1]
 
179
        msg = s
149
180
        
150
 
    
 
181
    _bzr_logger.exception(msg)