<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[14847] twext/trunk/twext</title>
</head>
<body>
<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; }
#msg dl a { font-weight: bold}
#msg dl a:link { color:#fc3; }
#msg dl a:active { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.calendarserver.org//changeset/14847">14847</a></dd>
<dt>Author</dt> <dd>wsanchez@apple.com</dd>
<dt>Date</dt> <dd>2015-05-28 14:49:02 -0700 (Thu, 28 May 2015)</dd>
</dl>
<h3>Log Message</h3>
<pre>Get rid of twext.python.log</pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#twexttrunktwextapplicationmasterchildpy">twext/trunk/twext/application/masterchild.py</a></li>
<li><a href="#twexttrunktwextapplicationservicepy">twext/trunk/twext/application/service.py</a></li>
<li><a href="#twexttrunktwextenterprisejobqueuepy">twext/trunk/twext/enterprise/jobqueue.py</a></li>
<li><a href="#twexttrunktwextinternetsendfdportpy">twext/trunk/twext/internet/sendfdport.py</a></li>
<li><a href="#twexttrunktwextinternetsocketfilepy">twext/trunk/twext/internet/socketfile.py</a></li>
<li><a href="#twexttrunktwextinternettcppy">twext/trunk/twext/internet/tcp.py</a></li>
<li><a href="#twexttrunktwextwholdap_servicepy">twext/trunk/twext/who/ldap/_service.py</a></li>
<li><a href="#twexttrunktwextwhoopendirectory_servicepy">twext/trunk/twext/who/opendirectory/_service.py</a></li>
</ul>
<h3>Removed Paths</h3>
<ul>
<li><a href="#twexttrunktwextpythonlogpy">twext/trunk/twext/python/log.py</a></li>
<li><a href="#twexttrunktwextpythontesttest_logpy">twext/trunk/twext/python/test/test_log.py</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="twexttrunktwextapplicationmasterchildpy"></a>
<div class="modfile"><h4>Modified: twext/trunk/twext/application/masterchild.py (14846 => 14847)</h4>
<pre class="diff"><span>
<span class="info">--- twext/trunk/twext/application/masterchild.py        2015-05-28 21:41:18 UTC (rev 14846)
+++ twext/trunk/twext/application/masterchild.py        2015-05-28 21:49:02 UTC (rev 14847)
</span><span class="lines">@@ -44,6 +44,7 @@
</span><span class="cx"> from twisted.python.usage import Options, UsageError
</span><span class="cx"> from twisted.python.reflect import namedClass
</span><span class="cx"> from twisted.python.util import FancyStrMixin
</span><ins>+from twisted.logger import Logger
</ins><span class="cx"> from twisted.application.service import MultiService, Service
</span><span class="cx"> from twisted.application.service import IServiceMaker
</span><span class="cx"> from twisted.application.internet import TCPServer
</span><span class="lines">@@ -52,7 +53,6 @@
</span><span class="cx"> from twisted.internet.protocol import ServerFactory
</span><span class="cx"> from twisted.internet.protocol import ProcessProtocol
</span><span class="cx">
</span><del>-from twext.python.log import Logger
</del><span class="cx"> from twext.internet.sendfdport import InheritingProtocolFactory, IStatus
</span><span class="cx"> from twext.internet.sendfdport import InheritedSocketDispatcher
</span><span class="cx"> from twext.internet.sendfdport import IStatusWatcher
</span></span></pre></div>
<a id="twexttrunktwextapplicationservicepy"></a>
<div class="modfile"><h4>Modified: twext/trunk/twext/application/service.py (14846 => 14847)</h4>
<pre class="diff"><span>
<span class="info">--- twext/trunk/twext/application/service.py        2015-05-28 21:41:18 UTC (rev 14846)
+++ twext/trunk/twext/application/service.py        2015-05-28 21:49:02 UTC (rev 14847)
</span><span class="lines">@@ -26,12 +26,11 @@
</span><span class="cx"> import os
</span><span class="cx"> import signal
</span><span class="cx">
</span><ins>+from twisted.logger import Logger
</ins><span class="cx"> from twisted.application.service import MultiService
</span><span class="cx">
</span><del>-from twext.python.log import Logger
</del><span class="cx">
</span><span class="cx">
</span><del>-
</del><span class="cx"> class ReExecService(MultiService):
</span><span class="cx"> """
</span><span class="cx"> A MultiService which catches SIGHUP and re-exec's the process.
</span></span></pre></div>
<a id="twexttrunktwextenterprisejobqueuepy"></a>
<div class="modfile"><h4>Modified: twext/trunk/twext/enterprise/jobqueue.py (14846 => 14847)</h4>
<pre class="diff"><span>
<span class="info">--- twext/trunk/twext/enterprise/jobqueue.py        2015-05-28 21:41:18 UTC (rev 14846)
+++ twext/trunk/twext/enterprise/jobqueue.py        2015-05-28 21:49:02 UTC (rev 14847)
</span><span class="lines">@@ -130,6 +130,8 @@
</span><span class="cx">
</span><span class="cx"> from zope.interface import implements
</span><span class="cx">
</span><ins>+from twisted.python.reflect import qual
+from twisted.logger import Logger
</ins><span class="cx"> from twisted.application.service import MultiService
</span><span class="cx"> from twisted.internet.protocol import Factory
</span><span class="cx"> from twisted.internet.defer import (
</span><span class="lines">@@ -138,8 +140,6 @@
</span><span class="cx"> from twisted.internet.endpoints import TCP4ClientEndpoint
</span><span class="cx"> from twisted.internet.error import AlreadyCalled, AlreadyCancelled
</span><span class="cx"> from twisted.protocols.amp import AMP, Command, Integer, String, Argument
</span><del>-from twisted.python.reflect import qual
-from twext.python.log import Logger
</del><span class="cx">
</span><span class="cx"> from twext.enterprise.dal.syntax import (
</span><span class="cx"> SchemaSyntax, Lock, NamedValue
</span></span></pre></div>
<a id="twexttrunktwextinternetsendfdportpy"></a>
<div class="modfile"><h4>Modified: twext/trunk/twext/internet/sendfdport.py (14846 => 14847)</h4>
<pre class="diff"><span>
<span class="info">--- twext/trunk/twext/internet/sendfdport.py        2015-05-28 21:41:18 UTC (rev 14846)
+++ twext/trunk/twext/internet/sendfdport.py        2015-05-28 21:49:02 UTC (rev 14847)
</span><span class="lines">@@ -29,10 +29,10 @@
</span><span class="cx"> from zope.interface import Interface
</span><span class="cx">
</span><span class="cx"> from twisted.python.sendmsg import send1msg, recv1msg, getsockfam
</span><ins>+from twisted.logger import Logger
</ins><span class="cx"> from twisted.internet.abstract import FileDescriptor
</span><span class="cx"> from twisted.internet.protocol import Protocol, Factory
</span><span class="cx">
</span><del>-from twext.python.log import Logger
</del><span class="cx"> from twext.python.sendfd import sendfd, recvfd
</span><span class="cx">
</span><span class="cx"> log = Logger()
</span></span></pre></div>
<a id="twexttrunktwextinternetsocketfilepy"></a>
<div class="modfile"><h4>Modified: twext/trunk/twext/internet/socketfile.py (14846 => 14847)</h4>
<pre class="diff"><span>
<span class="info">--- twext/trunk/twext/internet/socketfile.py        2015-05-28 21:41:18 UTC (rev 14846)
+++ twext/trunk/twext/internet/socketfile.py        2015-05-28 21:49:02 UTC (rev 14847)
</span><span class="lines">@@ -23,11 +23,11 @@
</span><span class="cx"> ]
</span><span class="cx">
</span><span class="cx">
</span><ins>+from twisted.logger import Logger
</ins><span class="cx"> from twisted.application import service
</span><span class="cx"> from twisted.internet import endpoints
</span><span class="cx"> from twisted.internet.defer import inlineCallbacks
</span><span class="cx">
</span><del>-from twext.python.log import Logger
</del><span class="cx">
</span><span class="cx"> log = Logger()
</span><span class="cx">
</span></span></pre></div>
<a id="twexttrunktwextinternettcppy"></a>
<div class="modfile"><h4>Modified: twext/trunk/twext/internet/tcp.py (14846 => 14847)</h4>
<pre class="diff"><span>
<span class="info">--- twext/trunk/twext/internet/tcp.py        2015-05-28 21:41:18 UTC (rev 14846)
+++ twext/trunk/twext/internet/tcp.py        2015-05-28 21:49:02 UTC (rev 14847)
</span><span class="lines">@@ -26,11 +26,11 @@
</span><span class="cx"> import socket
</span><span class="cx"> from OpenSSL import SSL
</span><span class="cx">
</span><ins>+from twisted.logger import Logger
</ins><span class="cx"> from twisted.application import internet
</span><span class="cx"> from twisted.internet import tcp, ssl
</span><span class="cx"> from twisted.internet.defer import succeed
</span><span class="cx">
</span><del>-from twext.python.log import Logger
</del><span class="cx">
</span><span class="cx"> log = Logger()
</span><span class="cx">
</span></span></pre></div>
<a id="twexttrunktwextpythonlogpy"></a>
<div class="delfile"><h4>Deleted: twext/trunk/twext/python/log.py (14846 => 14847)</h4>
<pre class="diff"><span>
<span class="info">--- twext/trunk/twext/python/log.py        2015-05-28 21:41:18 UTC (rev 14846)
+++ twext/trunk/twext/python/log.py        2015-05-28 21:49:02 UTC (rev 14847)
</span><span class="lines">@@ -1,1011 +0,0 @@
</span><del>-# -*- test-case-name: twext.python.test.test_log-*-
-##
-# Copyright (c) 2006-2015 Apple Inc. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-##
-
-"""
-Classes and functions to do granular logging.
-
-Example usage in a module C{some.module}::
-
- from twext.python.log import Logger
- log = Logger()
-
- def handleData(data):
- log.debug("Got data: {data!r}.", data=data)
-
-Or in a class::
-
- from twext.python.log import Logger
-
- class Foo(object):
- log = Logger()
-
- def oops(self, data):
- self.log.error("Oops! Invalid data from server: {data!r}",
- data=data)
-
-C{Logger}s have namespaces, for which logging can be configured independently.
-Namespaces may be specified by passing in a C{namespace} argument to L{Logger}
-when instantiating it, but if none is given, the logger will derive its own
-namespace by using the module name of the callable that instantiated it, or, in
-the case of a class, by using the fully qualified name of the class.
-
-In the first example above, the namespace would be C{some.module}, and in the
-second example, it would be C{some.module.Foo}.
-"""
-
-__all__ = [
- "InvalidLogLevelError",
- "LogLevel",
- "formatEvent",
- "Logger",
- "LegacyLogger",
- "ILogObserver",
- "LogPublisher",
- "PredicateResult",
- "ILogFilterPredicate",
- "FilteringLogObserver",
- "LogLevelFilterPredicate",
- "LegacyLogObserver",
- "replaceTwistedLoggers",
-]
-
-
-
-import sys
-from sys import stdout, stderr
-from string import Formatter
-import inspect
-import logging
-import time
-
-from zope.interface import Interface, implementer
-from twisted.python.constants import NamedConstant, Names
-from twisted.python.failure import Failure
-from twisted.python.reflect import safe_str, safe_repr
-import twisted.python.log
-from twisted.python.log import msg as twistedLogMessage
-from twisted.python.log import addObserver, removeObserver
-
-
-
-OBSERVER_REMOVED = (
- "Temporarily removing observer {observer} due to exception: {e}"
-)
-
-
-
-#
-# Log level definitions
-#
-
-class InvalidLogLevelError(Exception):
- """
- Someone tried to use a L{LogLevel} that is unknown to the logging system.
- """
- def __init__(self, level):
- """
- @param level: a L{LogLevel}
- """
- super(InvalidLogLevelError, self).__init__(str(level))
- self.level = level
-
-
-
-class LogLevel(Names):
- """
- Constants denoting log levels:
-
- - C{debug}: Information of use to a developer of the software, not
- generally of interest to someone running the software unless they are
- attempting to diagnose a software issue.
-
- - C{info}: Informational events: Routine information about the status of
- an application, such as incoming connections, startup of a subsystem,
- etc.
-
- - C{warn}: Warnings events: Events that may require greater attention than
- informational events but are not a systemic failure condition, such as
- authorization failures, bad data from a network client, etc.
-
- - C{error}: Error conditions: Events indicating a systemic failure, such
- as unhandled exceptions, loss of connectivity to a back-end database,
- etc.
- """
- debug = NamedConstant()
- info = NamedConstant()
- warn = NamedConstant()
- error = NamedConstant()
- critical = NamedConstant()
-
- @classmethod
- def levelWithName(cls, name):
- """
- @param name: the name of a L{LogLevel}
-
- @return: the L{LogLevel} with the specified C{name}
- """
- try:
- return cls.lookupByName(name)
- except ValueError:
- raise InvalidLogLevelError(name)
-
-
- @classmethod
- def _priorityForLevel(cls, constant):
- """
- We want log levels to have defined ordering - the order of definition -
- but they aren't value constants (the only value is the name). This is
- arguably a bug in Twisted, so this is just a workaround for U{until
- this is fixed in some way
- <https://twistedmatrix.com/trac/ticket/6523>}.
- """
- return cls._levelPriorities[constant]
-
-
-LogLevel._levelPriorities = dict(
- (constant, idx) for (idx, constant) in
- (enumerate(LogLevel.iterconstants()))
-)
-
-
-
-#
-# Mappings to Python's logging module
-#
-pythonLogLevelMapping = {
- LogLevel.debug: logging.DEBUG,
- LogLevel.info: logging.INFO,
- LogLevel.warn: logging.WARNING,
- LogLevel.error: logging.ERROR,
- LogLevel.critical: logging.CRITICAL,
-}
-
-
-
-##
-# Loggers
-##
-
-def formatEvent(event):
- """
- Formats an event as a L{unicode}, using the format in
- C{event["log_format"]}.
-
- This implementation should never raise an exception; if the formatting
- cannot be done, the returned string will describe the event generically so
- that a useful message is emitted regardless.
-
- @param event: a logging event
-
- @return: a L{unicode}
- """
- try:
- format = event.get("log_format", None)
-
- if format is None:
- raise ValueError("No log format provided")
-
- # Make sure format is unicode.
- if isinstance(format, bytes):
- # If we get bytes, assume it's UTF-8 bytes
- format = format.decode("utf-8")
-
- elif isinstance(format, unicode):
- pass
-
- else:
- raise TypeError("Log format must be unicode or bytes, not {0!r}"
- .format(format))
-
- return formatWithCall(format, event)
-
- except BaseException as e:
- return formatUnformattableEvent(event, e)
-
-
-
-def formatUnformattableEvent(event, error):
- """
- Formats an event as a L{unicode} that describes the event generically and a
- formatting error.
-
- @param event: a logging event
- @type dict: L{dict}
-
- @param error: the formatting error
- @type error: L{Exception}
-
- @return: a L{unicode}
- """
- try:
- return (
- u"Unable to format event {event!r}: {error}"
- .format(event=event, error=error)
- )
- except BaseException:
- # Yikes, something really nasty happened.
- #
- # Try to recover as much formattable data as possible; hopefully at
- # least the namespace is sane, which will help you find the offending
- # logger.
- failure = Failure()
-
- text = ", ".join(" = ".join((safe_repr(key), safe_repr(value)))
- for key, value in event.items())
-
- return (
- u"MESSAGE LOST: unformattable object logged: {error}\n"
- u"Recoverable data: {text}\n"
- u"Exception during formatting:\n{failure}"
- .format(error=safe_repr(error), failure=failure, text=text)
- )
-
-
-
-class Logger(object):
- """
- Logging object.
- """
-
- publisher = lambda e: None
-
-
- @staticmethod
- def _namespaceFromCallingContext():
- """
- Derive a namespace from the module containing the caller's caller.
-
- @return: a namespace
- """
- return inspect.currentframe().f_back.f_back.f_globals["__name__"]
-
-
- def __init__(self, namespace=None, source=None):
- """
- @param namespace: The namespace for this logger. Uses a dotted
- notation, as used by python modules. If not C{None}, then the name
- of the module of the caller is used.
-
- @param source: The object which is emitting events to this
- logger; this is automatically set on instances of a class
- if this L{Logger} is an attribute of that class.
- """
- if namespace is None:
- namespace = self._namespaceFromCallingContext()
-
- self.namespace = namespace
- self.source = source
-
-
- def __get__(self, oself, type=None):
- """
- When used as a descriptor, i.e.::
-
- # athing.py
- class Something(object):
- log = Logger()
- def hello(self):
- self.log.info("Hello")
-
- a L{Logger}'s namespace will be set to the name of the class it is
- declared on. In the above example, the namespace would be
- C{athing.Something}.
-
- Additionally, it's source will be set to the actual object referring to
- the L{Logger}. In the above example, C{Something.log.source} would be
- C{Something}, and C{Something().log.source} would be an instance of
- C{Something}.
- """
- if oself is None:
- source = type
- else:
- source = oself
-
- return self.__class__(
- '.'.join([type.__module__, type.__name__]),
- source
- )
-
-
- def __repr__(self):
- return "<%s %r>" % (self.__class__.__name__, self.namespace)
-
-
- def emit(self, level, format=None, **kwargs):
- """
- Emit a log event to all log observers at the given level.
-
- @param level: a L{LogLevel}
-
- @param format: a message format using new-style (PEP 3101)
- formatting. The logging event (which is a L{dict}) is
- used to render this format string.
-
- @param kwargs: additional keyword parameters to include with
- the event.
- """
- # FIXME: Updated Twisted supports 'in' on constants container
- if level not in LogLevel.iterconstants():
- self.failure(
- "Got invalid log level {invalidLevel!r} in {logger}.emit().",
- Failure(InvalidLogLevelError(level)),
- invalidLevel=level,
- logger=self,
- )
- # level = LogLevel.error
- # FIXME: continue to emit?
- return
-
- kwargs.update(
- log_logger=self, log_level=level, log_namespace=self.namespace,
- log_source=self.source, log_format=format, log_time=time.time(),
- )
-
- self.publisher(kwargs)
-
-
- def failure(self, format, failure=None, level=LogLevel.error, **kwargs):
- """
- Log an failure and emit a traceback.
-
- For example::
-
- try:
- frob(knob)
- except Exception:
- log.failure("While frobbing {knob}", knob=knob)
-
- or::
-
- d = deferredFrob(knob)
- d.addErrback(lambda f: log.failure, "While frobbing {knob}",
- f, knob=knob)
-
- @param format: a message format using new-style (PEP 3101)
- formatting. The logging event (which is a L{dict}) is
- used to render this format string.
-
- @param failure: a L{Failure} to log. If C{None}, a L{Failure} is
- created from the exception in flight.
-
- @param level: a L{LogLevel} to use.
-
- @param kwargs: additional keyword parameters to include with the
- event.
- """
- if failure is None:
- failure = Failure()
-
- self.emit(level, format, log_failure=failure, **kwargs)
-
-
-
-class LegacyLogger(object):
- """
- A logging object that provides some compatibility with the
- L{twisted.python.log} module.
- """
-
- def __init__(self, logger=None):
- if logger is None:
- self.newStyleLogger = Logger(Logger._namespaceFromCallingContext())
- else:
- self.newStyleLogger = logger
-
-
- def __getattribute__(self, name):
- try:
- return super(LegacyLogger, self).__getattribute__(name)
- except AttributeError:
- return getattr(twisted.python.log, name)
-
-
- def msg(self, *message, **kwargs):
- """
- This method is API-compatible with L{twisted.python.log.msg} and exists
- for compatibility with that API.
- """
- if message:
- message = " ".join(map(safe_str, message))
- else:
- message = None
- return self.newStyleLogger.emit(LogLevel.info, message, **kwargs)
-
-
- def err(self, _stuff=None, _why=None, **kwargs):
- """
- This method is API-compatible with L{twisted.python.log.err} and exists
- for compatibility with that API.
- """
- if _stuff is None:
- _stuff = Failure()
- elif isinstance(_stuff, Exception):
- _stuff = Failure(_stuff)
-
- if isinstance(_stuff, Failure):
- self.newStyleLogger.emit(LogLevel.error, failure=_stuff, why=_why,
- isError=1, **kwargs)
- else:
- # We got called with an invalid _stuff.
- self.newStyleLogger.emit(LogLevel.error, repr(_stuff), why=_why,
- isError=1, **kwargs)
-
-
-
-def bindEmit(level):
- doc = """
- Emit a log event at log level L{{{level}}}.
-
- @param format: a message format using new-style (PEP 3101)
- formatting. The logging event (which is a L{{dict}}) is used to
- render this format string.
-
- @param kwargs: additional keyword parameters to include with the
- event.
- """.format(level=level.name)
-
- #
- # Attach methods to Logger
- #
- def log_emit(self, format=None, **kwargs):
- self.emit(level, format, **kwargs)
-
- log_emit.__doc__ = doc
-
- setattr(Logger, level.name, log_emit)
-
-
-
-def _bindLevels():
- for level in LogLevel.iterconstants():
- bindEmit(level)
-
-_bindLevels()
-
-
-#
-# Observers
-#
-
-class ILogObserver(Interface):
- """
- An observer which can handle log events.
- """
-
- def __call__(event):
- """
- Log an event.
-
- @type event: C{dict} with (native) C{str} keys.
-
- @param event: A dictionary with arbitrary keys as defined by
- the application emitting logging events, as well as keys
- added by the logging system, with are:
- ...
- """
-
-
-
-@implementer(ILogObserver)
-class LogPublisher(object):
- """
- I{ILogObserver} that fans out events to other observers.
-
- Keeps track of a set of L{ILogObserver} objects and forwards
- events to each.
- """
- log = Logger()
-
- def __init__(self, *observers):
- self._observers = set(observers)
-
-
- @property
- def observers(self):
- return frozenset(self._observers)
-
-
- def addObserver(self, observer):
- """
- Registers an observer with this publisher.
-
- @param observer: An L{ILogObserver} to add.
- """
- self._observers.add(observer)
-
-
- def removeObserver(self, observer):
- """
- Unregisters an observer with this publisher.
-
- @param observer: An L{ILogObserver} to remove.
- """
- try:
- self._observers.remove(observer)
- except KeyError:
- pass
-
-
- def __call__(self, event):
- for observer in self.observers:
- try:
- observer(event)
- except BaseException as e:
- #
- # We have to remove the offending observer because
- # we're going to badmouth it to all of its friends
- # (other observers) and it might get offended and
- # raise again, causing an infinite loop.
- #
- self.removeObserver(observer)
- try:
- self.log.failure(OBSERVER_REMOVED, observer=observer, e=e)
- except BaseException:
- pass
- finally:
- self.addObserver(observer)
-
-
-
-class PredicateResult(Names):
- """
- Predicate results.
- """
- yes = NamedConstant() # Log this
- no = NamedConstant() # Don't log this
- maybe = NamedConstant() # No opinion
-
-
-
-class ILogFilterPredicate(Interface):
- """
- A predicate that determined whether an event should be logged.
- """
-
- def __call__(event):
- """
- Determine whether an event should be logged.
-
- @returns: a L{PredicateResult}.
- """
-
-
-
-@implementer(ILogObserver)
-class FilteringLogObserver(object):
- """
- L{ILogObserver} that wraps another L{ILogObserver}, but filters
- out events based on applying a series of L{ILogFilterPredicate}s.
- """
-
- def __init__(self, observer, predicates):
- """
- @param observer: an L{ILogObserver} to which this observer
- will forward events.
-
- @param predicates: an ordered iterable of predicates to apply
- to events before forwarding to the wrapped observer.
- """
- self.observer = observer
- self.predicates = list(predicates)
-
-
- def shouldLogEvent(self, event):
- """
- Determine whether an event should be logged, based
- C{self.predicates}.
-
- @param event: an event
- """
- for predicate in self.predicates:
- result = predicate(event)
- if result == PredicateResult.yes:
- return True
- if result == PredicateResult.no:
- return False
- if result == PredicateResult.maybe:
- continue
- raise TypeError("Invalid predicate result: {0!r}".format(result))
- return True
-
-
- def __call__(self, event):
- if self.shouldLogEvent(event):
- self.observer(event)
-
-
-
-@implementer(ILogFilterPredicate)
-class LogLevelFilterPredicate(object):
- """
- L{ILogFilterPredicate} that filters out events with a log level
- lower than the log level for the event's namespace.
-
- Events that not not have a log level or namespace are also dropped.
- """
-
- def __init__(self):
- # FIXME: Make this a class variable. But that raises an
- # _initializeEnumerants constants error in Twisted 12.2.0.
- self.defaultLogLevel = LogLevel.info
-
- self._logLevelsByNamespace = {}
- self.clearLogLevels()
-
-
- def logLevelForNamespace(self, namespace):
- """
- @param namespace: a logging namespace, or C{None} for the default
- namespace.
-
- @return: the L{LogLevel} for the specified namespace.
- """
- if not namespace:
- return self._logLevelsByNamespace[None]
-
- if namespace in self._logLevelsByNamespace:
- return self._logLevelsByNamespace[namespace]
-
- segments = namespace.split(".")
- index = len(segments) - 1
-
- while index > 0:
- namespace = ".".join(segments[:index])
- if namespace in self._logLevelsByNamespace:
- return self._logLevelsByNamespace[namespace]
- index -= 1
-
- return self._logLevelsByNamespace[None]
-
-
- def setLogLevelForNamespace(self, namespace, level):
- """
- Sets the global log level for a logging namespace.
-
- @param namespace: a logging namespace
-
- @param level: the L{LogLevel} for the given namespace.
- """
- if level not in LogLevel.iterconstants():
- raise InvalidLogLevelError(level)
-
- if namespace:
- self._logLevelsByNamespace[namespace] = level
- else:
- self._logLevelsByNamespace[None] = level
-
-
- def clearLogLevels(self):
- """
- Clears all global log levels to the default.
- """
- self._logLevelsByNamespace.clear()
- self._logLevelsByNamespace[None] = self.defaultLogLevel
-
-
- def __call__(self, event):
- level = event.get("log_level", None)
- namespace = event.get("log_namespace", None)
-
- if (
- level is None or
- namespace is None or
- LogLevel._priorityForLevel(level) <
- LogLevel._priorityForLevel(self.logLevelForNamespace(namespace))
- ):
- return PredicateResult.no
-
- return PredicateResult.maybe
-
-
-
-@implementer(ILogObserver)
-class LegacyLogObserver(object):
- """
- L{ILogObserver} that wraps an L{ILegacyLogObserver}.
- """
-
- def __init__(self, legacyObserver):
- """
- @param legacyObserver: an L{ILegacyLogObserver} to which this
- observer will forward events.
- """
- self.legacyObserver = legacyObserver
-
-
- def __call__(self, event):
- prefix = "[{log_namespace}#{log_level.name}] ".format(**event)
-
- level = event["log_level"]
-
- #
- # Twisted's logging supports indicating a python log level, so let's
- # provide the equivalent to our logging levels.
- #
- if level in pythonLogLevelMapping:
- event["logLevel"] = pythonLogLevelMapping[level]
-
- # Format new style -> old style
- if event["log_format"]:
- #
- # Create an object that implements __str__() in order to
- # defer the work of formatting until it's needed by a
- # legacy log observer.
- #
- class LegacyFormatStub(object):
- def __str__(oself):
- return formatEvent(event).encode("utf-8")
-
- event["format"] = prefix + "%(log_legacy)s"
- event["log_legacy"] = LegacyFormatStub()
-
- # log.failure() -> isError blah blah
- if "log_failure" in event:
- event["failure"] = event["log_failure"]
- event["isError"] = 1
- event["why"] = "{prefix}{message}".format(
- prefix=prefix, message=formatEvent(event)
- )
-
- self.legacyObserver(**event)
-
-
-
-# FIXME: This could have a better name.
-class DefaultLogPublisher(object):
- """
- This observer sets up a set of chained observers as follows:
-
- 1. B{rootPublisher} - a L{LogPublisher}
-
- 2. B{filters}: a L{FilteringLogObserver} that filters out messages
- using a L{LogLevelFilterPredicate}
-
- 3. B{filteredPublisher} - a L{LogPublisher}
-
- 4. B{legacyLogObserver} - a L{LegacyLogObserver} wired up to
- L{twisted.python.log.msg}. This allows any observers registered
- with Twisted's logging (that is, most observers in presently use) to
- receive (filtered) events.
-
- The purpose of this class is to provide a default log observer with
- sufficient hooks to enable applications to add observers that can either
- receive all log messages, or only log messages that are configured to pass
- though the L{LogLevelFilterPredicate}::
-
- from twext.python.log import Logger, ILogObserver
-
- log = Logger()
-
- @implementer(ILogObserver)
- class AMPObserver(object):
- def __call__(self, event):
- # eg.: Hold events in a ring buffer and expose them via AMP.
- ...
-
- @implementer(ILogObserver)
- class FileObserver(object):
- def __call__(self, event):
- # eg.: Take events and write them into a file.
- ...
-
- # Send all events to the AMPObserver
- log.publisher.addObserver(AMPObserver(), filtered=False)
-
- # Send filtered events to the FileObserver
- log.publisher.addObserver(AMPObserver())
-
- With no observers added, the default behavior is that the legacy Twisted
- logging system sees messages as controlled by L{LogLevelFilterPredicate}.
- """
-
- def __init__(self):
- self.legacyLogObserver = LegacyLogObserver(twistedLogMessage)
- self.filteredPublisher = LogPublisher(self.legacyLogObserver)
- self.levels = LogLevelFilterPredicate()
- self.filters = FilteringLogObserver(
- self.filteredPublisher, (self.levels,)
- )
- self.rootPublisher = LogPublisher(self.filters)
-
-
- def addObserver(self, observer, filtered=True):
- """
- Registers an observer with this publisher.
-
- @param observer: An L{ILogObserver} to add.
-
- @param filtered: If true, registers C{observer} after filters are
- applied; otherwise C{observer} will get all events.
- """
- if filtered:
- self.filteredPublisher.addObserver(observer)
- self.rootPublisher.removeObserver(observer)
- else:
- self.rootPublisher.addObserver(observer)
- self.filteredPublisher.removeObserver(observer)
-
-
- def removeObserver(self, observer):
- """
- Unregisters an observer with this publisher.
-
- @param observer: An L{ILogObserver} to remove.
- """
- self.rootPublisher.removeObserver(observer)
- self.filteredPublisher.removeObserver(observer)
-
-
- def __call__(self, event):
- self.rootPublisher(event)
-
-
-
-Logger.publisher = DefaultLogPublisher()
-
-
-
-#
-# Utilities
-#
-
-class CallMapping(object):
- def __init__(self, submapping):
- self._submapping = submapping
-
-
- def __getitem__(self, key):
- callit = key.endswith(u"()")
- realKey = key[:-2] if callit else key
- value = self._submapping[realKey]
- if callit:
- value = value()
- return value
-
-
-
-def formatWithCall(formatString, mapping):
- """
- Format a string like L{unicode.format}, but:
-
- - taking only a name mapping; no positional arguments
-
- - with the additional syntax that an empty set of parentheses
- correspond to a formatting item that should be called, and its result
- C{str}'d, rather than calling C{str} on the element directly as
- normal.
-
- For example::
-
- >>> formatWithCall("{string}, {function()}.",
- ... dict(string="just a string",
- ... function=lambda: "a function"))
- 'just a string, a function.'
-
- @param formatString: A PEP-3101 format string.
- @type formatString: L{unicode}
-
- @param mapping: A L{dict}-like object to format.
-
- @return: The string with formatted values interpolated.
- @rtype: L{unicode}
- """
- return unicode(
- theFormatter.vformat(formatString, (), CallMapping(mapping))
- )
-
-theFormatter = Formatter()
-
-
-def replaceTwistedLoggers():
- """
- Visit all Python modules that have been loaded and:
-
- - replace L{twisted.python.log} with a L{LegacyLogger}
-
- - replace L{twisted.python.log.msg} with a L{LegacyLogger}'s C{msg}
-
- - replace L{twisted.python.log.err} with a L{LegacyLogger}'s C{err}
- """
- log = Logger()
-
- for moduleName, module in sys.modules.iteritems():
- # Oddly, this happens
- if module is None:
- continue
-
- # Don't patch Twisted's logging module
- if module in (twisted.python, twisted.python.log):
- continue
-
- # Don't patch this module
- if moduleName is __name__:
- continue
-
- try:
- for name, obj in module.__dict__.iteritems():
- try:
- newLogger = Logger(namespace=module.__name__)
- except AttributeError:
- # Can't look up __name__. A hack in the "six" module causes
- # this. Skip the module.
- # See https://trac.calendarserver.org/ticket/832
- continue
-
- legacyLogger = LegacyLogger(logger=newLogger)
-
- if obj is twisted.python.log:
- log.info(
- "Replacing Twisted log module object {0} in {1}"
- .format(name, module.__name__)
- )
- setattr(module, name, legacyLogger)
-
- elif obj is twisted.python.log.msg:
- log.info(
- "Replacing Twisted log.msg object {0} in {1}"
- .format(name, module.__name__)
- )
- setattr(module, name, legacyLogger.msg)
-
- elif obj is twisted.python.log.err:
- log.info(
- "Replacing Twisted log.err object {0} in {1}"
- .format(name, module.__name__)
- )
- setattr(module, name, legacyLogger.err)
- except RuntimeError as e:
- # Python could use more specific exceptions, eh.
- # What we mean to catch is:
- # RuntimeError: dictionary changed size during iteration
- log.error(
- "Unable to replace twisted loggers for module {module}: "
- "{error}",
- module=module, error=e
- )
-
-
-
-# FIXME: This may not be needed; look into removing it.
-
-class StandardIOObserver(object):
- """
- (Legacy) log observer that writes to standard I/O.
- """
- def emit(self, eventDict):
- text = None
-
- if eventDict["isError"]:
- output = stderr
- if "failure" in eventDict:
- text = eventDict["failure"].getTraceback()
- else:
- output = stdout
-
- if not text:
- text = " ".join([str(m) for m in eventDict["message"]]) + "\n"
-
- output.write(text)
- output.flush()
-
-
- def start(self):
- addObserver(self.emit)
-
-
- def stop(self):
- removeObserver(self.emit)
</del></span></pre></div>
<a id="twexttrunktwextpythontesttest_logpy"></a>
<div class="delfile"><h4>Deleted: twext/trunk/twext/python/test/test_log.py (14846 => 14847)</h4>
<pre class="diff"><span>
<span class="info">--- twext/trunk/twext/python/test/test_log.py        2015-05-28 21:41:18 UTC (rev 14846)
+++ twext/trunk/twext/python/test/test_log.py        2015-05-28 21:49:02 UTC (rev 14847)
</span><span class="lines">@@ -1,1029 +0,0 @@
</span><del>-##
-# Copyright (c) 2005-2015 Apple Inc. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-##
-
-from zope.interface.verify import verifyObject, BrokenMethodImplementation
-
-from twisted.python import log as twistedLogging
-from twisted.python.failure import Failure
-from twisted.trial import unittest
-
-from twext.python.log import (
- LogLevel, InvalidLogLevelError,
- pythonLogLevelMapping,
- formatEvent, formatUnformattableEvent, formatWithCall,
- Logger, LegacyLogger,
- ILogObserver, LogPublisher, DefaultLogPublisher,
- FilteringLogObserver, PredicateResult,
- LogLevelFilterPredicate, OBSERVER_REMOVED
-)
-
-
-
-defaultLogLevel = LogLevelFilterPredicate().defaultLogLevel
-clearLogLevels = Logger.publisher.levels.clearLogLevels
-logLevelForNamespace = Logger.publisher.levels.logLevelForNamespace
-setLogLevelForNamespace = Logger.publisher.levels.setLogLevelForNamespace
-
-
-
-class TestLogger(Logger):
- def emit(self, level, format=None, **kwargs):
- if False:
- print "*" * 60
- print "level =", level
- print "format =", format
- for key, value in kwargs.items():
- print key, "=", value
- print "*" * 60
-
- def observer(event):
- self.event = event
-
- twistedLogging.addObserver(observer)
- try:
- Logger.emit(self, level, format, **kwargs)
- finally:
- twistedLogging.removeObserver(observer)
-
- self.emitted = {
- "level": level,
- "format": format,
- "kwargs": kwargs,
- }
-
-
-
-class TestLegacyLogger(LegacyLogger):
- def __init__(self, logger=TestLogger()):
- LegacyLogger.__init__(self, logger=logger)
-
-
-
-class LogComposedObject(object):
- """
- Just a regular object.
- """
- log = TestLogger()
-
- def __init__(self, state=None):
- self.state = state
-
-
- def __str__(self):
- return "<LogComposedObject {state}>".format(state=self.state)
-
-
-
-class SetUpTearDown(object):
- def setUp(self):
- super(SetUpTearDown, self).setUp()
- clearLogLevels()
-
-
- def tearDown(self):
- super(SetUpTearDown, self).tearDown()
- clearLogLevels()
-
-
-
-class LoggingTests(SetUpTearDown, unittest.TestCase):
- """
- General module tests.
- """
-
- def test_levelWithName(self):
- """
- Look up log level by name.
- """
- for level in LogLevel.iterconstants():
- self.assertIdentical(LogLevel.levelWithName(level.name), level)
-
-
- def test_levelWithInvalidName(self):
- """
- You can't make up log level names.
- """
- bogus = "*bogus*"
- try:
- LogLevel.levelWithName(bogus)
- except InvalidLogLevelError as e:
- self.assertIdentical(e.level, bogus)
- else:
- self.fail("Expected InvalidLogLevelError.")
-
-
- def test_defaultLogLevel(self):
- """
- Default log level is used.
- """
- self.failUnless(logLevelForNamespace(None), defaultLogLevel)
- self.failUnless(logLevelForNamespace(""), defaultLogLevel)
- self.failUnless(logLevelForNamespace("rocker.cool.namespace"),
- defaultLogLevel)
-
-
- def test_setLogLevel(self):
- """
- Setting and retrieving log levels.
- """
- setLogLevelForNamespace(None, LogLevel.error)
- setLogLevelForNamespace("twext.web2", LogLevel.debug)
- setLogLevelForNamespace("twext.web2.dav", LogLevel.warn)
-
- self.assertEquals(logLevelForNamespace(None),
- LogLevel.error)
- self.assertEquals(logLevelForNamespace("twisted"),
- LogLevel.error)
- self.assertEquals(logLevelForNamespace("twext.web2"),
- LogLevel.debug)
- self.assertEquals(logLevelForNamespace("twext.web2.dav"),
- LogLevel.warn)
- self.assertEquals(logLevelForNamespace("twext.web2.dav.test"),
- LogLevel.warn)
- self.assertEquals(logLevelForNamespace("twext.web2.dav.test1.test2"),
- LogLevel.warn)
-
-
- def test_setInvalidLogLevel(self):
- """
- Can't pass invalid log levels to setLogLevelForNamespace().
- """
- self.assertRaises(InvalidLogLevelError, setLogLevelForNamespace,
- "twext.web2", object())
-
- # Level must be a constant, not the name of a constant
- self.assertRaises(InvalidLogLevelError, setLogLevelForNamespace,
- "twext.web2", "debug")
-
-
- def test_clearLogLevels(self):
- """
- Clearing log levels.
- """
- setLogLevelForNamespace("twext.web2", LogLevel.debug)
- setLogLevelForNamespace("twext.web2.dav", LogLevel.error)
-
- clearLogLevels()
-
- self.assertEquals(logLevelForNamespace("twisted"), defaultLogLevel)
- self.assertEquals(logLevelForNamespace("twext.web2"), defaultLogLevel)
- self.assertEquals(logLevelForNamespace("twext.web2.dav"),
- defaultLogLevel)
- self.assertEquals(logLevelForNamespace("twext.web2.dav.test"),
- defaultLogLevel)
- self.assertEquals(logLevelForNamespace("twext.web2.dav.test1.test2"),
- defaultLogLevel)
-
-
- def test_namespace_default(self):
- """
- Default namespace is module name.
- """
- log = Logger()
- self.assertEquals(log.namespace, __name__)
-
-
- def test_formatWithCall(self):
- """
- L{formatWithCall} is an extended version of L{unicode.format} that will
- interpret a set of parentheses "C{()}" at the end of a format key to
- mean that the format key ought to be I{called} rather than stringified.
- """
- self.assertEquals(
- formatWithCall(
- u"Hello, {world}. {callme()}.",
- dict(world="earth", callme=lambda: "maybe")
- ),
- "Hello, earth. maybe."
- )
- self.assertEquals(
- formatWithCall(
- u"Hello, {repr()!r}.",
- dict(repr=lambda: "repr")
- ),
- "Hello, 'repr'."
- )
-
-
- def test_formatEvent(self):
- """
- L{formatEvent} will format an event according to several rules:
-
- - A string with no formatting instructions will be passed straight
- through.
-
- - PEP 3101 strings will be formatted using the keys and values of
- the event as named fields.
-
- - PEP 3101 keys ending with C{()} will be treated as instructions
- to call that key (which ought to be a callable) before
- formatting.
-
- L{formatEvent} will always return L{unicode}, and if given
- bytes, will always treat its format string as UTF-8 encoded.
- """
- def format(log_format, **event):
- event["log_format"] = log_format
- result = formatEvent(event)
- self.assertIdentical(type(result), unicode)
- return result
-
- self.assertEquals(u"", format(b""))
- self.assertEquals(u"", format(u""))
- self.assertEquals(u"abc", format("{x}", x="abc"))
- self.assertEquals(u"no, yes.",
- format("{not_called}, {called()}.",
- not_called="no", called=lambda: "yes"))
- self.assertEquals(u'S\xe1nchez', format("S\xc3\xa1nchez"))
- self.assertIn(u"Unable to format event", format(b"S\xe1nchez"))
- self.assertIn(u"Unable to format event",
- format(b"S{a}nchez", a=b"\xe1"))
- self.assertIn(u"S'\\xe1'nchez",
- format(b"S{a!r}nchez", a=b"\xe1"))
-
-
- def test_formatEventNoFormat(self):
- """
- Formatting an event with no format.
- """
- event = dict(foo=1, bar=2)
- result = formatEvent(event)
-
- self.assertIn("Unable to format event", result)
- self.assertIn(repr(event), result)
-
-
- def test_formatEventWeirdFormat(self):
- """
- Formatting an event with a bogus format.
- """
- event = dict(log_format=object(), foo=1, bar=2)
- result = formatEvent(event)
-
- self.assertIn("Log format must be unicode or bytes", result)
- self.assertIn(repr(event), result)
-
-
- def test_formatUnformattableEvent(self):
- """
- Formatting an event that's just plain out to get us.
- """
- event = dict(log_format="{evil()}", evil=lambda: 1 / 0)
- result = formatEvent(event)
-
- self.assertIn("Unable to format event", result)
- self.assertIn(repr(event), result)
-
-
- def test_formatUnformattableEventWithUnformattableKey(self):
- """
- Formatting an unformattable event that has an unformattable key.
- """
- event = {
- "log_format": "{evil()}",
- "evil": lambda: 1 / 0,
- Unformattable(): "gurk",
- }
- result = formatEvent(event)
- self.assertIn("MESSAGE LOST: unformattable object logged:", result)
- self.assertIn("Recoverable data:", result)
- self.assertIn("Exception during formatting:", result)
-
-
- def test_formatUnformattableEventWithUnformattableValue(self):
- """
- Formatting an unformattable event that has an unformattable value.
- """
- event = dict(
- log_format="{evil()}",
- evil=lambda: 1 / 0,
- gurk=Unformattable(),
- )
- result = formatEvent(event)
- self.assertIn("MESSAGE LOST: unformattable object logged:", result)
- self.assertIn("Recoverable data:", result)
- self.assertIn("Exception during formatting:", result)
-
-
- def test_formatUnformattableEventWithUnformattableErrorOMGWillItStop(self):
- """
- Formatting an unformattable event that has an unformattable value.
- """
- event = dict(
- log_format="{evil()}",
- evil=lambda: 1 / 0,
- recoverable="okay",
- )
- # Call formatUnformattableEvent() directly with a bogus exception.
- result = formatUnformattableEvent(event, Unformattable())
- self.assertIn("MESSAGE LOST: unformattable object logged:", result)
- self.assertIn(repr("recoverable") + " = " + repr("okay"), result)
-
-
-
-class LoggerTests(SetUpTearDown, unittest.TestCase):
- """
- Tests for L{Logger}.
- """
-
- def test_repr(self):
- """
- repr() on Logger
- """
- namespace = "bleargh"
- log = Logger(namespace)
- self.assertEquals(repr(log), "<Logger {0}>".format(repr(namespace)))
-
-
- def test_namespace_attribute(self):
- """
- Default namespace for classes using L{Logger} as a descriptor is the
- class name they were retrieved from.
- """
- obj = LogComposedObject()
- self.assertEquals(obj.log.namespace,
- "twext.python.test.test_log.LogComposedObject")
- self.assertEquals(LogComposedObject.log.namespace,
- "twext.python.test.test_log.LogComposedObject")
- self.assertIdentical(LogComposedObject.log.source, LogComposedObject)
- self.assertIdentical(obj.log.source, obj)
- self.assertIdentical(Logger().source, None)
-
-
- def test_sourceAvailableForFormatting(self):
- """
- On instances that have a L{Logger} class attribute, the C{log_source}
- key is available to format strings.
- """
- obj = LogComposedObject("hello")
- log = obj.log
- log.error("Hello, {log_source}.")
-
- self.assertIn("log_source", log.event)
- self.assertEquals(log.event["log_source"], obj)
-
- stuff = formatEvent(log.event)
- self.assertIn("Hello, <LogComposedObject hello>.", stuff)
-
-
- def test_basic_Logger(self):
- """
- Test that log levels and messages are emitted correctly for
- Logger.
- """
- # FIXME: Need a basic test like this for logger attached to a class.
- # At least: source should not be None in that case.
-
- log = TestLogger()
-
- for level in LogLevel.iterconstants():
- format = "This is a {level_name} message"
- message = format.format(level_name=level.name)
-
- method = getattr(log, level.name)
- method(format, junk=message, level_name=level.name)
-
- # Ensure that test_emit got called with expected arguments
- self.assertEquals(log.emitted["level"], level)
- self.assertEquals(log.emitted["format"], format)
- self.assertEquals(log.emitted["kwargs"]["junk"], message)
-
- if level >= logLevelForNamespace(log.namespace):
- self.assertTrue(hasattr(log, "event"), "No event observed.")
- self.assertEquals(log.event["log_format"], format)
- self.assertEquals(log.event["log_level"], level)
- self.assertEquals(log.event["log_namespace"], __name__)
- self.assertEquals(log.event["log_source"], None)
-
- self.assertEquals(log.event["logLevel"],
- pythonLogLevelMapping[level])
-
- self.assertEquals(log.event["junk"], message)
-
- # FIXME: this checks the end of message because we do
- # formatting in emit()
- self.assertEquals(
- formatEvent(log.event),
- message
- )
- else:
- self.assertFalse(hasattr(log, "event"))
-
-
- def test_defaultFailure(self):
- """
- Test that log.failure() emits the right data.
- """
- log = TestLogger()
- try:
- raise RuntimeError("baloney!")
- except RuntimeError:
- log.failure("Whoops")
-
- #
- # log.failure() will cause trial to complain, so here we check that
- # trial saw the correct error and remove it from the list of things to
- # complain about.
- #
- errors = self.flushLoggedErrors(RuntimeError)
- self.assertEquals(len(errors), 1)
-
- self.assertEquals(log.emitted["level"], LogLevel.error)
- self.assertEquals(log.emitted["format"], "Whoops")
-
-
- def test_conflicting_kwargs(self):
- """
- Make sure that kwargs conflicting with args don't pass through.
- """
- log = TestLogger()
-
- log.warn(
- "*",
- log_format="#",
- log_level=LogLevel.error,
- log_namespace="*namespace*",
- log_source="*source*",
- )
-
- # FIXME: Should conflicts log errors?
-
- self.assertEquals(log.event["log_format"], "*")
- self.assertEquals(log.event["log_level"], LogLevel.warn)
- self.assertEquals(log.event["log_namespace"], log.namespace)
- self.assertEquals(log.event["log_source"], None)
-
-
- def test_logInvalidLogLevel(self):
- """
- Test passing in a bogus log level to C{emit()}.
- """
- log = TestLogger()
-
- log.emit("*bogus*")
-
- errors = self.flushLoggedErrors(InvalidLogLevelError)
- self.assertEquals(len(errors), 1)
-
-
-
-class LogPublisherTests(SetUpTearDown, unittest.TestCase):
- """
- Tests for L{LogPublisher}.
- """
-
- def test_interface(self):
- """
- L{LogPublisher} is an L{ILogObserver}.
- """
- publisher = LogPublisher()
- try:
- verifyObject(ILogObserver, publisher)
- except BrokenMethodImplementation as e:
- self.fail(e)
-
-
- def test_observers(self):
- """
- L{LogPublisher.observers} returns the observers.
- """
- o1 = lambda e: None
- o2 = lambda e: None
-
- publisher = LogPublisher(o1, o2)
- self.assertEquals(set((o1, o2)), set(publisher.observers))
-
-
- def test_addObserver(self):
- """
- L{LogPublisher.addObserver} adds an observer.
- """
- o1 = lambda e: None
- o2 = lambda e: None
- o3 = lambda e: None
-
- publisher = LogPublisher(o1, o2)
- publisher.addObserver(o3)
- self.assertEquals(set((o1, o2, o3)), set(publisher.observers))
-
-
- def test_removeObserver(self):
- """
- L{LogPublisher.removeObserver} removes an observer.
- """
- o1 = lambda e: None
- o2 = lambda e: None
- o3 = lambda e: None
-
- publisher = LogPublisher(o1, o2, o3)
- publisher.removeObserver(o2)
- self.assertEquals(set((o1, o3)), set(publisher.observers))
-
-
- def test_removeObserverNotRegistered(self):
- """
- L{LogPublisher.removeObserver} removes an observer that is not
- registered.
- """
- o1 = lambda e: None
- o2 = lambda e: None
- o3 = lambda e: None
-
- publisher = LogPublisher(o1, o2)
- publisher.removeObserver(o3)
- self.assertEquals(set((o1, o2)), set(publisher.observers))
-
-
- def test_fanOut(self):
- """
- L{LogPublisher} calls its observers.
- """
- event = dict(foo=1, bar=2)
-
- events1 = []
- events2 = []
- events3 = []
-
- o1 = lambda e: events1.append(e)
- o2 = lambda e: events2.append(e)
- o3 = lambda e: events3.append(e)
-
- publisher = LogPublisher(o1, o2, o3)
- publisher(event)
- self.assertIn(event, events1)
- self.assertIn(event, events2)
- self.assertIn(event, events3)
-
-
- def test_observerRaises(self):
- nonTestEvents = []
- Logger.publisher.addObserver(lambda e: nonTestEvents.append(e))
-
- event = dict(foo=1, bar=2)
- exception = RuntimeError("ARGH! EVIL DEATH!")
-
- events = []
-
- def observer(event):
- events.append(event)
- raise exception
-
- publisher = LogPublisher(observer)
- publisher(event)
-
- # Verify that the observer saw my event
- self.assertIn(event, events)
-
- # Verify that the observer raised my exception
- errors = self.flushLoggedErrors(exception.__class__)
- self.assertEquals(len(errors), 1)
- self.assertIdentical(errors[0].value, exception)
-
- # Verify that the exception was logged
- for event in nonTestEvents:
- if (
- event.get("log_format", None) == OBSERVER_REMOVED and
- getattr(event.get("failure", None), "value") is exception
- ):
- break
- else:
- self.fail("Observer raised an exception "
- "and the exception was not logged.")
-
-
- def test_observerRaisesAndLoggerHatesMe(self):
- nonTestEvents = []
- Logger.publisher.addObserver(lambda e: nonTestEvents.append(e))
-
- event = dict(foo=1, bar=2)
- exception = RuntimeError("ARGH! EVIL DEATH!")
-
- def observer(event):
- raise RuntimeError("Sad panda")
-
- class GurkLogger(Logger):
- def failure(self, *args, **kwargs):
- raise exception
-
- publisher = LogPublisher(observer)
- publisher.log = GurkLogger()
- publisher(event)
-
- # Here, the lack of an exception thus far is a success, of sorts
-
-
-
-class DefaultLogPublisherTests(SetUpTearDown, unittest.TestCase):
- def test_addObserver(self):
- o1 = lambda e: None
- o2 = lambda e: None
- o3 = lambda e: None
-
- publisher = DefaultLogPublisher()
- publisher.addObserver(o1)
- publisher.addObserver(o2, filtered=True)
- publisher.addObserver(o3, filtered=False)
-
- self.assertEquals(
- set((o1, o2, publisher.legacyLogObserver)),
- set(publisher.filteredPublisher.observers),
- "Filtered observers do not match expected set"
- )
- self.assertEquals(
- set((o3, publisher.filters)),
- set(publisher.rootPublisher.observers),
- "Root observers do not match expected set"
- )
-
-
- def test_addObserverAgain(self):
- o1 = lambda e: None
- o2 = lambda e: None
- o3 = lambda e: None
-
- publisher = DefaultLogPublisher()
- publisher.addObserver(o1)
- publisher.addObserver(o2, filtered=True)
- publisher.addObserver(o3, filtered=False)
-
- # Swap filtered-ness of o2 and o3
- publisher.addObserver(o1)
- publisher.addObserver(o2, filtered=False)
- publisher.addObserver(o3, filtered=True)
-
- self.assertEquals(
- set((o1, o3, publisher.legacyLogObserver)),
- set(publisher.filteredPublisher.observers),
- "Filtered observers do not match expected set"
- )
- self.assertEquals(
- set((o2, publisher.filters)),
- set(publisher.rootPublisher.observers),
- "Root observers do not match expected set"
- )
-
-
- def test_removeObserver(self):
- o1 = lambda e: None
- o2 = lambda e: None
- o3 = lambda e: None
-
- publisher = DefaultLogPublisher()
- publisher.addObserver(o1)
- publisher.addObserver(o2, filtered=True)
- publisher.addObserver(o3, filtered=False)
- publisher.removeObserver(o2)
- publisher.removeObserver(o3)
-
- self.assertEquals(
- set((o1, publisher.legacyLogObserver)),
- set(publisher.filteredPublisher.observers),
- "Filtered observers do not match expected set"
- )
- self.assertEquals(
- set((publisher.filters,)),
- set(publisher.rootPublisher.observers),
- "Root observers do not match expected set"
- )
-
-
- def test_filteredObserver(self):
- namespace = __name__
-
- event_debug = dict(log_namespace=namespace,
- log_level=LogLevel.debug, log_format="")
- event_error = dict(log_namespace=namespace,
- log_level=LogLevel.error, log_format="")
- events = []
-
- observer = lambda e: events.append(e)
-
- publisher = DefaultLogPublisher()
-
- publisher.addObserver(observer, filtered=True)
- publisher(event_debug)
- publisher(event_error)
- self.assertNotIn(event_debug, events)
- self.assertIn(event_error, events)
-
-
- def test_filteredObserverNoFilteringKeys(self):
- event_debug = dict(log_level=LogLevel.debug)
- event_error = dict(log_level=LogLevel.error)
- event_none = dict()
- events = []
-
- observer = lambda e: events.append(e)
-
- publisher = DefaultLogPublisher()
- publisher.addObserver(observer, filtered=True)
- publisher(event_debug)
- publisher(event_error)
- publisher(event_none)
- self.assertNotIn(event_debug, events)
- self.assertNotIn(event_error, events)
- self.assertNotIn(event_none, events)
-
-
- def test_unfilteredObserver(self):
- namespace = __name__
-
- event_debug = dict(log_namespace=namespace, log_level=LogLevel.debug,
- log_format="")
- event_error = dict(log_namespace=namespace, log_level=LogLevel.error,
- log_format="")
- events = []
-
- observer = lambda e: events.append(e)
-
- publisher = DefaultLogPublisher()
-
- publisher.addObserver(observer, filtered=False)
- publisher(event_debug)
- publisher(event_error)
- self.assertIn(event_debug, events)
- self.assertIn(event_error, events)
-
-
-
-class FilteringLogObserverTests(SetUpTearDown, unittest.TestCase):
- """
- Tests for L{FilteringLogObserver}.
- """
-
- def test_interface(self):
- """
- L{FilteringLogObserver} is an L{ILogObserver}.
- """
- observer = FilteringLogObserver(lambda e: None, ())
- try:
- verifyObject(ILogObserver, observer)
- except BrokenMethodImplementation as e:
- self.fail(e)
-
-
- def filterWith(self, *filters):
- events = [
- dict(count=0),
- dict(count=1),
- dict(count=2),
- dict(count=3),
- ]
-
- class Filters(object):
- @staticmethod
- def twoMinus(event):
- if event["count"] <= 2:
- return PredicateResult.yes
- return PredicateResult.maybe
-
- @staticmethod
- def twoPlus(event):
- if event["count"] >= 2:
- return PredicateResult.yes
- return PredicateResult.maybe
-
- @staticmethod
- def notTwo(event):
- if event["count"] == 2:
- return PredicateResult.no
- return PredicateResult.maybe
-
- @staticmethod
- def no(event):
- return PredicateResult.no
-
- @staticmethod
- def bogus(event):
- return None
-
- predicates = (getattr(Filters, f) for f in filters)
- eventsSeen = []
- trackingObserver = lambda e: eventsSeen.append(e)
- filteringObserver = FilteringLogObserver(trackingObserver, predicates)
- for e in events:
- filteringObserver(e)
-
- return [e["count"] for e in eventsSeen]
-
-
- def test_shouldLogEvent_noFilters(self):
- self.assertEquals(self.filterWith(), [0, 1, 2, 3])
-
-
- def test_shouldLogEvent_noFilter(self):
- self.assertEquals(self.filterWith("notTwo"), [0, 1, 3])
-
-
- def test_shouldLogEvent_yesFilter(self):
- self.assertEquals(self.filterWith("twoPlus"), [0, 1, 2, 3])
-
-
- def test_shouldLogEvent_yesNoFilter(self):
- self.assertEquals(self.filterWith("twoPlus", "no"), [2, 3])
-
-
- def test_shouldLogEvent_yesYesNoFilter(self):
- self.assertEquals(self.filterWith("twoPlus", "twoMinus", "no"),
- [0, 1, 2, 3])
-
-
- def test_shouldLogEvent_badPredicateResult(self):
- self.assertRaises(TypeError, self.filterWith, "bogus")
-
-
- def test_call(self):
- e = dict(obj=object())
-
- def callWithPredicateResult(result):
- seen = []
- observer = FilteringLogObserver(lambda e: seen.append(e),
- (lambda e: result,))
- observer(e)
- return seen
-
- self.assertIn(e, callWithPredicateResult(PredicateResult.yes))
- self.assertIn(e, callWithPredicateResult(PredicateResult.maybe))
- self.assertNotIn(e, callWithPredicateResult(PredicateResult.no))
-
-
-
-class LegacyLoggerTests(SetUpTearDown, unittest.TestCase):
- """
- Tests for L{LegacyLogger}.
- """
-
- def test_namespace_default(self):
- """
- Default namespace is module name.
- """
- log = TestLegacyLogger(logger=None)
- self.assertEquals(log.newStyleLogger.namespace, __name__)
-
-
- def test_passThroughAttributes(self):
- """
- C{__getattribute__} on L{LegacyLogger} is passing through to Twisted's
- logging module.
- """
- log = TestLegacyLogger()
-
- # Not passed through
- self.assertIn("API-compatible", log.msg.__doc__)
- self.assertIn("API-compatible", log.err.__doc__)
-
- # Passed through
- self.assertIdentical(log.addObserver, twistedLogging.addObserver)
-
-
- def test_legacy_msg(self):
- """
- Test LegacyLogger's log.msg()
- """
- log = TestLegacyLogger()
-
- message = "Hi, there."
- kwargs = {"foo": "bar", "obj": object()}
-
- log.msg(message, **kwargs)
-
- self.assertIdentical(log.newStyleLogger.emitted["level"],
- LogLevel.info)
- self.assertEquals(log.newStyleLogger.emitted["format"], message)
-
- for key, value in kwargs.items():
- self.assertIdentical(log.newStyleLogger.emitted["kwargs"][key],
- value)
-
- log.msg(foo="")
-
- self.assertIdentical(log.newStyleLogger.emitted["level"],
- LogLevel.info)
- self.assertIdentical(log.newStyleLogger.emitted["format"], None)
-
-
- def test_legacy_err_implicit(self):
- """
- Test LegacyLogger's log.err() capturing the in-flight exception.
- """
- log = TestLegacyLogger()
-
- exception = RuntimeError("Oh me, oh my.")
- kwargs = {"foo": "bar", "obj": object()}
-
- try:
- raise exception
- except RuntimeError:
- log.err(**kwargs)
-
- self.legacy_err(log, kwargs, None, exception)
-
-
- def test_legacy_err_exception(self):
- """
- Test LegacyLogger's log.err() with a given exception.
- """
- log = TestLegacyLogger()
-
- exception = RuntimeError("Oh me, oh my.")
- kwargs = {"foo": "bar", "obj": object()}
- why = "Because I said so."
-
- try:
- raise exception
- except RuntimeError as e:
- log.err(e, why, **kwargs)
-
- self.legacy_err(log, kwargs, why, exception)
-
-
- def test_legacy_err_failure(self):
- """
- Test LegacyLogger's log.err() with a given L{Failure}.
- """
- log = TestLegacyLogger()
-
- exception = RuntimeError("Oh me, oh my.")
- kwargs = {"foo": "bar", "obj": object()}
- why = "Because I said so."
-
- try:
- raise exception
- except RuntimeError:
- log.err(Failure(), why, **kwargs)
-
- self.legacy_err(log, kwargs, why, exception)
-
-
- def test_legacy_err_bogus(self):
- """
- Test LegacyLogger's log.err() with a bogus argument.
- """
- log = TestLegacyLogger()
-
- exception = RuntimeError("Oh me, oh my.")
- kwargs = {"foo": "bar", "obj": object()}
- why = "Because I said so."
- bogus = object()
-
- try:
- raise exception
- except RuntimeError:
- log.err(bogus, why, **kwargs)
-
- errors = self.flushLoggedErrors(exception.__class__)
- self.assertEquals(len(errors), 0)
-
- self.assertIdentical(log.newStyleLogger.emitted["level"],
- LogLevel.error)
- self.assertEquals(log.newStyleLogger.emitted["format"], repr(bogus))
- self.assertIdentical(log.newStyleLogger.emitted["kwargs"]["why"], why)
-
- for key, value in kwargs.items():
- self.assertIdentical(log.newStyleLogger.emitted["kwargs"][key],
- value)
-
-
- def legacy_err(self, log, kwargs, why, exception):
- #
- # log.failure() will cause trial to complain, so here we check that
- # trial saw the correct error and remove it from the list of things to
- # complain about.
- #
- errors = self.flushLoggedErrors(exception.__class__)
- self.assertEquals(len(errors), 1)
-
- self.assertIdentical(log.newStyleLogger.emitted["level"],
- LogLevel.error)
- self.assertEquals(log.newStyleLogger.emitted["format"], None)
- emittedKwargs = log.newStyleLogger.emitted["kwargs"]
- self.assertIdentical(emittedKwargs["failure"].__class__, Failure)
- self.assertIdentical(emittedKwargs["failure"].value, exception)
- self.assertIdentical(emittedKwargs["why"], why)
-
- for key, value in kwargs.items():
- self.assertIdentical(log.newStyleLogger.emitted["kwargs"][key],
- value)
-
-
-
-class Unformattable(object):
- """
- An object that raises an exception from C{__repr__}.
- """
-
- def __repr__(self):
- return str(1 / 0)
</del></span></pre></div>
<a id="twexttrunktwextwholdap_servicepy"></a>
<div class="modfile"><h4>Modified: twext/trunk/twext/who/ldap/_service.py (14846 => 14847)</h4>
<pre class="diff"><span>
<span class="info">--- twext/trunk/twext/who/ldap/_service.py        2015-05-28 21:41:18 UTC (rev 14846)
+++ twext/trunk/twext/who/ldap/_service.py        2015-05-28 21:49:02 UTC (rev 14847)
</span><span class="lines">@@ -29,13 +29,13 @@
</span><span class="cx"> import ldap.async
</span><span class="cx">
</span><span class="cx"> from twisted.python.constants import Names, NamedConstant
</span><ins>+from twisted.python.threadpool import ThreadPool
+from twisted.logger import Logger
+from twisted.internet import reactor
</ins><span class="cx"> from twisted.internet.defer import succeed, inlineCallbacks, returnValue
</span><span class="cx"> from twisted.internet.threads import deferToThreadPool
</span><span class="cx"> from twisted.cred.credentials import IUsernamePassword
</span><del>-from twisted.python.threadpool import ThreadPool
-from twisted.internet import reactor
</del><span class="cx">
</span><del>-from twext.python.log import Logger
</del><span class="cx"> from twext.python.types import MappingProxyType
</span><span class="cx">
</span><span class="cx"> from ..idirectory import (
</span></span></pre></div>
<a id="twexttrunktwextwhoopendirectory_servicepy"></a>
<div class="modfile"><h4>Modified: twext/trunk/twext/who/opendirectory/_service.py (14846 => 14847)</h4>
<pre class="diff"><span>
<span class="info">--- twext/trunk/twext/who/opendirectory/_service.py        2015-05-28 21:41:18 UTC (rev 14846)
+++ twext/trunk/twext/who/opendirectory/_service.py        2015-05-28 21:49:02 UTC (rev 14847)
</span><span class="lines">@@ -25,12 +25,11 @@
</span><span class="cx"> from uuid import UUID
</span><span class="cx"> from zope.interface import implementer
</span><span class="cx">
</span><ins>+from twisted.logger import Logger
</ins><span class="cx"> from twisted.internet.defer import succeed, fail, inlineCallbacks, returnValue
</span><span class="cx"> from twisted.internet.threads import deferToThread
</span><span class="cx"> from twisted.web.guard import DigestCredentialFactory
</span><span class="cx">
</span><del>-from twext.python.log import Logger
-
</del><span class="cx"> from ..idirectory import (
</span><span class="cx"> DirectoryServiceError, DirectoryAvailabilityError,
</span><span class="cx"> InvalidDirectoryRecordError, QueryNotSupportedError,
</span></span></pre>
</div>
</div>
</body>
</html>