[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