[Checkins] SVN: zc.zkzopeserver/trunk/ - Added logging-configuration support.

Jim Fulton jim at zope.com
Thu Feb 2 22:50:05 UTC 2012


Log message for revision 124288:
  - Added logging-configuration support.
  
  - Fixed: servers were registered with the host information returned by
    socket.getsockname(), which was unhelpful.
  
  - Fixed: Killing a server (with SIGTERM) didn't shut down the
    ZooKeeper connection cleanly, causing a delay in removing registered
    ephemeral nodes.
  

Changed:
  U   zc.zkzopeserver/trunk/buildout.cfg
  U   zc.zkzopeserver/trunk/src/zc/zkzopeserver/README.txt
  U   zc.zkzopeserver/trunk/src/zc/zkzopeserver/__init__.py
  U   zc.zkzopeserver/trunk/src/zc/zkzopeserver/tests.py

-=-
Modified: zc.zkzopeserver/trunk/buildout.cfg
===================================================================
--- zc.zkzopeserver/trunk/buildout.cfg	2012-02-02 14:58:36 UTC (rev 124287)
+++ zc.zkzopeserver/trunk/buildout.cfg	2012-02-02 22:50:04 UTC (rev 124288)
@@ -1,6 +1,6 @@
 [buildout]
 develop = .
-parts = test py paste.ini
+parts = py server
 
 [test]
 recipe = zc.recipe.testrunner
@@ -11,6 +11,7 @@
 eggs = ${test:eggs}
        PasteScript
        bobo
+       zc.zk
        
 interpreter = py
 
@@ -25,4 +26,8 @@
   use = egg:zc.zkzopeserver
   zookeeper = 127.0.0.1:2181
   path = /
-  monitor_server = true
+  monitor_server = 127.0.0.1:0
+
+[server]
+recipe = zc.zdaemonrecipe
+program = ${buildout:bin-directory}/paster serve --log-file=- ${paste.ini:location}

Modified: zc.zkzopeserver/trunk/src/zc/zkzopeserver/README.txt
===================================================================
--- zc.zkzopeserver/trunk/src/zc/zkzopeserver/README.txt	2012-02-02 14:58:36 UTC (rev 124287)
+++ zc.zkzopeserver/trunk/src/zc/zkzopeserver/README.txt	2012-02-02 22:50:04 UTC (rev 124288)
@@ -48,6 +48,24 @@
    The value is an address of the form HOST:PORT.  See `Monitor
    server`_ below. (Host can be empty to listen on all interfaces.)
 
+loggers
+   Logging configuration.
+
+   This can be one of:
+
+   - A logging level name (CRITICAL, ERROR, WARNING, INFO, or DEBUG),
+     or
+
+   - A ZConfig loggers-definition string.
+
+     If the configuration includes format strings, you'll need to use
+     double dollar signs rather than %, as in::
+
+        format $$(message)s
+
+     This is necessary due to the use of string formats in the Paste
+     Deployment configuration syntax.
+
 .. test
 
     >>> import ConfigParser, StringIO
@@ -55,6 +73,10 @@
     >>> parser.readfp(StringIO.StringIO(server_config))
     >>> kw = dict(parser.items('server:main'))
 
