[CalendarServer-changes] [11257] CalendarServer/trunk/twext/python
source_changes at macosforge.org
source_changes at macosforge.org
Wed May 29 14:38:52 PDT 2013
Revision: 11257
http://trac.calendarserver.org//changeset/11257
Author: glyph at apple.com
Date: 2013-05-29 14:38:52 -0700 (Wed, 29 May 2013)
Log Message:
-----------
Allow for composing rather than subclassing to get convenience of logging
mixin. Bonus: now you get the instance or class as a structured log object.
Modified Paths:
--------------
CalendarServer/trunk/twext/python/log.py
CalendarServer/trunk/twext/python/test/test_log.py
Modified: CalendarServer/trunk/twext/python/log.py
===================================================================
--- CalendarServer/trunk/twext/python/log.py 2013-05-29 20:37:23 UTC (rev 11256)
+++ CalendarServer/trunk/twext/python/log.py 2013-05-29 21:38:52 UTC (rev 11257)
@@ -1,3 +1,4 @@
+# -*- test-case-name: twext.python.test.test_log-*-
##
# Copyright (c) 2006-2013 Apple Inc. All rights reserved.
#
@@ -170,12 +171,15 @@
"""
Logging object.
"""
- def __init__(self, namespace=None):
+ 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 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 messages 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:
currentFrame = inspect.currentframe()
@@ -185,8 +189,30 @@
namespace = callerModule
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 something(self):
+ self.log.info("Hello")
+
+ a L{Logger}'s namespace will be set to the name of the class it is
+ declared on, in this case, C{athing.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)
@@ -201,9 +227,10 @@
if not self.willLogAtLevel(level):
return
- kwargs["level"] = level
- kwargs["levelName"] = level.name
- kwargs["namespace"] = self.namespace
+ kwargs.update(
+ level = level, levelName = level.name,
+ namespace = self.namespace, source = self.source,
+ )
#
# Twisted's logging supports indicating a python log level, so let's
Modified: CalendarServer/trunk/twext/python/test/test_log.py
===================================================================
--- CalendarServer/trunk/twext/python/test/test_log.py 2013-05-29 20:37:23 UTC (rev 11256)
+++ CalendarServer/trunk/twext/python/test/test_log.py 2013-05-29 21:38:52 UTC (rev 11257)
@@ -32,10 +32,6 @@
class TestLogger(Logger):
- def __init__(self, namespace=None):
- super(TestLogger, self).__init__(namespace)
-
-
def emit(self, level, message, **kwargs):
def observer(eventDict):
self.eventDict = eventDict
@@ -54,6 +50,21 @@
+class LogComposedObject(object):
+ """
+ Just a regular object.
+ """
+ log = TestLogger()
+
+ def __init__(self, state=None):
+ self.state = state
+
+
+ def __str__(self):
+ return "<LogComposedObject %s>" % (self.state,)
+
+
+
class LoggingEnabledObject(LoggingMixIn):
pass
@@ -86,6 +97,33 @@
self.assertEquals(object.logger.namespace, "twext.python.test.test_log.LoggingEnabledObject")
+ 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{source} key
+ is available to format strings.
+ """
+ obj = LogComposedObject("hello")
+ log = obj.log
+ log.error(format="Hello. %(source)s")
+ stuff = twistedLogging.textFromEventDict(log.eventDict)
+ self.assertIn("Hello. <LogComposedObject hello>", stuff)
+
+
def test_basic_Logger(self):
"""
Test that log levels and messages are emitted correctly for
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20130529/6834e812/attachment-0001.html>
More information about the calendarserver-changes
mailing list