[Checkins] SVN: zc.z3monitor/trunk/s Commands are now provided as utilities. This means that z3monitor

Jim Fulton jim at zope.com
Wed Nov 28 20:15:39 EST 2007


Log message for revision 82024:
  Commands are now provided as utilities.  This means that z3monitor
  commands can be added by simply implementing simple utilities.
  
  Added a working help command.
  
  Added ZEO connection status to the zeocache output.
  

Changed:
  U   zc.z3monitor/trunk/setup.py
  U   zc.z3monitor/trunk/src/zc/z3monitor/CHANGES.txt
  U   zc.z3monitor/trunk/src/zc/z3monitor/README.txt
  U   zc.z3monitor/trunk/src/zc/z3monitor/__init__.py
  U   zc.z3monitor/trunk/src/zc/z3monitor/configure.zcml
  A   zc.z3monitor/trunk/src/zc/z3monitor/interfaces.py
  A   zc.z3monitor/trunk/src/zc/z3monitor/sleeptest.zcml
  U   zc.z3monitor/trunk/src/zc/z3monitor/tests.py

-=-
Modified: zc.z3monitor/trunk/setup.py
===================================================================
--- zc.z3monitor/trunk/setup.py	2007-11-28 22:09:15 UTC (rev 82023)
+++ zc.z3monitor/trunk/setup.py	2007-11-29 01:15:37 UTC (rev 82024)
@@ -3,7 +3,7 @@
 name = 'zc.z3monitor'
 setup(
     name = name,
-    version = '0.3.0dev',
+    version = '0.3.0',
     author = 'Jim Fulton',
     author_email = 'jim at zope.com',
     description = 'Zope 3 Monitor',

Modified: zc.z3monitor/trunk/src/zc/z3monitor/CHANGES.txt
===================================================================
--- zc.z3monitor/trunk/src/zc/z3monitor/CHANGES.txt	2007-11-28 22:09:15 UTC (rev 82023)
+++ zc.z3monitor/trunk/src/zc/z3monitor/CHANGES.txt	2007-11-29 01:15:37 UTC (rev 82024)
@@ -1,6 +1,19 @@
 Change History
 **************
 
+0.3.0 (2007-11-29)
+==================
+
+New Features
+------------
+
+Commands are now provided as utilities.  This means that z3monitor
+commands can be added by simply implementing simple utilities.
+
+Added a working help command.
+
+Added ZEO connection status to the zeocache output.
+
 0.2.0 (2007-11-15)
 ==================
 

Modified: zc.z3monitor/trunk/src/zc/z3monitor/README.txt
===================================================================
--- zc.z3monitor/trunk/src/zc/z3monitor/README.txt	2007-11-28 22:09:15 UTC (rev 82023)
+++ zc.z3monitor/trunk/src/zc/z3monitor/README.txt	2007-11-29 01:15:37 UTC (rev 82024)
@@ -12,7 +12,84 @@
     >>> connection = zc.ngi.testing.TextConnection()
     >>> server = zc.z3monitor.Server(connection)
 
-It accesses databases as utilities.  Let's create some test databases
+The server supports an extensible set of commands.  It looks up
+commands as named zc.z3monitor.interfaces.IZ3MonitorPlugin plugins.
+
+To see this, we'll create a hello plugin:
+
+    >>> def hello(connection, name='world'):
+    ...     """Say hello
+    ...     
+    ...     Provide a name if you're not the world.
+    ...     """
+    ...     connection.write("Hi %s, nice to meet ya!\n" % name) 
+
+and register it:
+
+    >>> import zope.component, zc.z3monitor.interfaces
+    >>> zope.component.provideUtility(
+    ...   hello, zc.z3monitor.interfaces.IZ3MonitorPlugin, 'hello')
+
+Now we can give the hello command to the server:
+
+    >>> connection.test_input('hello\n')
+    Hi world, nice to meet ya!
+    -> CLOSE
+
+We can pass a name:
+
+    >>> connection.test_input('hello Jim\n')
+    Hi Jim, nice to meet ya!
+    -> CLOSE
+
+The server comes with a number of userful commands.  Let's register
+them so we can see what they do:
+
+    >>> zope.component.provideUtility(zc.z3monitor.help,
+    ...     zc.z3monitor.interfaces.IZ3MonitorPlugin, 'help')
+    >>> zope.component.provideUtility(zc.z3monitor.monitor,
+    ...     zc.z3monitor.interfaces.IZ3MonitorPlugin, 'monitor')
+    >>> zope.component.provideUtility(zc.z3monitor.dbinfo,
+    ...     zc.z3monitor.interfaces.IZ3MonitorPlugin, 'dbinfo')
+    >>> zope.component.provideUtility(zc.z3monitor.zeocache,
+    ...     zc.z3monitor.interfaces.IZ3MonitorPlugin, 'zeocache')
+
+The first is the help command.  Giving help without input, gives a
+list of available commands:
+
+    >>> connection.test_input('help\n')
+    Supported commands:
+      dbinfo -- Get database statistics
+      hello -- Say hello
+      help -- Get help about server commands
+      monitor -- Get general process info
+      zeocache -- Get ZEO client cache and status information
+    -> CLOSE
+
+We can get detailed help by specifying a command name:
+
+    >>> connection.test_input('help help\n')
+    Help for help:
+    <BLANKLINE>
+    Get help about server commands
+    <BLANKLINE>
+        By default, a list of commands and summaries is printed.  Provide
+        a command name to get detailed documentation for a command.
+    <BLANKLINE>
+    -> CLOSE
+
+    >>> connection.test_input('help hello\n')
+    Help for hello:
+    <BLANKLINE>
+    Say hello
+    <BLANKLINE>
+        Provide a name if you're not the world.
+    <BLANKLINE>
+    -> CLOSE
+
+
+The commands that come with the monitor use databnase information.  
+They access databases as utilities.  Let's create some test databases
 and register them as utilities.
 
     >>> from ZODB.tests.util import DB
@@ -35,22 +112,31 @@
 To get information about the process overall, use the monitor
 command:
 
+    >>> connection.test_input('help monitor\n')
+    Help for monitor:
+    <BLANKLINE>
+    Get general process info
+    <BLANKLINE>
+        The minimal output has:
+    <BLANKLINE>
+        - The number of open database connections to the main database, which
+          is the database registered without a name.
+        - The virual memory size, and
+        - The resident memory size.
+    <BLANKLINE>
+        If there are old database connections, they will be listed.  By
+        default, connections are considered old if they are greater than 100
+        seconds old. You can pass a minimum old connection age in seconds.
+        If you pass a value of 0, you'll see all connections.
+    <BLANKLINE>
+    -> CLOSE
+
     >>> connection.test_input('monitor\n')
     0 
     VmSize:	   35284 kB 
     VmRSS:	   28764 kB 
     -> CLOSE
 
-The minimal output has:
-
-- The number of open database connections to the main database, which
-  is the database registered without a name.
-- The virual memory size, and
-- The resident memory size.
-
-If there are old database connections, they will be listed.  By
-default, connections are considered old if they are greater than 100
-seconds old.  If you pass a value of 0, you'll see all connections.
 Let's create a couple of connections and then call z2monitor again
 with a value of 0:
 
@@ -65,7 +151,7 @@
     0.0    (0) 
     -> CLOSE
 
-The extra line of output gine connection debug info.
+The extra line of output gives connection debug info.
 If we set some additional input, we'll see it:
 
     >>> conn1.setDebugInfo('/foo')
@@ -85,9 +171,34 @@
 Database Information
 --------------------
 
-To get information about a database, give the dbinfo command followed
-by a database name:
+To get information about a database, use the dbinfo command:
 
+    >>> connection.test_input('help dbinfo\n')
+    Help for dbinfo:
+    <BLANKLINE>
+    Get database statistics
+    <BLANKLINE>
+        By default statistics are returned for the main database.  The
+        statistics are returned as a single line consisting of the:
+    <BLANKLINE>
+        - number of database loads
+    <BLANKLINE>
+        - number of database stores
+    <BLANKLINE>
+        - number of connections in the last five minutes
+    <BLANKLINE>
+        - number of objects in the object caches (combined)
+    <BLANKLINE>
+        - number of non-ghost objects in the object caches (combined)
+    <BLANKLINE>
+        You can pass a database name, where "-" is an alias for the main database.
+    <BLANKLINE>
+        By default, the statitics are for a sampling interval of 5
+        minutes.  You can request another sampling interval, up to an
+        hour, by passing a sampling interval in seconds after the database name.    
+    <BLANKLINE>
+    -> CLOSE
+
     >>> connection.test_input('dbinfo\n')
     0   0   2   0   0 
     -> CLOSE
@@ -106,18 +217,6 @@
     1   2   3   1   1 
     -> CLOSE
 
-The dbinfo command returns 5 values:
-
-- number of database loads
-
-- number of database stores
-
-- number of connections in the last five minutes
-
-- number of objects in the object caches (combined)
-
-- number of non-ghost objects in the object caches (combined)
-
 You can specify a database name.  So, to get statistics for the other
 database, we'll specify the name it was registered with:
 
@@ -147,26 +246,48 @@
 ZEO cache statistics
 --------------------
 
-You can also get ZEO cache statistics using the zeocache command.
+You can get ZEO cache statistics using the zeocache command.
 
+    >>> connection.test_input('help zeocache\n')
+    Help for zeocache:
+    <BLANKLINE>
+    Get ZEO client cache and status information
+    <BLANKLINE>
+        The commands returns data in a single line:
+    <BLANKLINE>
+        - the number of records added to the cache,
+    <BLANKLINE>
+        - the number of bytes added to the cache,
+    <BLANKLINE>
+        - the number of records evicted from the cache,
+    <BLANKLINE>
+        - the number of bytes evictes from the cache,
+    <BLANKLINE>
+        - the number of cache accesses.
+    <BLANKLINE>
+        - a flag indicating whether the ZEO storage is connected
+    <BLANKLINE>
+        By default, data for the main database are returned.  To return
+        information for another database, pass the database name.
+    <BLANKLINE>
+    -> CLOSE
+
     >>> connection.test_input('zeocache\n')
-    42 4200 23 2300 1000 
+    42 4200 23 2300 1000 1 
     -> CLOSE
 
-The output statistics are:
+You can specify a database name:
 
-- the number of records added to the cache,
+    >>> connection.test_input('zeocache other\n')
+    42 4200 23 2300 1000 1 
+    -> CLOSE
 
-- the number of bytes added to the cache,
+In this example, we're using a faux ZEO connection.  It has an
+attribute that determines whether it is connected or not.  Id we
+change it, then the zeocache output will change:
 
-- the number of records evicted from the cache,
+    >>> main._storage._is_connected = False
+    >>> connection.test_input('zeocache\n')
+    42 4200 23 2300 1000 0 
+    -> CLOSE
 
-- the number of bytes evictes from the cache,
-
-- the number of cache accesses.
-
-You can also specify a database name:
-
-    >>> connection.test_input('zeocache other\n')
-    42 4200 23 2300 1000 
-    -> CLOSE

Modified: zc.z3monitor/trunk/src/zc/z3monitor/__init__.py
===================================================================
--- zc.z3monitor/trunk/src/zc/z3monitor/__init__.py	2007-11-28 22:09:15 UTC (rev 82023)
+++ zc.z3monitor/trunk/src/zc/z3monitor/__init__.py	2007-11-29 01:15:37 UTC (rev 82024)
@@ -20,6 +20,7 @@
 import ZODB.interfaces
 
 import zope.component
+import zope.component.interfaces
 import zope.interface
 import zope.publisher.browser
 import zope.publisher.interfaces.browser
@@ -42,89 +43,165 @@
         args = data.strip().split()
         if not args:
             return
-        command = args.pop(0)
-        try:
-            command = getattr(self, 'command_'+command)
-        except AttributeError:
-            connection.write('invalid command %r\n' % command)
+        command_name = args.pop(0)
+        command = zope.component.queryUtility(
+            zc.z3monitor.interfaces.IZ3MonitorPlugin,
+            command_name)
+        if command is None:
+            connection.write('invalid command %r\n' % command_name)
         else:
             try:
                 command(connection, *args)
             except Exception, v:
-
                 traceback.print_exc(100, connection)
                 print >> connection, "%s: %s\n" % (v.__class__.__name__, v)
 
         connection.write(zc.ngi.END_OF_DATA)
 
-    def handle_close(self, connection, reason):
-        pass
 
-    def command_help(self, connection):
-        print >> connection, "Commands: help monitor dbinfo"
+def help(connection, command_name=None):
+    """Get help about server commands
 
-    opened_time_search = re.compile('[(](\d+[.]\d*)s[)]').search
-    def command_monitor(self, connection, long=100):
-        min = float(long)
-        db = zope.component.getUtility(ZODB.interfaces.IDatabase)
+    By default, a list of commands and summaries is printed.  Provide
+    a command name to get detailed documentation for a command.
+    """
+    if command_name is None:
+        connection.write(str(
+            "Supported commands:\n  "
+            + '\n  '.join(sorted(
+                "%s -- %s" % (name, (u.__doc__ or '?').split('\n', 1)[0])
+                for (name, u) in
+                zope.component.getUtilitiesFor(
+                    zc.z3monitor.interfaces.IZ3MonitorPlugin)))
+            + '\n'))
+    else:
+        command = zope.component.getUtility(
+            zc.z3monitor.interfaces.IZ3MonitorPlugin,
+            command_name)
+        connection.write("Help for %s:\n\n%s\n"
+                         % (command_name, command.__doc__)
+                         )
 
-        result = []
-        nconnections = 0
-        for data in db.connectionDebugInfo():
-            opened = data['opened']
-            if not opened:
-                continue
-            nconnections += 1
-            match = self.opened_time_search(opened)
-            # XXX the code originally didn't make sure a there was really a
-            # match; which caused exceptions in some (unknown) circumstances
-            # that we weren't able to reproduce; this hack keeps that from
-            # happening
-            if not match:
-                continue
-            age = float(match.group(1))
-            if age < min:
-                continue
-            result.append((age, data['info']))
+opened_time_search = re.compile('[(](\d+[.]\d*)s[)]').search
 
-        result.sort()
+def monitor(connection, long=100):
+    """Get general process info
 
-        print >>connection, str(nconnections)
-        for status in getStatus():
-            print >>connection, status
-        for age, info in result:
-            print >>connection, age, info.encode('utf-8')
+    The minimal output has:
 
-    def command_dbinfo(self, connection, database='', deltat=300):
-        if database == '-':
-            database = ''
-        db = zope.component.getUtility(ZODB.interfaces.IDatabase, database)
+    - The number of open database connections to the main database, which
+      is the database registered without a name.
+    - The virual memory size, and
+    - The resident memory size.
 
-        am = db.getActivityMonitor()
-        if am is None:
-            data = -1, -1, -1
-        else:
-            now = time.time()
-            analysis = am.getActivityAnalysis(now-int(deltat), now, 1)[0]
-            data = (analysis['loads'],
-                    analysis['stores'],
-                    analysis['connections'],
-                    )
+    If there are old database connections, they will be listed.  By
+    default, connections are considered old if they are greater than 100
+    seconds old. You can pass a minimum old connection age in seconds.
+    If you pass a value of 0, you'll see all connections.
+    """
 
-        ng = s = 0
-        for detail in db.cacheDetailSize():
-            ng += detail['ngsize']
-            s += detail['size']
+    min = float(long)
+    db = zope.component.getUtility(ZODB.interfaces.IDatabase)
 
-        print >> connection, data[0], data[1], data[2], s, ng
+    result = []
+    nconnections = 0
+    for data in db.connectionDebugInfo():
+        opened = data['opened']
+        if not opened:
+            continue
+        nconnections += 1
+        match = opened_time_search(opened)
+        # XXX the code originally didn't make sure a there was really a
+        # match; which caused exceptions in some (unknown) circumstances
+        # that we weren't able to reproduce; this hack keeps that from
+        # happening
+        if not match:
+            continue
+        age = float(match.group(1))
+        if age < min:
+            continue
+        result.append((age, data['info']))
 
-    def command_zeocache(self, connection, database=''):
-        db = zope.component.getUtility(ZODB.interfaces.IDatabase, database)
-        stats = db._storage._cache.fc.getStats()
-        print >> connection, ' '.join(map(str, stats))
-        
+    result.sort()
 
+    print >>connection, str(nconnections)
+    for status in getStatus():
+        print >>connection, status
+    for age, info in result:
+        print >>connection, age, info.encode('utf-8')
 
+def dbinfo(connection, database='', deltat=300):
+    """Get database statistics
+
+    By default statistics are returned for the main database.  The
+    statistics are returned as a single line consisting of the:
+
+    - number of database loads
+
+    - number of database stores
+
+    - number of connections in the last five minutes
+
+    - number of objects in the object caches (combined)
+
+    - number of non-ghost objects in the object caches (combined)
+
+    You can pass a database name, where "-" is an alias for the main database.
+
+    By default, the statitics are for a sampling interval of 5
+    minutes.  You can request another sampling interval, up to an
+    hour, by passing a sampling interval in seconds after the database name.    
+    """
+    
+    if database == '-':
+        database = ''
+    db = zope.component.getUtility(ZODB.interfaces.IDatabase, database)
+
+    am = db.getActivityMonitor()
+    if am is None:
+        data = -1, -1, -1
+    else:
+        now = time.time()
+        analysis = am.getActivityAnalysis(now-int(deltat), now, 1)[0]
+        data = (analysis['loads'],
+                analysis['stores'],
+                analysis['connections'],
+                )
+
+    ng = s = 0
+    for detail in db.cacheDetailSize():
+        ng += detail['ngsize']
+        s += detail['size']
+
+    print >> connection, data[0], data[1], data[2], s, ng
+
+def zeocache(connection, database=''):
+    """Get ZEO client cache and status information
+
+    The commands returns data in a single line:
+
+    - the number of records added to the cache,
+
+    - the number of bytes added to the cache,
+
+    - the number of records evicted from the cache,
+
+    - the number of bytes evictes from the cache,
+
+    - the number of cache accesses.
+
+    - a flag indicating whether the ZEO storage is connected
+
+    By default, data for the main database are returned.  To return
+    information for another database, pass the database name.
+    """
+    
+    db = zope.component.getUtility(ZODB.interfaces.IDatabase, database)
+    storage = db._storage
+    stats = list(storage._cache.fc.getStats())
+    stats.append(int(storage.is_connected()))
+    print >> connection, ' '.join(map(str, stats))
+
 @zope.component.adapter(zope.app.appsetup.interfaces.IDatabaseOpenedEvent)
 def initialize(opened_event):
     config = zope.app.appsetup.product.getProductConfiguration(__name__)

Modified: zc.z3monitor/trunk/src/zc/z3monitor/configure.zcml
===================================================================
--- zc.z3monitor/trunk/src/zc/z3monitor/configure.zcml	2007-11-28 22:09:15 UTC (rev 82023)
+++ zc.z3monitor/trunk/src/zc/z3monitor/configure.zcml	2007-11-29 01:15:37 UTC (rev 82024)
@@ -1,12 +1,12 @@
-<configure
-    xmlns="http://namespaces.zope.org/zope"
-    >
+<configure xmlns="http://namespaces.zope.org/zope">
   <subscriber handler=".initialize" />
   <subscriber handler=".save_request_in_connection_info" />
-  <adapter
-      name="sleeptest"
-      factory=".Test"
-      permission="zope.Public"
-      provides="zope.publisher.interfaces.browser.IBrowserPage"
-      />
+  <utility component=".help"
+           provides=".interfaces.IZ3MonitorPlugin" name="help" />
+  <utility component=".monitor"
+           provides=".interfaces.IZ3MonitorPlugin" name="monitor" />
+  <utility component=".dbinfo"
+           provides=".interfaces.IZ3MonitorPlugin" name="dbinfo" />
+  <utility component=".zeocache"
+           provides=".interfaces.IZ3MonitorPlugin" name="zeocache" />
 </configure>

Added: zc.z3monitor/trunk/src/zc/z3monitor/interfaces.py
===================================================================
--- zc.z3monitor/trunk/src/zc/z3monitor/interfaces.py	                        (rev 0)
+++ zc.z3monitor/trunk/src/zc/z3monitor/interfaces.py	2007-11-29 01:15:37 UTC (rev 82024)
@@ -0,0 +1,25 @@
+##############################################################################
+#
+# Copyright (c) 2005 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (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.
+#
+##############################################################################
+
+import zope.interface
+
+class IZ3MonitorPlugin(zope.interface.Interface):
+
+    def __call__(output, *args):
+        """Compute some monitoring information
+
+        The output argument is a file-like object with write method.
+
+        A variable number of arguments are passed.
+        """


Property changes on: zc.z3monitor/trunk/src/zc/z3monitor/interfaces.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: zc.z3monitor/trunk/src/zc/z3monitor/sleeptest.zcml
===================================================================
--- zc.z3monitor/trunk/src/zc/z3monitor/sleeptest.zcml	                        (rev 0)
+++ zc.z3monitor/trunk/src/zc/z3monitor/sleeptest.zcml	2007-11-29 01:15:37 UTC (rev 82024)
@@ -0,0 +1,8 @@
+<configure xmlns="http://namespaces.zope.org/zope">
+  <adapter
+      name="sleeptest"
+      factory=".Test"
+      permission="zope.Public"
+      provides="zope.publisher.interfaces.browser.IBrowserPage"
+      />
+</configure>


Property changes on: zc.z3monitor/trunk/src/zc/z3monitor/sleeptest.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Modified: zc.z3monitor/trunk/src/zc/z3monitor/tests.py
===================================================================
--- zc.z3monitor/trunk/src/zc/z3monitor/tests.py	2007-11-28 22:09:15 UTC (rev 82023)
+++ zc.z3monitor/trunk/src/zc/z3monitor/tests.py	2007-11-29 01:15:37 UTC (rev 82024)
@@ -18,6 +18,7 @@
 
 from zope.testing import doctest, renormalizing
 
+
 class FauxCache:
 
     @property
@@ -27,7 +28,12 @@
     def getStats(self):
         return 42, 4200, 23, 2300, 1000
 
+def is_connected(self):
+    return self._is_connected
+
 ZODB.MappingStorage.MappingStorage._cache = FauxCache()
+ZODB.MappingStorage.MappingStorage._is_connected = True
+ZODB.MappingStorage.MappingStorage.is_connected = is_connected
 
 def test_suite():
     return unittest.TestSuite((
@@ -35,7 +41,6 @@
             'README.txt',
             checker=renormalizing.RENormalizing([
                 (re.compile("Vm(Size|RSS):\s+\d+\s+kB"), 'Vm\\1 NNN kB'),
-                (re.compile("\d\d+\s+\d\d+\s+\d+"), 'RRR WWW C'),
                 (re.compile("\d+[.]\d+ seconds"), 'N.NNNNNN seconds'),
                 ]),
             ),



More information about the Checkins mailing list