Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Separate ZODB connection information into new ZODB Connections view #1178

Merged
merged 9 commits into from
Oct 26, 2023
6 changes: 6 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ https://github.com/zopefoundation/Zope/blob/4.x/CHANGES.rst
5.8.7 (unreleased)
------------------

- Separate ZODB connection information into new ZODB Connections view.

- Move the cache detail links to the individual database pages.

- Fix the auto refresh functionality on the Reference Count page

- Update the Ace editor in the ZMI.

- Restrict access to static ZMI resources.
Expand Down
31 changes: 19 additions & 12 deletions src/App/ApplicationManager.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from App.special_dtml import DTMLFile
from App.Undo import UndoSupport
from App.version_txt import version_txt
from App.ZODBConnectionDebugger import ZODBConnectionDebugger
from DateTime.DateTime import DateTime
from OFS.Traversable import Traversable
from Persistence import Persistent
Expand Down Expand Up @@ -63,7 +64,9 @@ class DatabaseChooser(Tabs, Traversable, Implicit):
{'label': 'Databases', 'action': 'manage_main'},
{'label': 'Configuration', 'action': '../Configuration/manage_main'},
{'label': 'DAV Locks', 'action': '../DavLocks/manage_main'},
{'label': 'Debug Information', 'action': '../DebugInfo/manage_main'},
{'label': 'Reference Counts', 'action': '../DebugInfo/manage_main'},
{'label': 'ZODB Connections',
'action': '../ZODBConnections/manage_main'},
)
MANAGE_TABS_NO_BANNER = True

Expand Down Expand Up @@ -108,7 +111,9 @@ class ConfigurationViewer(Tabs, Traversable, Implicit):
{'label': 'Databases', 'action': '../Database/manage_main'},
{'label': 'Configuration', 'action': 'manage_main'},
{'label': 'DAV Locks', 'action': '../DavLocks/manage_main'},
{'label': 'Debug Information', 'action': '../DebugInfo/manage_main'},
{'label': 'Reference Counts', 'action': '../DebugInfo/manage_main'},
{'label': 'ZODB Connections',
'action': '../ZODBConnections/manage_main'},
)
MANAGE_TABS_NO_BANNER = True

Expand Down Expand Up @@ -150,7 +155,7 @@ class DebugManager(Tabs, Traversable, Implicit):
manage = manage_main = manage_workspace = DTMLFile('dtml/debug', globals())
manage_main._setName('manage_main')
id = 'DebugInfo'
name = title = 'Debug Information'
name = title = 'Reference Counts'
meta_type = name
zmi_icon = 'fas fa-bug'

Expand All @@ -159,7 +164,9 @@ class DebugManager(Tabs, Traversable, Implicit):
{'label': 'Databases', 'action': '../Database/manage_main'},
{'label': 'Configuration', 'action': '../Configuration/manage_main'},
{'label': 'DAV Locks', 'action': '../DavLocks/manage_main'},
{'label': 'Debug Information', 'action': 'manage_main'},
{'label': 'Reference Counts', 'action': 'manage_main'},
{'label': 'ZODB Connections',
'action': '../ZODBConnections/manage_main'},
)

def refcount(self, n=None, t=(type(Implicit), type(object))):
Expand Down Expand Up @@ -224,19 +231,14 @@ def rcdeltas(self):
'rc': n[1][0],
} for n in rd]

def dbconnections(self):
import Zope2 # for data
return Zope2.DB.connectionDebugInfo()

def manage_getSysPath(self):
return list(sys.path)


InitializeClass(DebugManager)