+    >>> import zope.testing.loggingsupport
+    >>> loghandler = zope.testing.loggingsupport.InstalledHandler(
+    ...     'zc.zkzopeserver')
+
     >>> import pkg_resources
     >>> dist = kw.pop('use').split(':')[1]
     >>> [run] = [v.load()
@@ -84,6 +106,36 @@
     >>> zk.get_children('/fooservice/providers')
     []
 
+  A SIGTERM signal handler is installed:
+
+    >>> import signal
+    >>> [sig, handler] = signal.signal.call_args[0]
+    >>> sig == signal.SIGTERM
+    True
+    >>> try: handler(sig, None)
+    ... except SystemExit, v: print v
+    0
+
+    >>> signal.getsignal.return_value = handler
+    >>> signal.signal.reset_mock()
+
+  The fact that a handler was installed is logged, as is the address:
+
+    >>> print loghandler
+    zc.zkzopeserver INFO
+      Installed SIGTERM handler
+    zc.zkzopeserver INFO
+      Serving on :35177
+
+    >>> loghandler.clear()
+
+  Nothing was done w logging:
+
+    >>> import logging
+    >>> logging.basicConfig.call_args
+    >>> import ZConfig
+    >>> ZConfig.configureLoggers.call_args
+
 Monitor server
 ==============
 
@@ -152,6 +204,15 @@
 
     >>> handler.uninstall()
 
+  The signal handler wasn't set again, as it was already set:
+
+    >>> signal.signal.call_args
+    >>> print loghandler
+    zc.zkzopeserver INFO
+      Serving on :46834
+
+    >>> loghandler.uninstall()
+
 Some notes on the monitor server:
 
 - A monitor server won't be useful unless you've registered some
@@ -220,12 +281,18 @@
 Change History
 ==============
 
-0.2.1 (2012-02-??)
+0.3.0 (2012-02-??)
 ------------------
 
+- Added logging-configuration support.
+
 - Fixed: servers were registered with the host information returned by
   socket.getsockname(), which was unhelpful.
 
+- Fixed: Killing a server (with SIGTERM) didn't shut down the
+  ZooKeeper connection cleanly, causing a delay in removing registered
+  ephemeral nodes.
+
 0.2.0 (2012-01-18)
 ------------------
 

Modified: zc.zkzopeserver/trunk/src/zc/zkzopeserver/__init__.py
===================================================================
--- zc.zkzopeserver/trunk/src/zc/zkzopeserver/__init__.py	2012-02-02 14:58:36 UTC (rev 124287)
+++ zc.zkzopeserver/trunk/src/zc/zkzopeserver/__init__.py	2012-02-02 22:50:04 UTC (rev 124288)
@@ -12,12 +12,17 @@
 #
 ##############################################################################
 import asyncore
+import logging
 import re
+import signal
+import sys
 import threading
 import zc.zk
 import zope.server.dualmodechannel
 import zope.server.taskthreads
 
+logger = logging.getLogger(__name__)
+
 event_for_testing = threading.Event()
 server_for_testing = None
 
@@ -45,10 +50,18 @@
         zookeeper, path, session_timeout=None,
         name=__name__, host='', port=0, threads=1, monitor_server=None,
         zservertracelog=None,
+        loggers=None,
         ):
     port = int(port)
     threads = int(threads)
 
+    if loggers:
+        if re.match('\w+$', loggers) and hasattr(logging, loggers):
+            logging.basicConfig(level=getattr(logging, loggers))
+        else:
+            import ZConfig
+            ZConfig.configureLoggers(loggers.replace('$$(', '%('))
+
     task_dispatcher = zope.server.taskthreads.ThreadedTaskDispatcher()
     task_dispatcher.setThreadCount(threads)
     if zservertracelog == 'true':
@@ -67,8 +80,8 @@
 
     server.ZooKeeper = zc.zk.ZooKeeper(
         zookeeper, session_timeout and int(session_timeout))
-    server.ZooKeeper.register_server(
-        path, "%s:%s" % (host, server.socket.getsockname()[1]), **props)
+    addr = "%s:%s" % (host, server.socket.getsockname()[1])
+    server.ZooKeeper.register_server(path, addr, **props)
 
     map = asyncore.socket_map
     poll_fun = asyncore.poll
@@ -77,7 +90,12 @@
     server_for_testing = server
     event_for_testing.set()
 
+    if not signal.getsignal(signal.SIGTERM):
+        signal.signal(signal.SIGTERM, lambda *a: sys.exit(0))
+        logger.info('Installed SIGTERM handler')
+
     try:
+        logger.info('Serving on %s', addr)
         while server.accepting:
             poll_fun(30.0, map)
     finally:

