Skip to content

Commit

Permalink
Separate ZODB connection information into new ZODB Connections view (#…
Browse files Browse the repository at this point in the history
…1178)

* - Separate ZODB connection information into new ZODB Connections view

* - move comment and remove auto-refresh

* table facelift

* - fall back to PATH_INFO if REQUEST_URI is missing or empty

* - Move the cache detail links to the individual database pages.
- Fix the auto refresh functionality on the Reference Count page.

* DebugInfo: monotonized table style

* - fix cache_detail pages to show data for the selected database only

* set table-spacing back to standard

Ref: #1178 (comment)

---------

Co-authored-by: drfho <dr.frank.hoffmann@web.de>
  • Loading branch information
dataflake and drfho committed Oct 26, 2023
1 parent 29e088e commit 8fdd567
Show file tree
Hide file tree
Showing 8 changed files with 214 additions and 56 deletions.
6 changes: 6 additions & 0 deletions CHANGES.rst
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
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
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
@@ -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
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
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>

0 comments on commit 8fdd567

Please sign in to comment.