1
# Copyright 2010-2011 Canonical Ltd. This software is licensed under the
2
# GNU Affero General Public License version 3 (see the file LICENSE).
17
from StringIO import StringIO
21
from testtools.content import (
26
from lp.services.log import loglevels
29
LEVEL_PREFIXES = dict(
30
(debug_level, "DEBUG%d" % (1 + debug_level - loglevels.DEBUG))
31
for debug_level in xrange(loglevels.DEBUG9, loglevels.DEBUG))
33
LEVEL_PREFIXES.update({
34
loglevels.DEBUG: 'DEBUG',
35
loglevels.INFO: 'INFO',
36
loglevels.WARNING: 'WARNING',
37
loglevels.ERROR: 'ERROR',
38
loglevels.CRITICAL: 'CRITICAL',
42
class LaunchpadLogger(logging.Logger):
43
"""Logger that support our custom levels."""
45
def debug1(self, msg, *args, **kwargs):
46
if self.isEnabledFor(loglevels.DEBUG1):
47
self._log(loglevels.DEBUG1, msg, args, **kwargs)
49
def debug2(self, msg, *args, **kwargs):
50
if self.isEnabledFor(loglevels.DEBUG2):
51
self._log(loglevels.DEBUG2, msg, args, **kwargs)
53
def debug3(self, msg, *args, **kwargs):
54
if self.isEnabledFor(loglevels.DEBUG3):
55
self._log(loglevels.DEBUG3, msg, args, **kwargs)
57
def debug4(self, msg, *args, **kwargs):
58
if self.isEnabledFor(loglevels.DEBUG4):
59
self._log(loglevels.DEBUG4, msg, args, **kwargs)
61
def debug5(self, msg, *args, **kwargs):
62
if self.isEnabledFor(loglevels.DEBUG5):
63
self._log(loglevels.DEBUG5, msg, args, **kwargs)
65
def debug6(self, msg, *args, **kwargs):
66
if self.isEnabledFor(loglevels.DEBUG6):
67
self._log(loglevels.DEBUG6, msg, args, **kwargs)
69
def debug7(self, msg, *args, **kwargs):
70
if self.isEnabledFor(loglevels.DEBUG7):
71
self._log(loglevels.DEBUG7, msg, args, **kwargs)
73
def debug8(self, msg, *args, **kwargs):
74
if self.isEnabledFor(loglevels.DEBUG8):
75
self._log(loglevels.DEBUG8, msg, args, **kwargs)
77
def debug9(self, msg, *args, **kwargs):
78
if self.isEnabledFor(loglevels.DEBUG9):
79
self._log(loglevels.DEBUG9, msg, args, **kwargs)
83
"""A logging Filter that inserts a prefix into messages.
85
If no static prefix is provided, the Logger's name is used.
88
def __init__(self, prefix=None):
91
def filter(self, record):
92
prefix = self.prefix or record.name
93
record.msg = '[%s] %s' % (prefix, record.msg)
97
class NullHandler(logging.Handler):
98
"""A do-nothing Handler used to silence 'No handlers for logger' warnings.
101
def emit(self, record):
106
"""Emulates a proper logger, just printing everything out the given file.
108
# XXX: GavinPanella 2011-11-04 bug=886053: This is a test fixture not a
111
loglevel = loglevels.DEBUG
113
def __init__(self, output_file=None):
114
"""The default output_file is sys.stdout."""
115
self.output_file = output_file
117
def setLevel(self, loglevel):
118
self.loglevel = loglevel
120
def getEffectiveLevel(self):
123
def _format_message(self, msg, *args):
124
if not isinstance(msg, basestring):
126
# To avoid type errors when the msg has % values and args is empty,
127
# don't expand the string with empty args.
132
def message(self, level, msg, *stuff, **kw):
133
if level < self.loglevel:
136
# We handle the default output file here because sys.stdout
137
# might have been reassigned. Between now and when this object
139
if self.output_file is None:
140
output_file = sys.stdout
142
output_file = self.output_file
143
prefix = LEVEL_PREFIXES.get(level, "%d>" % level)
144
print >> output_file, prefix, self._format_message(msg, *stuff)
147
traceback.print_exc(file=output_file)
149
def log(self, level, *stuff, **kw):
150
self.message(level, *stuff, **kw)
152
def warning(self, *stuff, **kw):
153
self.message(loglevels.WARNING, *stuff, **kw)
157
def error(self, *stuff, **kw):
158
self.message(loglevels.ERROR, *stuff, **kw)
162
def critical(self, *stuff, **kw):
163
self.message(loglevels.CRITICAL, *stuff, **kw)
167
def info(self, *stuff, **kw):
168
self.message(loglevels.INFO, *stuff, **kw)
170
def debug(self, *stuff, **kw):
171
self.message(loglevels.DEBUG, *stuff, **kw)
173
def debug2(self, *stuff, **kw):
174
self.message(loglevels.DEBUG2, *stuff, **kw)
176
def debug3(self, *stuff, **kw):
177
self.message(loglevels.DEBUG3, *stuff, **kw)
179
def debug4(self, *stuff, **kw):
180
self.message(loglevels.DEBUG4, *stuff, **kw)
182
def debug5(self, *stuff, **kw):
183
self.message(loglevels.DEBUG5, *stuff, **kw)
185
def debug6(self, *stuff, **kw):
186
self.message(loglevels.DEBUG6, *stuff, **kw)
188
def debug7(self, *stuff, **kw):
189
self.message(loglevels.DEBUG7, *stuff, **kw)
191
def debug8(self, *stuff, **kw):
192
self.message(loglevels.DEBUG8, *stuff, **kw)
194
def debug9(self, *stuff, **kw):
195
self.message(loglevels.DEBUG9, *stuff, **kw)
198
class DevNullLogger(FakeLogger):
199
"""A logger that drops all messages."""
200
# XXX: GavinPanella 2011-11-04 bug=886053: This is a test fixture not a
203
def message(self, *args, **kwargs):
204
"""Do absolutely nothing."""
207
class BufferLogger(FakeLogger):
208
"""A logger that logs to a StringIO object."""
209
# XXX: GavinPanella 2011-11-04 bug=886053: This is a test fixture not a
213
super(BufferLogger, self).__init__(StringIO())
215
def getLogBuffer(self):
216
"""Return the existing log messages."""
217
return self.output_file.getvalue()
219
def clearLogBuffer(self):
220
"""Clear out the existing log messages."""
221
self.output_file = StringIO()
223
def getLogBufferAndClear(self):
224
"""Return the existing log messages and clear the buffer."""
225
messages = self.getLogBuffer()
226
self.clearLogBuffer()
231
"""Return a `testtools.content.Content` for this object's buffer.
233
Use with `testtools.TestCase.addDetail`, `fixtures.Fixture.addDetail`,
234
and anything else that understands details.
236
get_bytes = lambda: [self.getLogBuffer().encode("utf-8")]
237
return Content(UTF8_TEXT, get_bytes)