[Zope3-checkins] SVN: Zope3/trunk/ Made hit logging configurable in zope.conf (instead of being hardcoded to

Marius Gedminas marius at pov.lt
Sun May 30 17:54:54 EDT 2004


Log message for revision 25133:
Made hit logging configurable in zope.conf (instead of being hardcoded to
sys.stdout).

There's a new section <hitlog> in zope.conf.  It is like <eventlog>, but
the log level and message formats are ignored (the hit log always uses
the "common" HTTP log format).

I hope the changes to ZConfig are acceptable.  If so, they should be merged to
ZConfig upstream repository.  If not, then the new hitlog section should be
moved to somewhere in zope/app/server by someone who knows more about ZConfig
than I do.

CommonHTTPHitLogger now uses a new IMessageLogger implementation called
PythonLogger.  This logger simply forwards every message to a logger named
'hitlog' from the Python logging module (which is configured by the
aforementioned <hitlog> sectioned).

I question the necessity of most of the other logger classes in
zope.server.logger -- it seems to me they're reimplementing the wheel (the
standard logging module).




-=-
Modified: Zope3/trunk/doc/CHANGES.txt
===================================================================
--- Zope3/trunk/doc/CHANGES.txt	2004-05-30 07:55:31 UTC (rev 25132)
+++ Zope3/trunk/doc/CHANGES.txt	2004-05-30 21:54:54 UTC (rev 25133)
@@ -11,8 +11,10 @@
     New features
 
       - Event handlers can now be implemented as regular Python
-        functions. 
+        functions.
 
+      - Hit logging is now configurable in zope.conf.
+
     Bug fixes
 
       - Fixed so that a UnicodeDecodeError isn't produced when Internet
@@ -36,7 +38,6 @@
 
         - No longer have event publication or subscription services.
 
-
       - We had an object hub service. The object hub service was responsible
         for:
 

Modified: Zope3/trunk/src/ZConfig/components/logger/component.xml
===================================================================
--- Zope3/trunk/src/ZConfig/components/logger/component.xml	2004-05-30 07:55:31 UTC (rev 25132)
+++ Zope3/trunk/src/ZConfig/components/logger/component.xml	2004-05-30 21:54:54 UTC (rev 25133)
@@ -6,5 +6,6 @@
   <import package="ZConfig.components.logger" file="handlers.xml"/>
   <import package="ZConfig.components.logger" file="logger.xml"/>
   <import package="ZConfig.components.logger" file="eventlog.xml"/>
+  <import package="ZConfig.components.logger" file="hitlog.xml"/>
 
 </component>

Added: Zope3/trunk/src/ZConfig/components/logger/hitlog.xml
===================================================================
--- Zope3/trunk/src/ZConfig/components/logger/hitlog.xml	2004-05-30 07:55:31 UTC (rev 25132)
+++ Zope3/trunk/src/ZConfig/components/logger/hitlog.xml	2004-05-30 21:54:54 UTC (rev 25133)
@@ -0,0 +1,18 @@
+<component prefix="ZConfig.components.logger.logger">
+
+  <import package="ZConfig.components.logger" file="abstract.xml"/>
+  <import package="ZConfig.components.logger" file="base-logger.xml"/>
+
+  <sectiontype name="hitlog"
+               datatype=".HitLogFactory"
+               extends="ZConfig.logger.base-logger"
+               implements="ZConfig.logger.log">
+    <description>
+      Configuration for the hit logger.
+
+      Note that the setting of verbosity level and message formats are not
+      used.
+    </description>
+  </sectiontype>
+
+</component>


Property changes on: Zope3/trunk/src/ZConfig/components/logger/hitlog.xml
___________________________________________________________________
Name: svn:eol-style
   + native

Modified: Zope3/trunk/src/ZConfig/components/logger/logger.py
===================================================================
--- Zope3/trunk/src/ZConfig/components/logger/logger.py	2004-05-30 07:55:31 UTC (rev 25132)
+++ Zope3/trunk/src/ZConfig/components/logger/logger.py	2004-05-30 21:54:54 UTC (rev 25133)
@@ -13,6 +13,7 @@
 ##############################################################################
 """ZConfig factory datatypes for loggers."""
 
+import logging
 from ZConfig.components.logger.factory import Factory
 
 
@@ -34,7 +35,6 @@
 
     def create(self):
         # set the logger up
-        import logging
         logger = logging.getLogger(self.name)
         logger.setLevel(self.level)
         if self.handler_factories:
