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
  • Loading branch information
dataflake committed Oct 26, 2023
1 parent 29e088e commit 6e8de12
Show file tree
Hide file tree
Showing 7 changed files with 178 additions and 26 deletions.
2 changes: 2 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ https://github.com/zopefoundation/Zope/blob/4.x/CHANGES.rst
5.8.7 (unreleased)
------------------

- Separate ZODB connection information into new ZODB Connections view

- Update the Ace editor in the ZMI.

- Restrict access to static ZMI resources.
Expand Down
23 changes: 14 additions & 9 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,10 +231,6 @@ 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)

Expand Down Expand Up @@ -255,6 +258,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 +267,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
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:
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 = ''

# output UTC time with the standard Z time zone indicator
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)
18 changes: 2 additions & 16 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,7 +14,7 @@
<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>
Expand Down Expand Up @@ -80,20 +80,6 @@
</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>
55 changes: 55 additions & 0 deletions src/App/dtml/zodbConnections.dtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<dtml-var manage_page_header>
<dtml-if debug_auto_reload>
<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='ZODB Connections')">
<dtml-var manage_tabs>
</dtml-with>

<main class="container-fluid">

<p class="form-help mt-4">
This page shows ZODB database connections and their current state. The
Duration column tells you how long an open database connection has been
held by the request shown in the Info column next to it. You can use this
view to diagnose long-running requests.
</p>

<h3>ZODB database connections</h3>
<table class="table-sm table-bordered mb-5">
<tr>
<th>Opened at</th>
<th>Duration</th>
<th>Info</th>
</tr>
<dtml-in dbconnections mapping>
<tr>
<td class="text-nowrap" valign="top">&dtml-open_since;</td>
<td class="text-nowrap" valign="top"><dtml-var "open_for or '-'"></td>
<td>
<dtml-if open_for>
<details>
<dtml-if request_info>
<summary>
<b><dtml-var "request_info['REQUEST_METHOD']"> <dtml-var "request_info['REQUEST_URI']"></b>
</summary>
<pre><dtml-var request_formatted html_quote></pre>
<dtml-else>
<summary>
No request information available
</summary>
<pre><dtml-var info_formatted html_quote></pre>
</dtml-if>
</details>
<dtml-else>
-
</dtml-if>
</td>
</tr>
</dtml-in>
</table>

</main>

<dtml-var manage_page_footer>
12 changes: 12 additions & 0 deletions src/zmi/styles/resources/zmi_base.css
Original file line number Diff line number Diff line change
Expand Up @@ -969,6 +969,18 @@ header.navbar select.form-control-sm,
box-sizing: border-box;
}

.zmi-connection_info {
display: none;
}

#request_uri {
margin-bottom: 0;
}

#conn_info {
margin-top: 1rem;
}

/* EXAMPLE: IMPLANT YOUR LOGO */
/*
.zmi-manage_menu {
Expand Down

0 comments on commit 6e8de12

Please sign in to comment.