~bzr-pqm/bzr/bzr.dev

1 by mbp at sourcefrog
import from baz patch-364
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
15
344 by Martin Pool
- It's not an error to use the library without
16
"""Messages and logging for bazaar-ng
17
1097 by Martin Pool
- send trace messages out through python logging module
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.
344 by Martin Pool
- It's not an error to use the library without
27
"""
28
29
1097 by Martin Pool
- send trace messages out through python logging module
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
1101 by Martin Pool
- add global definition to quieten pychecker
43
_file_handler = None
44
45
1097 by Martin Pool
- send trace messages out through python logging module
46
class QuietFormatter(logging.Formatter):
47
    """Formatter that supresses the details of errors.
48
49
    This is used by default on stderr so as not to scare the user.
50
    """
51
    # At first I tried overriding formatException to suppress the
52
    # exception details, but that has global effect: no loggers
53
    # can get the exception details is we suppress them here.
54
55
    def format(self, record):
56
        s = 'bzr: '
57
        if record.levelno >= logging.WARNING:
58
            s += record.levelname + ': '
59
            
60
        s += record.getMessage() 
61
            
62
        if record.exc_info:
63
            # give just a summary of the exception, not the whole thing
64
            exinfo = traceback.extract_tb(record.exc_info[2])
65
            # the format of this string matches one of the REs
66
            s += '\n'
67
            s += ('  at %s line %d, in %s()\n' % exinfo[-1][:3])
68
            s += '  see ~/.bzr.log for debug information'
69
70
        return s
71
        
72
################
73
# configure default handler to stderr
74
75
_stderr_handler = logging.StreamHandler()
76
_stderr_handler.setFormatter(QuietFormatter())
77
78
if os.environ.get('BZR_DEBUG'):
79
    _stderr_handler.setLevel(logging.DEBUG)
80
else:
81
    _stderr_handler.setLevel(logging.INFO)
82
83
logging.getLogger('').addHandler(_stderr_handler)
84
85
86
87
################
88
# configure convenient aliases for output routines
89
90
_bzr_logger = logging.getLogger('bzr')
91
_bzr_logger.setLevel(logging.DEBUG) 
92
93
note =      _bzr_logger.info
94
warning =   _bzr_logger.warning
95
log_error = _bzr_logger.error
96
error =     _bzr_logger.error
97
mutter =    _bzr_logger.debug
98
debug =     _bzr_logger.debug
99
100
101
102
103
# we do the rollover using this code, rather than the default from python
104
# logging, because we only want to rollover at program startup, not on each
105
# message.  maybe that's not a good enough reason.
257 by Martin Pool
- Write less startup junk to .bzr.log
106
261 by Martin Pool
- auto-rollover of .bzr.log
107
def _rollover_trace_maybe(trace_fname):
359 by Martin Pool
- pychecker fixups
108
    import stat
261 by Martin Pool
- auto-rollover of .bzr.log
109
    try:
110
        size = os.stat(trace_fname)[stat.ST_SIZE]
333 by Martin Pool
- allow trace file to grow up to 4MB
111
        if size <= 4 << 20:
261 by Martin Pool
- auto-rollover of .bzr.log
112
            return
113
        old_fname = trace_fname + '.old'
114
115
        try:
116
            # must remove before rename on windows
117
            os.remove(old_fname)
118
        except OSError:
119
            pass
120
121
        try:
122
            # might fail if in use on windows
123
            os.rename(trace_fname, old_fname)
124
        except OSError:
125
            pass
126
    except OSError:
127
        return
128
129
130
397 by Martin Pool
- open_tracefile takes a tracefilename parameter
131
def open_tracefile(argv=[], tracefilename='~/.bzr.log'):
59 by mbp at sourcefrog
lift out tracefile creation code
132
    # Messages are always written to here, so that we have some
133
    # information if something goes wrong.  In a future version this
134
    # file will be removed on successful completion.
1097 by Martin Pool
- send trace messages out through python logging module
135
    global _file_handler
359 by Martin Pool
- pychecker fixups
136
    import stat, codecs
59 by mbp at sourcefrog
lift out tracefile creation code
137
397 by Martin Pool
- open_tracefile takes a tracefilename parameter
138
    trace_fname = os.path.join(os.path.expanduser(tracefilename))
261 by Martin Pool
- auto-rollover of .bzr.log
139
    _rollover_trace_maybe(trace_fname)
257 by Martin Pool
- Write less startup junk to .bzr.log
140
141
    # buffering=1 means line buffered
403 by Martin Pool
- Don't give an error if the trace file can't be opened
142
    try:
1097 by Martin Pool
- send trace messages out through python logging module
143
        tf = codecs.open(trace_fname, 'at', 'utf8', buffering=1)
403 by Martin Pool
- Don't give an error if the trace file can't be opened
144
1097 by Martin Pool
- send trace messages out through python logging module
145
        if os.fstat(tf.fileno())[stat.ST_SIZE] == 0:
1100 by Martin Pool
- fix name error reported by Andrew Bennetts
146
            tf.write("\nthis is a debug log for diagnosing/reporting problems in bzr\n")
147
            tf.write("you can delete or truncate this file, or include sections in\n")
148
            tf.write("bug reports to bazaar-ng@lists.canonical.com\n\n")
1097 by Martin Pool
- send trace messages out through python logging module
149
        
150
        _file_handler = logging.StreamHandler(tf)
151
        fmt = r'[%(process)5d] %(asctime)s.%(msecs)03d %(levelname)s: %(message)s'
152
        datefmt = r'%a %H:%M:%S'
153
        _file_handler.setFormatter(logging.Formatter(fmt, datefmt))
154
        _file_handler.setLevel(logging.DEBUG)
155
        logging.getLogger('').addHandler(_file_handler)
403 by Martin Pool
- Don't give an error if the trace file can't be opened
156
157
        import bzrlib
1097 by Martin Pool
- send trace messages out through python logging module
158
        
159
        debug('bzr %s invoked on python %s (%s)'
160
              % (bzrlib.__version__,
161
                 '.'.join(map(str, sys.version_info)),
162
                 sys.platform))
403 by Martin Pool
- Don't give an error if the trace file can't be opened
163
1097 by Martin Pool
- send trace messages out through python logging module
164
        debug('  arguments: %r' % argv)
165
        debug('  working dir: ' + os.getcwdu())
403 by Martin Pool
- Don't give an error if the trace file can't be opened
166
    except IOError, e:
167
        warning("failed to open trace file: %s" % (e))
260 by Martin Pool
- remove atexit() dependency for writing out execution times
168
1097 by Martin Pool
- send trace messages out through python logging module
169
170
171
def log_exception(msg=None):
172
    """Log the last exception into the trace file.
173
174
    The exception string representation is used as the error
175
    summary, unless msg is given.
176
    """
177
    if msg == None:
178
        ei = sys.exc_info()
179
        s = str(ei[1])
180
        if s[-1] == '\n':
181
            s = s[:-1]
182
        msg = s
269 by Martin Pool
- Put proper prefix at start of exceptions in trace file
183
        
1097 by Martin Pool
- send trace messages out through python logging module
184
    _bzr_logger.exception(msg)