Merge configure-log-observer-in-tacs-3534

Author: radix
Reviewer: therve
Fixes: #3534

Now it's possible to specify the log observer used by twistd in a .tac
file by setting the ILogObserver component on the application.


git-svn-id: svn://svn.twistedmatrix.com/svn/Twisted/trunk@25379 bbbe8e31-12d6-0310-92fd-ac37d47ddeeb
This commit is contained in:
radix 2008-11-16 17:26:17 +00:00
parent 02d18ee652
commit bdd26c9da7
7 changed files with 163 additions and 15 deletions

View File

@ -59,7 +59,6 @@
<li><a href="cred.py">cred.py</a></li>
</ul>
<h2>GUI</h2>
<ul>
<li><a href="wxdemo.py">wxdemo.py</a> - wxPython</li>
@ -75,12 +74,18 @@
files for authenticated users from <code class="shell">/home</code>.</li>
</ul>
<h2>Logging</h2>
<ul>
<li><a href="twistd-logging.tac">twistd-logging.tac</a></li>
<li><a href="testlogging.py">testlogging.py</a></li>
<li><a href="rotatinglog.py">rotatinglog.py</a></li>
</ul>
<h2>Miscellaenous</h2>
<ul>
<li><a href="stdiodemo.py">stdiodemo.py</a></li>
<li><a href="mouse.py">mouse.py</a></li>
<li><a href="ptyserv.py">ptyserv.py</a></li>
<li><a href="rotatinglog.py">rotatinglog.py</a></li>
<li><a href="courier.py">courier.py</a></li>
<li><a href="example.html">example.html</a></li>
<li><a href="longex.py">longex.py</a></li>
@ -88,7 +93,6 @@
<li><a href="stdin.py">stdin.py</a></li>
<li><a href="filewatch.py">filewatch.py</a></li>
<li><a href="gpsfix.py">gpsfix.py</a></li>
<li><a href="testlogging.py">testlogging.py</a></li>
</ul>
</body>

View File

@ -0,0 +1,33 @@
# Invoke this script with:
# $ twistd -ny twistd-logging.tac
# It will create a log file named "twistd-logging.log". The log file will
# be formatted such that each line contains the representation of the dict
# structure of each log message.
from twisted.application.service import Application
from twisted.python.log import ILogObserver, msg
from twisted.python.util import untilConcludes
from twisted.internet.task import LoopingCall
logfile = open("twistd-logging.log", "a")
def log(eventDict):
# untilConcludes is necessary to retry the operation when the system call
# has been interrupted.
untilConcludes(logfile.write, "Got a log! %r\n" % eventDict)
untilConcludes(logfile.flush)
def logSomething():
msg("A log message")
LoopingCall(logSomething).start(1)
application = Application("twistd-logging")
application.setComponent(ILogObserver, log)

View File

@ -114,6 +114,37 @@ service.tac</code></p>
<p>For more information, see the <code>twistd</code> man page.</p>
<h3>Customizing <code>twistd</code> logging in a .tac application</h3>
<p>
The logging behavior can be customized through an API
accessible from <code>.tac</code> files. The <code class="API"
base="twisted.python.log.ILogObserver">ILogObserver</code> component can be
set on an Application in order to customize the default log observer that
<code>twistd</code> will use.
</p>
<p>
Here is an example of how to use <code class="API"
base="twisted.python.logfile">DailyLogFile</code>, which rotates the log once
per day.
</p>
<pre class="python">
from twisted.application.service import Application
from twisted.python.log import ILogObserver, FileLogObserver
from twisted.python.logfile import DailyLogFile
application = Application("myapp")
logfile = DailyLogFile("my.log", "/tmp")
application.setComponent(ILogObserver, FileLogObserver(logfile).emit)
</pre>
<p>
invoking <code class="shell">twistd -y my.tac</code> will create a log file
at<code>/tmp/my.log</code>.
</p>
<h3>Services provided by Twisted</h3>
<p>Twisted provides several services that you want to know about.</p>

View File

@ -166,6 +166,14 @@ twisted.python.log.addObserver(yourCallable)
in your program.</li>
</ul>
<h2>Customizing <code>twistd</code> logging</h2>
<p>
The behavior of the logging that <code>twistd</code> does can be customized
by setting the <code>ILogObserver</code> component on the application
object. See the <a href="application.xhtml">Application document</a> for
more information.
</p>
</body>
</html>

View File