@@ -56,7 +56,6 @@
         If all handlers and the logger itself have level==NOTSET, this
         returns NOTSET.
         """
-        import logging
         lowest = self.level
         for factory in self.handler_factories:
             level = factory.getLevel()
@@ -88,6 +87,21 @@
     name = None
 
 
+class HitLogFactory(LoggerFactoryBase):
+    """Logger factory that returns the hit logger."""
+
+    name = "hitlog"
+
+    def create(self):
+        logger = LoggerFactoryBase.create(self)
+        logger.setLevel(logging.INFO)
+        logger.propagate = False
+        formatter = logging.Formatter()
+        for handler in logger.handlers:
+            handler.setFormatter(formatter)
+        return logger
+
+
 class LoggerFactory(LoggerFactoryBase):
     """Logger factory that returns the named logger."""
 

Modified: Zope3/trunk/src/ZConfig/components/logger/tests/test_logger.py
===================================================================
--- Zope3/trunk/src/ZConfig/components/logger/tests/test_logger.py	2004-05-30 07:55:31 UTC (rev 25132)
+++ Zope3/trunk/src/ZConfig/components/logger/tests/test_logger.py	2004-05-30 21:54:54 UTC (rev 25133)
@@ -32,8 +32,10 @@
     # XXX This tries to save and restore the state of logging around
     # the test.  Somewhat surgical; there may be a better way.
 
+    name = None
+
     def setUp(self):
-        self._old_logger = logging.getLogger()
+        self._old_logger = logging.getLogger(self.name)
         self._old_level = self._old_logger.level
         self._old_handlers = self._old_logger.handlers[:]
         self._old_logger.handlers[:] = []
@@ -46,16 +48,7 @@
             self._old_logger.addHandler(h)
         self._old_logger.setLevel(self._old_level)
 
-
-class TestConfig(LoggingTestBase):
-
     _schema = None
-    _schematext = """
-      <schema>
-        <import package='ZConfig.components.logger'/>
-        <section type='eventlog' name='*' attribute='eventlog'/>
-      </schema>
-    """
 
     def get_schema(self):
         if self._schema is None:
@@ -69,6 +62,16 @@
         self.assert_(not handler)
         return conf
 
+
+class TestConfig(LoggingTestBase):
+
+    _schematext = """
+      <schema>
+        <import package='ZConfig.components.logger'/>
+        <section type='eventlog' name='*' attribute='eventlog'/>
+      </schema>
+    """
+
     def test_logging_level(self):
         # Make sure the expected names are supported; it's not clear
         # how to check the values in a meaningful way.
@@ -223,8 +226,59 @@
         return logger
 
 
+class TestHitLogging(LoggingTestBase):
+
+    name = "hitlog"
+
+    _schematext = """
+      <schema>
+        <import package='ZConfig.components.logger'/>
+        <section type='hitlog' name='*' attribute='hitlog'/>
+      </schema>
+    """
+
+    def test_config_without_logger(self):
+        conf = self.get_config("")
+        self.assert_(conf.hitlog is None)
+
+    def test_config_without_handlers(self):
+        logger = self.check_simple_logger("<hitlog/>")
+        # Make sure there's a NullHandler, since a warning gets
+        # printed if there are no handlers:
+        self.assertEqual(len(logger.handlers), 1)
+        self.assert_(isinstance(logger.handlers[0],
+                                loghandler.NullHandler))
+
+    def test_formatter(self):
+        logger = self.check_simple_logger("<hitlog>\n"
+                                          "  <syslog>\n"
+                                          "    level error\n"
+                                          "    facility local3\n"
+                                          "    format xyzzy\n"
+                                          "  </syslog>\n"
+                                          "</hitlog>")
+        self.assertEqual(len(logger.handlers), 1)
+        syslog = logger.handlers[0]
+        self.assertEqual(syslog.level, logging.ERROR)
+        self.assert_(isinstance(syslog, loghandler.SysLogHandler))
+        self.assertEqual(syslog.formatter._fmt, "%(message)s")
+
+    def check_simple_logger(self, text):
+        conf = self.get_config(text)
+        self.assert_(conf.hitlog is not None)
+        logger = conf.hitlog()
+        self.assert_(isinstance(logger, logging.Logger))
+        self.assert_(not logger.propagate)
+        self.assertEquals(logger.name, "hitlog")
+        self.assertEquals(logger.level, logging.INFO)
+        return logger
+
+
 def test_suite():
-    return unittest.makeSuite(TestConfig)
+    suite = unittest.TestSuite()
+    suite.addTest(unittest.makeSuite(TestConfig))
+    suite.addTest(unittest.makeSuite(TestHitLogging))
+    return suite
 
 if __name__ == '__main__':
     unittest.main(defaultTest="test_suite")

Modified: Zope3/trunk/src/zope/app/server/main.py
===================================================================
--- Zope3/trunk/src/zope/app/server/main.py	2004-05-30 07:55:31 UTC (rev 25132)
+++ Zope3/trunk/src/zope/app/server/main.py	2004-05-30 21:54:54 UTC (rev 25133)
@@ -84,6 +84,7 @@
     sys.setcheckinterval(options.check_interval)
 
     options.eventlog()
+    options.hitlog()
 
     zope.app.appsetup.config(options.site_definition)
 

Modified: Zope3/trunk/src/zope/app/server/schema.xml
===================================================================
--- Zope3/trunk/src/zope/app/server/schema.xml	2004-05-30 07:55:31 UTC (rev 25132)
+++ Zope3/trunk/src/zope/app/server/schema.xml	2004-05-30 21:54:54 UTC (rev 25133)
@@ -31,6 +31,12 @@
     </description>
   </section>
 
+  <section type="hitlog" attribute="hitlog" name="*">
+    <description>
+      Configuration for the hit log.
+    </description>
+  </section>
+
   <multisection type="server" name="*" attribute="servers" />
 
   <key name="site-definition" default="site.zcml">

Modified: Zope3/trunk/src/zope/server/http/commonhitlogger.py
===================================================================
--- Zope3/trunk/src/zope/server/http/commonhitlogger.py	2004-05-30 07:55:31 UTC (rev 25132)
+++ Zope3/trunk/src/zope/server/http/commonhitlogger.py	2004-05-30 21:54:54 UTC (rev 25133)
@@ -20,7 +20,7 @@
 import sys
 
 from zope.server.http.http_date import monthname
-from zope.server.logger.filelogger import FileLogger
+from zope.server.logger.pythonlogger import PythonLogger
 from zope.server.logger.resolvinglogger import ResolvingLogger
 from zope.server.logger.unresolvinglogger import UnresolvingLogger
 
@@ -31,7 +31,7 @@
     def __init__(self, logger_object=None, resolver=None):
         if logger_object is None:
             # logger_object is an IMessageLogger
-            logger_object = FileLogger(sys.stdout)
+            logger_object = PythonLogger('hitlog')
 
         # self.output is an IRequestLogger
         if resolver is not None:

Added: Zope3/trunk/src/zope/server/http/tests/test_commonhitlogger.py
===================================================================
--- Zope3/trunk/src/zope/server/http/tests/test_commonhitlogger.py	2004-05-30 07:55:31 UTC (rev 25132)
+++ Zope3/trunk/src/zope/server/http/tests/test_commonhitlogger.py	2004-05-30 21:54:54 UTC (rev 25133)
@@ -0,0 +1,50 @@
+##############################################################################
+#
+# Copyright (c) 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+
+$Id$
+"""
+
+import unittest
+import logging
+
+
+class TestCommonHitLogger(unittest.TestCase):
+
+    def test_default_constructor(self):
+        from zope.server.http.commonhitlogger import CommonHitLogger
+        from zope.server.logger.unresolvinglogger import UnresolvingLogger
+        from zope.server.logger.pythonlogger import PythonLogger
+        logger = CommonHitLogger()
+        # CommonHitLogger is registered as an argumentless factory via
+        # ZCML, so the defaults should be sensible
+        self.assert_(isinstance(logger.output, UnresolvingLogger))
+        self.assert_(isinstance(logger.output.logger, PythonLogger))
+        self.assert_(logger.output.logger.name, 'hitlog')
+        self.assert_(logger.output.logger.level, logging.INFO)
+
+    # XXX please add unit tests for other methods as well:
+    #     compute_timezone_for_log
+    #     log_date_string
+    #     log
+
+
+def test_suite():
+    suite = unittest.TestSuite()
+    suite.addTest(unittest.makeSuite(TestCommonHitLogger))
+    return suite
+
+
+if __name__ == '__main__':
+    unittest.main()