class ApplicationManager(CacheManager,
Persistent,
class ApplicationManager(Persistent,
Tabs,
Traversable,
Implicit):
Expand All @@ -255,6 +257,7 @@ class ApplicationManager(CacheManager,
Configuration = ConfigurationViewer()
DavLocks = DavLockManager()
DebugInfo = DebugManager()
ZODBConnections = ZODBConnectionDebugger()

manage = manage_main = DTMLFile('dtml/cpContents', globals())
manage_main._setName('manage_main')
Expand All @@ -263,7 +266,8 @@ class ApplicationManager(CacheManager,
{'label': 'Databases', 'action': 'Database/manage_main'},
{'label': 'Configuration', 'action': 'Configuration/manage_main'},
{'label': 'DAV Locks', 'action': 'DavLocks/manage_main'},
{'label': 'Debug Information', 'action': 'DebugInfo/manage_main'},
{'label': 'Reference Counts', 'action': 'DebugInfo/manage_main'},
{'label': 'ZODB Connections', 'action': 'ZODBConnections/manage_main'},
)
MANAGE_TABS_NO_BANNER = True

Expand Down Expand Up @@ -311,7 +315,7 @@ def getCLIENT_HOME(self):
return getConfiguration().clienthome


class AltDatabaseManager(Traversable, UndoSupport):
class AltDatabaseManager(CacheManager, Traversable, UndoSupport):
""" Database management DBTab-style
"""
id = 'DatabaseManagement'
Expand All @@ -333,6 +337,9 @@ def _getDB(self):
def cache_length(self):
return self._getDB().cacheSize()

def cache_active_and_inactive_count(self):
return sum([x['size'] for x in self._getDB().cacheDetailSize()])

def cache_length_bytes(self):
return self._getDB().getCacheSizeBytes()

Expand Down
4 changes: 3 additions & 1 deletion src/App/DavLockManager.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ class DavLockManager(Item, Implicit):
{'label': 'Databases', 'action': '../Database/manage_main'},
{'label': 'Configuration', 'action': '../Configuration/manage_main'},
{'label': 'DAV Locks', 'action': 'manage_main'},
{'label': 'Debug Information', 'action': '../DebugInfo/manage_main'},
{'label': 'Reference Counts', 'action': '../DebugInfo/manage_main'},
{'label': 'ZODB Connections',
'action': '../ZODBConnections/manage_main'},
)

@security.protected(webdav_manage_locks)
Expand Down
90 changes: 90 additions & 0 deletions src/App/ZODBConnectionDebugger.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
##############################################################################
#
# Copyright (c) 2023 Zope Foundation and Contributors.
#
# 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 pprint
import time
from operator import itemgetter

from AccessControl.class_init import InitializeClass
from AccessControl.SecurityInfo import ClassSecurityInfo
from Acquisition import Implicit
from App.special_dtml import DTMLFile
from OFS.SimpleItem import Item


class ZODBConnectionDebugger(Item, Implicit):
id = 'ZODBConnectionDebugger'
name = title = 'ZODB Connections'
meta_type = 'ZODB Connection Debugger'
zmi_icon = 'fas fa-bug'

security = ClassSecurityInfo()

manage_zodb_conns = manage_main = manage = manage_workspace = DTMLFile(
'dtml/zodbConnections', globals())
manage_zodb_conns._setName('manage_zodb_conns')
manage_options = (
{'label': 'Control Panel', 'action': '../manage_main'},
{'label': 'Databases', 'action': '../Database/manage_main'},
{'label': 'Configuration', 'action': '../Configuration/manage_main'},
{'label': 'DAV Locks', 'action': '../DavLocks/manage_main'},
{'label': 'Reference Counts', 'action': '../DebugInfo/manage_main'},
{'label': 'ZODB Connections', 'action': 'manage_main'},
)

def dbconnections(self):
import Zope2 # for data

result = []
now = time.time()

def get_info(connection):
# `result`, `time` and `before` are lexically inherited.
request_info = {}
request_info_formatted = ''
debug_info_formatted = ''
opened = connection.opened
debug_info = connection.getDebugInfo() or {}

if debug_info:
debug_info_formatted = pprint.pformat(debug_info)
if len(debug_info) == 2:
request_info = debug_info[0]
request_info.update(debug_info[1])
request_info_formatted = pprint.pformat(request_info)

if opened is not None:
# output UTC time with the standard Z time zone indicator
open_since = "{}".format(
time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime(opened)))
open_for = "{:.2f}s".format(now - opened)
else:
open_since = '(closed)'
open_for = ''

result.append({
'open_since': open_since,
'open_for': open_for,
'info': debug_info,
'info_formatted': debug_info_formatted,
'request_info': request_info,
'request_formatted': request_info_formatted,
'before': connection.before,
'cache_size': len(connection._cache),
})

Zope2.DB._connectionMap(get_info)
return sorted(result, key=itemgetter('open_since'))


InitializeClass(ZODBConnectionDebugger)
13 changes: 12 additions & 1 deletion src/App/dtml/dbMain.dtml
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,22 @@
<tr>
<th>Total</th>
<th>&dtml-cache_length;</th>
<th>&nbsp;</th>
<th>&dtml-cache_active_and_inactive_count;</th>
</tr>
</tbody>
</table>

<p class="form-help">
<a href="cache_detail" class="mr-3" target="blank">
<i class="fas fa-microchip" title="Cache detail"></i>
Cache detail
</a>
<a href="cache_extreme_detail" target="blank">
<i class="fas fa-microchip" title="Cache extreme detail"></i>
Cache extreme detail
</a>
</p>

