1
# -*- test-case-name: twisted.test.test_log -*-
2
# Copyright (c) 2001-2004 Twisted Matrix Laboratories.
3
# See LICENSE for details.
5
"""Logging and metrics infrastructure.
8
from __future__ import division
17
from twisted.python import util, context, reflect
20
"""Actually, this interface is just a synoym for the dictionary interface,
21
but it serves as a key for the default information in a log.
23
I do not inherit from Interface because the world is a cruel place.
26
context.setDefault(ILogContext,
30
def callWithContext(ctx, func, *args, **kw):
31
newCtx = context.get(ILogContext).copy()
33
return context.call({ILogContext: newCtx}, func, *args, **kw)
35
def callWithLogger(logger, func, *args, **kw):
37
Utility method which wraps a function in a try:/except:, logs a failure if
38
one occurrs, and uses the system's logPrefix.
41
lp = logger.logPrefix()
42
except KeyboardInterrupt:
45
lp = '(buggy logPrefix method)'
48
return callWithContext({"system": lp}, func, *args, **kw)
49
except KeyboardInterrupt:
54
def showwarning(message, category, filename, lineno, file=None):
56
msg(warning=message, category=reflect.qual(category), filename=filename, lineno=lineno,
57
format="%(filename)s:%(lineno)s: %(category)s: %(warning)s")
59
_oldshowwarning(message, category, filename, lineno, file)
65
def startKeepingErrors():
67
DEPRECATED in Twisted 2.5.
69
Support function for testing frameworks.
71
Start keeping errors in a buffer which can be retrieved (and emptied) with
74
warnings.warn("log.startKeepingErrors is deprecated since Twisted 2.5",
75
category=DeprecationWarning, stacklevel=2)
80
def flushErrors(*errorTypes):
82
DEPRECATED in Twisted 2.5. See L{TestCase.flushLoggedErrors}.
84
Support function for testing frameworks.
86
Return a list of errors that occurred since the last call to flushErrors().
87
(This will return None unless startKeepingErrors has been called.)
90
warnings.warn("log.flushErrors is deprecated since Twisted 2.5. "
91
"If you need to flush errors from within a unittest, "
92
"use TestCase.flushLoggedErrors instead.",
93
category=DeprecationWarning, stacklevel=2)
94
return _flushErrors(*errorTypes)
97
def _flushErrors(*errorTypes):
99
PRIVATE. DEPRECATED. DON'T USE.
107
for errT in errorTypes:
114
def ignoreErrors(*types):
116
warnings.warn("log.ignoreErrors is deprecated since Twisted 2.5",
117
category=DeprecationWarning, stacklevel=2)
122
PRIVATE. DEPRECATED. DON'T USE.
125
_ignoreErrors.append(type)
129
warnings.warn("log.clearIgnores is deprecated since Twisted 2.5",
130
category=DeprecationWarning, stacklevel=2)
135
PRIVATE. DEPRECATED. DON'T USE.
141
def err(_stuff=None, _why=None, **kw):
143
Write a failure to the log.
146
_stuff = failure.Failure()
147
if isinstance(_stuff, failure.Failure):
151
for err in _ignoreErrors:
152
r = _stuff.check(err)
159
_keptErrors.append(_stuff)
161
_keptErrors.append(_stuff)
162
msg(failure=_stuff, why=_why, isError=1, **kw)
163
elif isinstance(_stuff, Exception):
164
msg(failure=failure.Failure(_stuff), why=_why, isError=1, **kw)
166
msg(repr(_stuff), why=_why, isError=1, **kw)
172
This represents a class which may 'own' a log. Used by subclassing.
176
Override this method to insert custom logging behavior. Its
177
return value will be inserted in front of every line. It may
178
be called more times than the number of output lines.
183
"""Class for singleton log message publishing."""
185
synchronized = ['msg']
190
def addObserver(self, other):
191
"""Add a new observer.
193
Observers are callable objects that will be called with each new log
196
assert callable(other)
197
self.observers.append(other)
199
def removeObserver(self, other):
200
"""Remove an observer."""
201
self.observers.remove(other)
203
def msg(self, *message, **kw):
204
"""Log a new message.
208
| log.msg('Hello, world.')
210
In particular, you MUST avoid the forms::
212
| log.msg(u'Hello, world.')
213
| log.msg('Hello ', 'world.')
215
These forms work (sometimes) by accident and will be disabled
216
entirely in the future.
218
actualEventDict = (context.get(ILogContext) or {}).copy()
219
actualEventDict.update(kw)
220
actualEventDict['message'] = message
221
actualEventDict['time'] = time.time()
222
for i in xrange(len(self.observers) - 1, -1, -1):
224
self.observers[i](actualEventDict)
225
except KeyboardInterrupt:
226
# Don't swallow keyboard interrupt!
228
except UnicodeEncodeError:
231
o = self.observers.pop(i)
232
err(failure.Failure(),
233
"Log observer %s failed, removing from observer list." % (o,))
239
theLogPublisher = LogPublisher()
240
addObserver = theLogPublisher.addObserver
241
removeObserver = theLogPublisher.removeObserver
242
msg = theLogPublisher.msg
245
class FileLogObserver:
247
Log observer that writes to a file-like object.
249
@type timeFormat: C{str} or C{NoneType}
250
@ivar timeFormat: If not C{None}, the format string passed to strftime().
254
def __init__(self, f):
258
def _safeFormat(self, fmtString, crap):
259
#There's a way we could make this if not safer at least more
260
#informative: perhaps some sort of str/repr wrapper objects
261
#could be wrapped around the things inside of 'crap'. That way
262
#if the event dict contains an object with a bad __repr__, we
263
#can only cry about that individual object instead of the
266
text = fmtString % crap
267
except KeyboardInterrupt:
271
text = ('Invalid format string or unformattable object in log message: %r, %s' % (fmtString, crap))
274
text = 'UNFORMATTABLE OBJECT WRITTEN TO LOG with fmt %r, MESSAGE LOST' % (fmtString,)
276
text = 'PATHOLOGICAL ERROR IN BOTH FORMAT STRING AND MESSAGE DETAILS, MESSAGE LOST'
280
def getTimezoneOffset(self):
282
Return the current local timezone offset from UTC.
285
@return: The number of seconds offset from UTC. West is positive,
293
def formatTime(self, when):
295
Return the given UTC value formatted as a human-readable string
296
representing that time in the local timezone.
299
@param when: POSIX timestamp to convert to a human-readable string.
303
if self.timeFormat is not None:
304
return time.strftime(self.timeFormat, time.localtime(when))
306
tzOffset = -self.getTimezoneOffset()
307
when = datetime.datetime.utcfromtimestamp(when + tzOffset)
308
tzHour = int(tzOffset / 60 / 60)
309
tzMin = int(tzOffset / 60 % 60)
310
return '%d/%02d/%02d %02d:%02d %+03d%02d' % (
311
when.year, when.month, when.day,
312
when.hour, when.minute,
316
def emit(self, eventDict):
317
edm = eventDict['message']
319
if eventDict['isError'] and eventDict.has_key('failure'):
320
text = ((eventDict.get('why') or 'Unhandled Error')
321
+ '\n' + eventDict['failure'].getTraceback())
322
elif eventDict.has_key('format'):
323
text = self._safeFormat(eventDict['format'], eventDict)
325
# we don't know how to log this
328
text = ' '.join(map(reflect.safe_str, edm))
330
timeStr = self.formatTime(eventDict['time'])
331
fmtDict = {'system': eventDict['system'], 'text': text.replace("\n", "\n\t")}
332
msgStr = self._safeFormat("[%(system)s] %(text)s\n", fmtDict)
334
util.untilConcludes(self.write, timeStr + " " + msgStr)
335
util.untilConcludes(self.flush) # Hoorj!
338
"""Start observing log events."""
339
addObserver(self.emit)
342
"""Stop observing log events."""
343
removeObserver(self.emit)
346
class StdioOnnaStick:
347
"""Class that pretends to be stout/err."""
352
name = '<stdio (log)>'
354
def __init__(self, isError=0):
355
self.isError = isError
368
raise IOError("can't read from the log!")
375
def write(self, data):
376
d = (self.buf + data).split('\n')
379
for message in messages:
380
msg(message, printed=1, isError=self.isError)
382
def writelines(self, lines):
384
msg(line, printed=1, isError=self.isError)
390
_oldshowwarning = None
393
def startLogging(file, *a, **kw):
394
"""Initialize logging to a specified file.
396
flo = FileLogObserver(file)
397
startLoggingWithObserver(flo.emit, *a, **kw)
399
def startLoggingWithObserver(observer, setStdout=1):
400
"""Initialize logging to a specified observer. If setStdout is true
401
(defaults to yes), also redirect sys.stdout and sys.stderr
402
to the specified file.
404
global defaultObserver, _oldshowwarning
405
if not _oldshowwarning:
406
_oldshowwarning = warnings.showwarning
407
warnings.showwarning = showwarning
409
defaultObserver.stop()
410
defaultObserver = None
411
addObserver(observer)
421
def write(self, bytes): pass
422
def flush(self): pass
423
def close(self): pass
427
"""Throw away all logs.
433
# Prevent logfile from being erased on reload. This only works in cpython.
437
logfile = StdioOnnaStick(0)
438
logerr = StdioOnnaStick(1)
441
class DefaultObserver:
444
Will ignore all non-error messages and send error messages to sys.stderr.
445
Will be removed when startLogging() is called for the first time.
448
def _emit(self, eventDict):
449
if eventDict["isError"]:
450
if eventDict.has_key('failure'):
451
text = eventDict['failure'].getTraceback()
453
text = " ".join([str(m) for m in eventDict["message"]]) + "\n"
454
sys.stderr.write(text)
458
addObserver(self._emit)
461
removeObserver(self._emit)
464
# Some more sibling imports, at the bottom and unqualified to avoid
465
# unresolvable circularity
466
import threadable, failure
467
threadable.synchronize(LogPublisher)
473
defaultObserver = DefaultObserver()
474
defaultObserver.start()