Property changes on: Zope3/trunk/src/zope/server/http/tests/test_commonhitlogger.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: Zope3/trunk/src/zope/server/logger/pythonlogger.py
===================================================================
--- Zope3/trunk/src/zope/server/logger/pythonlogger.py	2004-05-30 07:55:31 UTC (rev 25132)
+++ Zope3/trunk/src/zope/server/logger/pythonlogger.py	2004-05-30 21:54:54 UTC (rev 25133)
@@ -0,0 +1,44 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+
+$Id$
+"""
+import logging
+
+from zope.server.interfaces.logger import IMessageLogger
+from zope.interface import implements
+
+__metaclass__ = type
+
+
+class PythonLogger:
+    """Proxy for Python's logging module
+    """
+
+    implements(IMessageLogger)
+
+    def __init__(self, name=None, level=logging.INFO):
+        self.name = name
+        self.level = level
+        self.logger = logging.getLogger(name)
+
+    def __repr__(self):
+        return '<python logger: %s %s>' % (self.name,
+                    logging.getLevelName(self.level))
+
+    def logMessage(self, message):
+        """See IMessageLogger"""
+        self.logger.log(self.level, message.rstrip())
+


Property changes on: Zope3/trunk/src/zope/server/logger/pythonlogger.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: Zope3/trunk/src/zope/server/logger/tests/__init__.py
===================================================================
--- Zope3/trunk/src/zope/server/logger/tests/__init__.py	2004-05-30 07:55:31 UTC (rev 25132)
+++ Zope3/trunk/src/zope/server/logger/tests/__init__.py	2004-05-30 21:54:54 UTC (rev 25133)
@@ -0,0 +1,2 @@
+#
+# This file is necessary to make this directory a package.


Property changes on: Zope3/trunk/src/zope/server/logger/tests/__init__.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: Zope3/trunk/src/zope/server/logger/tests/test_pythonlogger.py
===================================================================
--- Zope3/trunk/src/zope/server/logger/tests/test_pythonlogger.py	2004-05-30 07:55:31 UTC (rev 25132)
+++ Zope3/trunk/src/zope/server/logger/tests/test_pythonlogger.py	2004-05-30 21:54:54 UTC (rev 25133)
@@ -0,0 +1,69 @@
+##############################################################################
+#
+# Copyright (c) 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+
+$Id$
+"""
+
+import unittest
+import logging
+from zope.interface.verify import verifyObject
+
+
+class HandlerStub(logging.Handler):
+
+    last_record = None
+
+    def emit(self, record):
+        self.last_record = record
+
+
+class TestPythonLogger(unittest.TestCase):
+
+    name = 'test.pythonlogger'
+
+    def setUp(self):
+        self.logger = logging.getLogger(self.name)
+        self.logger.propagate = False
+        self.logger.setLevel(logging.INFO)
+        self.handler = HandlerStub()
+        self.logger.addHandler(self.handler)
+
+    def tearDown(self):
+        self.logger.removeHandler(self.handler)
+
+    def test(self):
+        from zope.server.logger.pythonlogger import PythonLogger
+        from zope.server.interfaces.logger import IMessageLogger
+        plogger = PythonLogger(self.name)
+        verifyObject(IMessageLogger, plogger)
+        msg1 = 'test message 1'
+        plogger.logMessage(msg1)
+        self.assertEquals(self.handler.last_record.msg, msg1)
+        self.assertEquals(self.handler.last_record.levelno, logging.INFO)
+        msg2 = 'test message 2\r\n'
+        plogger.level = logging.ERROR
+        plogger.logMessage(msg2)
+        self.assertEquals(self.handler.last_record.msg, msg2.rstrip())
+        self.assertEquals(self.handler.last_record.levelno, logging.ERROR)
+
+
+def test_suite():
+    suite = unittest.TestSuite()
+    suite.addTest(unittest.makeSuite(TestPythonLogger))
+    return suite
+
+
+if __name__ == '__main__':
+    unittest.main()


Property changes on: Zope3/trunk/src/zope/server/logger/tests/test_pythonlogger.py
___________________________________________________________________
Name: svn:eol-style
   + native

Modified: Zope3/trunk/zope.conf.in
===================================================================
--- Zope3/trunk/zope.conf.in	2004-05-30 07:55:31 UTC (rev 25132)
+++ Zope3/trunk/zope.conf.in	2004-05-30 21:54:54 UTC (rev 25133)
@@ -30,6 +30,20 @@
   </filestorage>
 </zodb>
 
+<hitlog>
+  # This sets up logging to both a file (access.log) and to standard
+  # output (STDOUT).  The "path" setting can be a relative or absolute
+  # filesystem path or the tokens STDOUT or STDERR.
+
+  <logfile>
+    path access.log
+  </logfile>
+
+  <logfile>
+    path STDOUT
+  </logfile>
+</hitlog>
+
 <eventlog>
   # This sets up logging to both a file (z3.log) and to standard
   # output (STDOUT).  The "path" setting can be a relative or absolute




More information about the Zope3-Checkins mailing list