Modified: zc.zkzopeserver/trunk/src/zc/zkzopeserver/tests.py
===================================================================
--- zc.zkzopeserver/trunk/src/zc/zkzopeserver/tests.py	2012-02-02 14:58:36 UTC (rev 124287)
+++ zc.zkzopeserver/trunk/src/zc/zkzopeserver/tests.py	2012-02-02 22:50:04 UTC (rev 124288)
@@ -59,6 +59,7 @@
     ...         host='localhost',
     ...         port='3042',
     ...         threads='3',
+    ...         loggers='INFO',
     ...         )
 
     >>> import zc.zkzopeserver
@@ -92,6 +93,11 @@
     >>> reset()
     3
 
+    basicConfig was called:
+
+    >>> import logging
+    >>> logging.basicConfig.assert_called_with(level=logging.INFO)
+
     Did session_timeout get passed? If we're using the mock ZooKeeper,
     we camn tell:
 
@@ -108,6 +114,29 @@
     >>> zk.close()
     """
 
+def test_ZConfig_logger_config():
+    """
+    Make sure various advertized options work:
+
+    >>> @zc.thread.Thread
+    ... def server():
+    ...     zc.zkzopeserver.run(
+    ...         slow_app, {},
+    ...         zookeeper='zookeeper.example.com:2181',
+    ...         path='/fooservice/providers',
+    ...         loggers='This is not real $$(message)s $$(whatever)s',
+    ...         )
+
+    >>> import zc.zkzopeserver
+    >>> zc.zkzopeserver.event_for_testing.wait(1)
+
+    >>> import ZConfig
+    >>> ZConfig.configureLoggers.assert_called_with(
+    ...     'This is not real %(message)s %(whatever)s')
+
+    >>> zc.zkzopeserver.stop_for_testing(server)
+    """
+
 def test_monitor_server_w_empty_host():
     r"""
 
@@ -131,9 +160,9 @@
     >>> zc.zkzopeserver.stop_for_testing(server)
     >>> zk.close()
 
-    """
+    """ # '
 
-def readmesetup(test):
+def setup(test):
     zc.zk.testing.setUp(test)
     setupstack.context_manager(
         test, mock.patch('netifaces.interfaces')).return_value = 'iface'
@@ -141,13 +170,23 @@
         test, mock.patch('netifaces.ifaddresses')
         ).return_value = {2: [dict(addr='1.2.3.4')]}
 
-def readmeteardown(test):
+    setupstack.context_manager(
+        test, mock.patch('signal.getsignal')
+        ).return_value = 0
+    setupstack.context_manager(test, mock.patch('signal.signal'))
+
+    setupstack.context_manager(test, mock.patch('logging.basicConfig'))
+
+    setupstack.context_manager(test, mock.patch('ZConfig.configureLoggers'))
+
+def teardown(test):
     setupstack.tearDown(test)
     zc.zk.testing.tearDown(test)
 
 def test_suite():
     checker = zope.testing.renormalizing.RENormalizing([
         (re.compile('pid = \d+'), 'pid = 9999'),
+        (re.compile('Serving on :\d+'), 'Serving on :9999'),
         (re.compile(r' \d+ \d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d\.\d+'),
          ' IIIII DDDD-MM-DD HH:MM:SS.SS'),
         ])
@@ -155,11 +194,8 @@
         manuel.testing.TestSuite(
             manuel.doctest.Manuel(checker=checker) + manuel.capture.Manuel(),
             'README.txt',
-            setUp=readmesetup, tearDown=readmeteardown,
+            setUp=setup, tearDown=teardown,
             ),
-        doctest.DocTestSuite(
-            setUp=zc.zk.testing.setUp, tearDown=zc.zk.testing.tearDown,
-            checker=checker,
-            ),
+        doctest.DocTestSuite(setUp=setup, tearDown=teardown, checker=checker),
         ))
     return suite



More information about the checkins mailing list