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:
parent
02d18ee652
commit
bdd26c9da7
|
@ -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>
|
||||
|
|
|
@ -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)
|
||||
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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)."],
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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):
|
||||
|
|
Loading…
Reference in New Issue