1
# -*- test-case-name: twisted.test.test_log -*-
2
# Copyright (c) 2001-2009 Twisted Matrix Laboratories.
3
# See LICENSE for details.
6
Logging and metrics infrastructure.
9
from __future__ import division
14
from datetime import datetime
17
from zope.interface import Interface
19
from twisted.python import util, context, reflect
25
Actually, this interface is just a synoym for the dictionary interface,
26
but it serves as a key for the default information in a log.
28
I do not inherit from Interface because the world is a cruel place.
33
class ILogObserver(Interface):
35
An observer which can do something with log events.
37
Given that most log observers are actually bound methods, it's okay to not
38
explicitly declare provision of this interface.
40
def __call__(eventDict):
44
@type eventDict: C{dict} with C{str} keys.
45
@param eventDict: A dictionary with arbitrary keys. However, these
46
keys are often available:
47
- C{message}: A C{tuple} of C{str} containing messages to be
49
- C{system}: A C{str} which indicates the "system" which is
50
generating this event.
51
- C{isError}: A C{bool} indicating whether this event represents
53
- C{failure}: A L{failure.Failure} instance
54
- C{why}: Used as header of the traceback in case of errors.
55
- C{format}: A string format used in place of C{message} to
56
customize the event. The intent is for the observer to format
57
a message by doing something like C{format % eventDict}.
62
context.setDefault(ILogContext,
66
def callWithContext(ctx, func, *args, **kw):
67
newCtx = context.get(ILogContext).copy()
69
return context.call({ILogContext: newCtx}, func, *args, **kw)
71
def callWithLogger(logger, func, *args, **kw):
73
Utility method which wraps a function in a try:/except:, logs a failure if
74
one occurrs, and uses the system's logPrefix.
77
lp = logger.logPrefix()
78
except KeyboardInterrupt:
81
lp = '(buggy logPrefix method)'
84
return callWithContext({"system": lp}, func, *args, **kw)
85
except KeyboardInterrupt:
96
def startKeepingErrors():
98
DEPRECATED in Twisted 2.5.
100
Support function for testing frameworks.
102
Start keeping errors in a buffer which can be retrieved (and emptied) with
105
warnings.warn("log.startKeepingErrors is deprecated since Twisted 2.5",
106
category=DeprecationWarning, stacklevel=2)
111
def flushErrors(*errorTypes):
113
DEPRECATED in Twisted 2.5. See L{TestCase.flushLoggedErrors}.
115
Support function for testing frameworks.
117
Return a list of errors that occurred since the last call to flushErrors().
118
(This will return None unless startKeepingErrors has been called.)
121
warnings.warn("log.flushErrors is deprecated since Twisted 2.5. "
122
"If you need to flush errors from within a unittest, "
123
"use TestCase.flushLoggedErrors instead.",
124
category=DeprecationWarning, stacklevel=2)
125
return _flushErrors(*errorTypes)
128
def _flushErrors(*errorTypes):
130
PRIVATE. DEPRECATED. DON'T USE.
138
for errT in errorTypes:
145
def ignoreErrors(*types):
149
warnings.warn("log.ignoreErrors is deprecated since Twisted 2.5",
150
category=DeprecationWarning, stacklevel=2)
155
PRIVATE. DEPRECATED. DON'T USE.
158
_ignoreErrors.append(type)
164
warnings.warn("log.clearIgnores is deprecated since Twisted 2.5",
165
category=DeprecationWarning, stacklevel=2)
170
PRIVATE. DEPRECATED. DON'T USE.
176
def err(_stuff=None, _why=None, **kw):
178
Write a failure to the log.
180
The C{_stuff} and C{_why} parameters use an underscore prefix to lessen
181
the chance of colliding with a keyword argument the application wishes
182
to pass. It is intended that they be supplied with arguments passed
183
positionally, not by keyword.
185
@param _stuff: The failure to log. If C{_stuff} is C{None} a new
186
L{Failure} will be created from the current exception state. If
187
C{_stuff} is an C{Exception} instance it will be wrapped in a
189
@type _stuff: C{NoneType}, C{Exception}, or L{Failure}.
191
@param _why: The source of this failure. This will be logged along with
192
C{_stuff} and should describe the context in which the failure
197
_stuff = failure.Failure()
198
if isinstance(_stuff, failure.Failure):
202
for err in _ignoreErrors:
203
r = _stuff.check(err)
210
_keptErrors.append(_stuff)
212
_keptErrors.append(_stuff)
213
msg(failure=_stuff, why=_why, isError=1, **kw)
214
elif isinstance(_stuff, Exception):
215
msg(failure=failure.Failure(_stuff), why=_why, isError=1, **kw)
217
msg(repr(_stuff), why=_why, isError=1, **kw)
224
This represents a class which may 'own' a log. Used by subclassing.
228
Override this method to insert custom logging behavior. Its
229
return value will be inserted in front of every line. It may
230
be called more times than the number of output lines.
237
Class for singleton log message publishing.
240
synchronized = ['msg']
245
def addObserver(self, other):
249
@type other: Provider of L{ILogObserver}
250
@param other: A callable object that will be called with each new log
253
assert callable(other)
254
self.observers.append(other)
256
def removeObserver(self, other):
260
self.observers.remove(other)
262
def msg(self, *message, **kw):
268
>>> log.msg('Hello, world.')
270
In particular, you MUST avoid the forms::
272
>>> log.msg(u'Hello, world.')
273
>>> log.msg('Hello ', 'world.')
275
These forms work (sometimes) by accident and will be disabled
276
entirely in the future.
278
actualEventDict = (context.get(ILogContext) or {}).copy()
279
actualEventDict.update(kw)
280
actualEventDict['message'] = message
281
actualEventDict['time'] = time.time()
282
for i in xrange(len(self.observers) - 1, -1, -1):
284
self.observers[i](actualEventDict)
285
except KeyboardInterrupt:
286
# Don't swallow keyboard interrupt!
288
except UnicodeEncodeError:
291
observer = self.observers[i]
292
self.observers[i] = lambda event: None
293
err(failure.Failure(),
294
"Log observer %s failed." % (observer,))
295
self.observers[i] = observer
298
def showwarning(self, message, category, filename, lineno, file=None,
301
Twisted-enabled wrapper around L{warnings.showwarning}.
303
If C{file} is C{None}, the default behaviour is to emit the warning to
304
the log system, otherwise the original L{warnings.showwarning} Python
308
self.msg(warning=message, category=reflect.qual(category),
309
filename=filename, lineno=lineno,
310
format="%(filename)s:%(lineno)s: %(category)s: %(warning)s")
312
if sys.version_info < (2, 6):
313
_oldshowwarning(message, category, filename, lineno, file)
315
_oldshowwarning(message, category, filename, lineno, file, line)
323
theLogPublisher = LogPublisher()
324
addObserver = theLogPublisher.addObserver
325
removeObserver = theLogPublisher.removeObserver
326
msg = theLogPublisher.msg
327
showwarning = theLogPublisher.showwarning
330
def _safeFormat(fmtString, fmtDict):
332
Try to format the string C{fmtString} using C{fmtDict} arguments,
333
swallowing all errors to always return a string.
335
# There's a way we could make this if not safer at least more
336
# informative: perhaps some sort of str/repr wrapper objects
337
# could be wrapped around the things inside of C{fmtDict}. That way
338
# if the event dict contains an object with a bad __repr__, we
339
# can only cry about that individual object instead of the
342
text = fmtString % fmtDict
343
except KeyboardInterrupt:
347
text = ('Invalid format string or unformattable object in log message: %r, %s' % (fmtString, fmtDict))
350
text = 'UNFORMATTABLE OBJECT WRITTEN TO LOG with fmt %r, MESSAGE LOST' % (fmtString,)
352
text = 'PATHOLOGICAL ERROR IN BOTH FORMAT STRING AND MESSAGE DETAILS, MESSAGE LOST'
356
def textFromEventDict(eventDict):
358
Extract text from an event dict passed to a log observer. If it cannot
359
handle the dict, it returns None.
361
The possible keys of eventDict are:
362
- C{message}: by default, it holds the final text. It's required, but can
363
be empty if either C{isError} or C{format} is provided (the first
364
having the priority).
365
- C{isError}: boolean indicating the nature of the event.
366
- C{failure}: L{failure.Failure} instance, required if the event is an
368
- C{why}: if defined, used as header of the traceback in case of errors.
369
- C{format}: string format used in place of C{message} to customize
370
the event. It uses all keys present in C{eventDict} to format
372
Other keys will be used when applying the C{format}, or ignored.
374
edm = eventDict['message']
376
if eventDict['isError'] and 'failure' in eventDict:
377
text = ((eventDict.get('why') or 'Unhandled Error')
378
+ '\n' + eventDict['failure'].getTraceback())
379
elif 'format' in eventDict:
380
text = _safeFormat(eventDict['format'], eventDict)
382
# we don't know how to log this
385
text = ' '.join(map(reflect.safe_str, edm))
389
class FileLogObserver:
391
Log observer that writes to a file-like object.
393
@type timeFormat: C{str} or C{NoneType}
394
@ivar timeFormat: If not C{None}, the format string passed to strftime().
398
def __init__(self, f):
402
def getTimezoneOffset(self, when):
404
Return the current local timezone offset from UTC.
407
@param when: POSIX (ie, UTC) timestamp for which to find the offset.
410
@return: The number of seconds offset from UTC. West is positive,
413
offset = datetime.utcfromtimestamp(when) - datetime.fromtimestamp(when)
414
return offset.days * (60 * 60 * 24) + offset.seconds
416
def formatTime(self, when):
418
Format the given UTC value as a string representing that time in the
421
By default it's formatted as a ISO8601-like string (ISO8601 date and
422
ISO8601 time separated by a space). It can be customized using the
423
C{timeFormat} attribute, which will be used as input for the underlying
424
C{time.strftime} call.
427
@param when: POSIX (ie, UTC) timestamp for which to find the offset.
431
if self.timeFormat is not None:
432
return time.strftime(self.timeFormat, time.localtime(when))
434
tzOffset = -self.getTimezoneOffset(when)
435
when = datetime.utcfromtimestamp(when + tzOffset)
436
tzHour = abs(int(tzOffset / 60 / 60))
437
tzMin = abs(int(tzOffset / 60 % 60))
442
return '%d-%02d-%02d %02d:%02d:%02d%s%02d%02d' % (
443
when.year, when.month, when.day,
444
when.hour, when.minute, when.second,
445
tzSign, tzHour, tzMin)
447
def emit(self, eventDict):
448
text = textFromEventDict(eventDict)
452
timeStr = self.formatTime(eventDict['time'])
453
fmtDict = {'system': eventDict['system'], 'text': text.replace("\n", "\n\t")}
454
msgStr = _safeFormat("[%(system)s] %(text)s\n", fmtDict)
456
util.untilConcludes(self.write, timeStr + " " + msgStr)
457
util.untilConcludes(self.flush) # Hoorj!
461
Start observing log events.
463
addObserver(self.emit)
467
Stop observing log events.
469
removeObserver(self.emit)
472
class PythonLoggingObserver(object):
474
Output twisted messages to Python standard library L{logging} module.
476
WARNING: specific logging configurations (example: network) can lead to
477
a blocking system. Nothing is done here to prevent that, so be sure to not
478
use this: code within Twisted, such as twisted.web, assumes that logging
482
def __init__(self, loggerName="twisted"):
484
@param loggerName: identifier used for getting logger.
485
@type loggerName: C{str}
487
self.logger = logging.getLogger(loggerName)
489
def emit(self, eventDict):
491
Receive a twisted log entry, format it and bridge it to python.
493
By default the logging level used is info; log.err produces error
494
level, and you can customize the level by using the C{logLevel} key::
496
>>> log.msg('debugging', logLevel=logging.DEBUG)
499
if 'logLevel' in eventDict:
500
level = eventDict['logLevel']
501
elif eventDict['isError']:
502
level = logging.ERROR
505
text = textFromEventDict(eventDict)
508
self.logger.log(level, text)
512
Start observing log events.
514
addObserver(self.emit)
518
Stop observing log events.
520
removeObserver(self.emit)
523
class StdioOnnaStick:
525
Class that pretends to be stout/err.
531
name = '<stdio (log)>'
533
def __init__(self, isError=0):
534
self.isError = isError
547
raise IOError("can't read from the log!")
554
def write(self, data):
555
d = (self.buf + data).split('\n')
558
for message in messages:
559
msg(message, printed=1, isError=self.isError)
561
def writelines(self, lines):
563
msg(line, printed=1, isError=self.isError)
569
_oldshowwarning = None
572
def startLogging(file, *a, **kw):
574
Initialize logging to a specified file.
576
@return: A L{FileLogObserver} if a new observer is added, None otherwise.
578
if isinstance(file, StdioOnnaStick):
580
flo = FileLogObserver(file)
581
startLoggingWithObserver(flo.emit, *a, **kw)
586
def startLoggingWithObserver(observer, setStdout=1):
588
Initialize logging to a specified observer. If setStdout is true
589
(defaults to yes), also redirect sys.stdout and sys.stderr
590
to the specified file.
592
global defaultObserver, _oldshowwarning
593
if not _oldshowwarning:
594
_oldshowwarning = warnings.showwarning
595
warnings.showwarning = showwarning
597
defaultObserver.stop()
598
defaultObserver = None
599
addObserver(observer)
609
def write(self, bytes): pass
610
def flush(self): pass
611
def close(self): pass
622
# Prevent logfile from being erased on reload. This only works in cpython.
626
logfile = StdioOnnaStick(0)
627
logerr = StdioOnnaStick(1)
630
class DefaultObserver:
634
Will ignore all non-error messages and send error messages to sys.stderr.
635
Will be removed when startLogging() is called for the first time.
638
def _emit(self, eventDict):
639
if eventDict["isError"]:
640
if 'failure' in eventDict:
641
text = eventDict['failure'].getTraceback()
643
text = " ".join([str(m) for m in eventDict["message"]]) + "\n"
644
sys.stderr.write(text)
648
addObserver(self._emit)
651
removeObserver(self._emit)
654
# Some more sibling imports, at the bottom and unqualified to avoid
655
# unresolvable circularity
656
import threadable, failure
657
threadable.synchronize(LogPublisher)
663
defaultObserver = DefaultObserver()
664
defaultObserver.start()