@ -8,6 +8,7 @@ from twisted.python import runtime, log, usage, failure, util, logfile
from twisted.python.versions import Version
from twisted.python.reflect import qual
from twisted.python.deprecate import deprecated
from twisted.python.log import ILogObserver
from twisted.persisted import sob
from twisted.application import service, reactors
from twisted.internet import defer
@ -220,7 +221,7 @@ class AppLogger(object):
default.
@type _logfilename: C{str}
@param _observer: log observer added at C{start} and removed at C{stop}.
@ivar _observer: log observer added at C{start} and removed at C{stop}.
@type _observer: C{callable}
"""
_observer = None
@ -232,8 +233,19 @@ class AppLogger(object):
def start(self, application):
"""
Initialize the logging system.
If an L{ILogObserver} component has been set on C{application}, then
it will be used as the log observer. Otherwise a log observer will be
created based on the command-line options.
@param application: The application on which to check for an
L{ILogObserver}.
"""
self._observer = self._getLogObserver()
observer = application.getComponent(ILogObserver, None)
if observer is None:
observer = self._getLogObserver()
self._observer = observer
log.startLoggingWithObserver(self._observer)
self._initialLog()
@ -589,7 +601,8 @@ class ServerOptions(usage.Options, ReactorSelectionMixin):
['file','f','twistd.tap',
"read the given .tap file"],
['python','y', None,
"read an application from within a Python file (implies -o)"],
"read an application from within a Python file "
"(implies -o)"],
['xml', 'x', None,
"Read an application from a .tax file "
"(Marmalade format)."],

View File

@ -8,16 +8,18 @@ Logging and metrics infrastructure.
from __future__ import division
# System Imports
import sys
import time
import warnings
from datetime import datetime
import logging
# Sibling Imports
from zope.interface import Interface
from twisted.python import util, context, reflect
class ILogContext:
"""
Actually, this interface is just a synoym for the dictionary interface,
@ -26,6 +28,37 @@ class ILogContext:
I do not inherit from Interface because the world is a cruel place.
"""
class ILogObserver(Interface):
"""
An observer which can do something with log events.
Given that most log observers are actually bound methods, it's okay to not
explicitly declare provision of this interface.
"""
def __call__(eventDict):
"""
Log an event.
@type eventDict: C{dict} with C{str} keys.
@param eventDict: A dictionary with arbitrary keys. However, these
keys are often available:
- C{message}: A C{tuple} of C{str} containing messages to be
logged.
- C{system}: A C{str} which indicates the "system" which is
generating this event.
- C{isError}: A C{bool} indicating whether this event represents
an error.
- C{failure}: A L{failure.Failure} instance
- C{why}: Used as header of the traceback in case of errors.
- C{format}: A string format used in place of C{message} to
customize the event. The intent is for the observer to format
a message by doing something like C{format % eventDict}.
"""
context.setDefault(ILogContext,
{"isError": 0,
"system": "-"})
@ -221,8 +254,9 @@ class LogPublisher:
"""
Add a new observer.
Observers are callable objects that will be called with each new log
message (a dict).
@type other: Provider of L{ILogObserver}
@param other: A callable object that will be called with each new log
message (a dict).
"""
assert callable(other)
self.observers.append(other)

View File

@ -21,7 +21,9 @@ from twisted.application import service, app
from twisted.scripts import twistd
from twisted.python import log
from twisted.python.usage import UsageError
from twisted.python.log import ILogObserver
from twisted.python.versions import Version
from twisted.python.components import Componentized
from twisted.internet.defer import Deferred
try:
@ -1099,20 +1101,43 @@ class AppLoggerTestCase(unittest.TestCase):
log.removeObserver(observer)
def _checkObserver(self, logs):
"""
Ensure that initial C{twistd} logs are written to the given list.
@type logs: C{list}
@param logs: The list whose C{append} method was specified as the
initial log observer.
"""
self.assertEquals(self.observers, [logs.append])
self.assertIn("starting up", logs[0]["message"][0])
self.assertIn("reactor class", logs[1]["message"][0])
def test_start(self):
"""
L{app.AppLogger.start} call L{log.addObserver}, and that writes some
L{app.AppLogger.start} calls L{log.addObserver}, and then writes some
messages about twistd and the reactor.
"""
logger = app.AppLogger({})
observer = []
logger._getLogObserver = lambda: observer.append
logger.start(Componentized())
self._checkObserver(observer)
logger.start(None)
self.assertEquals(self.observers, [observer.append])
self.assertIn("starting up", observer[0]["message"][0])
self.assertIn("reactor class", observer[1]["message"][0])
def test_startUsesApplicationLogObserver(self):
"""
When the L{ILogObserver} component is available on the application,
that object will be used as the log observer instead of constructing a
new one.
"""
application = Componentized()
logs = []
application.setComponent(ILogObserver, logs.append)
logger = app.AppLogger({})
logger.start(application)
self._checkObserver(logs)
def test_getLogObserverStdout(self):