[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