<div class="zmi-controls mb-5">
<form action="&dtml-URL1;/manage_minimize" method="post">
<input class="btn btn-primary" id="minimize" type="submit"
Expand Down
63 changes: 21 additions & 42 deletions src/App/dtml/debug.dtml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<meta HTTP-EQUIV="Refresh"
CONTENT="&dtml-debug_auto_reload;;URL=&dtml-URL;?debug_auto_reload=&dtml-debug_auto_reload;">
</dtml-if>
<dtml-with "_(management_view='Debug Information')">
<dtml-with "_(management_view='Reference Counts')">
<dtml-var manage_tabs>
</dtml-with>

Expand All @@ -14,11 +14,11 @@
<main class="container-fluid">

<p class="form-help mt-4">
Reference count and database connection information
Python reference count information
</p>

<h3>Top 100 reference counts</h3>
<select name="foo" size="10" class="form-control">
<select name="foo" size="10" class="form-control my-3">
<dtml-in "refcount(100)">
<option>&dtml-sequence-item;: &dtml-sequence-key;</option>
</dtml-in>
Expand All @@ -28,14 +28,14 @@
<dtml-if delta_info>
<h3 class="mt-4">Changes since last refresh</h3>

<table class="table table-bordered table-sm mb-2">
<table class="table table-sm table-striped my-3">
<dtml-in rcdeltas mapping>
<dtml-if sequence-start>
<tr>
<th>Class</th>
<th><dtml-var rcdate fmt="%Y/%m/%d - %H:%M:%S" null=""></th>
<th><dtml-var ZopeTime fmt="%Y/%m/%d - %H:%M:%S"></th>
<th>Delta</th>
<th class="text-muted">Class</th>
<th class="text-muted"><dtml-var rcdate fmt="%Y/%m/%d - %H:%M:%S" null=""></th>
<th class="text-muted"><dtml-var ZopeTime fmt="%Y/%m/%d - %H:%M:%S"></th>
<th class="text-muted">Delta</th>
</tr>
</dtml-if>
<tr>
Expand All @@ -49,51 +49,30 @@
</dtml-if>
</dtml-let>

<p class="form-help">
<a href="../Database/cache_detail" class="mr-3" target="blank">
<i class="fas fa-microchip" title="Cache detail"></i>
Cache detail
</a>
<a href="../Database/cache_extreme_detail" target="blank">
<i class="fas fa-microchip" title="Cache extreme detail"></i>
Cache extreme detail
</a>
</p>

<div class="zmi-controls row border-top border-bottom p-3 bg-light">
<form action="&dtml-URL;" method="GET" class="form-group form-inline p-0 m-0">
<button class="btn btn-primary mr-3"
onclick="window.location.href='&dtml-URL;?update_snapshot=1';"
>Update Snapshot</button>
<dtml-if debug_auto_reload>
<button class="btn btn-primary mr-3"
onclick="window.location.href='&dtml-URL;';"
>Stop auto refresh</button>
<button class="btn btn-primary mr-3"
onclick="window.location.href='&dtml-URL;';"
>Stop auto refresh</button>
</form>
<dtml-else>
<button class="btn btn-primary mr-3"
onclick="window.location.href='&dtml-URL;';"
>Refresh</button>
<label for="debug_auto_reload" class="mx-3">Auto refresh interval (seconds):</label>
<input class="form-control mr-3" type="text" id="debug_auto_reload" name="debug_auto_reload" size="3" value="10" />
<input class="btn btn-primary" type="submit" value="Start auto refresh" />
<button class="btn btn-primary mr-3"
onclick="window.location.href='&dtml-URL;';"
>Refresh</button>
</form>
<form action="&dtml-URL;" method="GET" class="form-group form-inline p-0 m-0">
<label for="debug_auto_reload" class="mx-3">Auto refresh interval (seconds):</label>
<input class="form-control mr-3" type="text" id="debug_auto_reload" name="debug_auto_reload" size="3" value="10" />
<input class="btn btn-primary" type="submit" value="Start auto refresh" />
</form>
</dtml-if>
</form>
</div>

<h3>ZODB database connections</h3>
<table class="table table-sm table-bordered mb-5">
<tr>
<th>Opened</th>
<th>Info</th>
</tr>
<dtml-in dbconnections mapping>
<tr>
<td class="text-nowrap">&dtml-opened;</td>
<td><samp>&dtml-info;</samp></td>
</tr>
</dtml-in>
</table>

</main>

<dtml-var manage_page_footer>
Loading
Loading