iris-history-monitor 重新设计:面向 IRIS 2026.1 的新界面和 REST 后端
2019 年,我编写了iris-history-monitor,旨在以可视化方式展示系统监视器(System Monitor)记录的关于 InterSystems IRIS 实例的信息,并用更易于阅读的界面取代管理门户(Management Portal)中的仪表盘和进程列表。
虽然它曾有效运行,但已显陈旧。该工具基于一个大型网格库构建,由一组 CSP 页面组成,将每次请求的数据保存在共享全局变量中;在当前的 IRIS 环境中,其“历史”屏幕会显示为空白,却未说明原因。 本文将介绍此次重新设计:您现在所看到的界面,以及为使后端能在 IRIS 2026.1 上运行而进行的底层改动。
在 2026 年 10 月 13 日发布的 2.2.0 版本中,我还添加了可选的浏览器通知功能。现在,当您在其他界面工作时,只要相关标签页保持打开状态,监控系统就能在指标恶化时及时提醒您。

有哪些变化
- 全新的三屏界面,包括“概览”、“历史”和“进程**”,这些界面以静态文件(React、TypeScript、Vite)的形式构建,由 IRIS 通过其自身的 Web 应用程序提供服务。**服务器端无需构建步骤,也无需运行其他 Web 服务器。
- **一个带有契约的 REST API。**该接口调用一个带版本号的 API(
/historymonitor/api/v1),其规范由代码库中的 OpenAPI 文档描述。错误代码为application/problem+json。 - **统一登录。**页面和 API 共享一个 IRIS 会话。用户拥有一个角色
HistoryMonitorViewer,该角色仅允许其查看监控信息,无其他权限。 - **旧页面已移除。**旧地址会重定向到对应的新页面,因此书签仍可正常使用。
- **当历史记录未被记录时会进行提示。**在标准的 IRIS 安装环境中,系统监控器不会记录历史记录。历史记录页面以前是空的;现在它会明确提示这一点,并向管理员说明应采取的操作。
- **当指标恶化时触发浏览器通知。**2.2.0 版本在页眉中新增了可选控制项,将同一检查中的变化合并为一条通知,并抑制来自同一浏览器源的不同标签页中的重复通知。该功能使用现有 API,无需服务器端变更。
- 可通过 IPM 或 Docker 安装。
zpm "install iris-history-monitor",或使用已发布的镜像。 - **通过持续集成(CI)确保质量。**每个拉取请求都会运行 ObjectScript 测试、在干净实例上进行 IPM 安装、从上一版本升级、界面测试、浏览器端到端测试以及 Docker 构建。
变更前后
这是 2019 年的系统仪表盘和今天的概览。旧版会为每个指标绘制一张图表,并用文字和绿色勾号显示状态。新版将需要关注的内容置于首位,并以图标、文字和颜色三种形式显示每个状态,绝不会仅用颜色表示。
| 2019 | 现在 |
|---|---|
|  |  |
|  |  |
界面
概述
仪表盘中的每项指标及其状态:正常、警告、严重或不可用。需要关注的指标会优先显示,页面每10秒自动刷新一次。**点击“暂停”**按钮可停止刷新,当状态发生变化时,屏幕阅读器会收到提示,而非在每次刷新时都进行提示。
历史记录
许可证使用情况、CSP 会话和数据库大小,可按每 5 分钟、每小时或每天为周期进行统计,以支持缩放的图表和表格形式呈现,并支持导出为 CSV 文件。历史数据表以 UTC 时间为基准;屏幕上显示的时间将转换为您所在时区的当地时间。
有两项新功能。在“粒度”下会显示实例保留该粒度数据的时间长度(“保留:最近 60 天”),当记录停止时,系统会明确标注,并显示最后一次采样的时间:

进程
该进程列表支持筛选(命名空间、用户、状态、文本)、排序、分页,并提供单个进程的详细信息对话框以及CSV导出功能。
浏览器通知
此前曾有用户请求添加浏览器通知功能(#6)。我在 2.2.0 版本中根据规范 005 实现了该功能。其目的是让用户无需一直盯着“概览”页面,也能及时发现问题。
在页眉处,点击**“通知”(小铃铛图标),然后选择“开启”**。只有在尚未授予权限的情况下,该操作才会向浏览器请求权限。该设置将保存在当前浏览器中。对话框中还会说明,当网址不安全、权限被阻止或浏览器不支持通知时的情况。
规则设计得刻意简洁:
- **状态恶化时通知:**从“正常”变为“警告”、从“正常”变为“危急”,或从“警告”变为“危急”。
- **分组变化:**单次检查中多个指标同时恶化将触发一条通知。点击该通知会将监控标签页置于最前,并打开“概览”页面。
- **避免干扰:**首次读数、新指标、状态未变或正在改善,以及涉及“不可用”状态的过渡情况均不触发通知。此外,当“概览”页面可见时也不会触发通知;因为相关变化已在该页面显示并提示。
在其下方,一个安装在界面外壳中的监视器会在通知开启且被允许的情况下,每 30 秒读取现有的 GET /overview 数据,包括在“历史记录”或“进程”页面上,以及标签页被隐藏时。它拥有自己的查询缓存,因此“概览”页面的**“暂停”**按钮仍不会改变其数值。 浏览器可能会减慢后台定时器的运行速度;README 文件中描述了隐藏标签页的检查频率约为每分钟一次,而非承诺确切的执行时间。
为避免同一来源的不同标签页之间出现重复数据,我将每个指标的最后一次通知状态和时间存储在 localStorage 中。若同一指标在 10 分钟内出现重复的状态,则该数据将被忽略。此机制依赖于浏览器共享存储的可用性;它并非跨浏览器或跨计算机的协调机制。
存在实际限制。通知需要浏览器权限、支持该功能的浏览器、HTTPS 或 localhost,以及一个打开的监控标签页。浏览器关闭后,不会有服务继续运行。 若八小时内无任何交互,后台通知检查将停止,并会通过一条通知说明暂停原因,因此这些检查不会让被遗忘标签页的 IRIS 会话和许可单元持续占用资源。按键、点击鼠标或将标签页恢复到前台均可恢复检查;首次读取将建立新的基准值。
我添加此功能是为了满足那些需要保持监控窗口打开的用户。对于无人值守的警报,请使用实例的“系统监控”电子邮件警报(^%SYSMONMGR),或由警报管理器读取的 SAM 指标。浏览器通知不能替代独立于浏览器运行的监控。README文件中也描述了这些限制。
语言、主题和小屏幕
界面支持英语、葡萄牙语(巴西)和西班牙语,可选择浅色、深色或系统主题,并且可在手机大小的屏幕上正常运行。
| 葡萄牙语,深色主题 | 在手机上 |
|---|---|
|  |  |
在 2.2.0 版本中,首页加载了 135.3 KB 的 JavaScript(gzip 压缩后),较通知功能上线前(约 133 KB)的 132.8 KB 有所增加。此数据统计的是入口脚本及其预加载的 JavaScript 模块,不包括整个页面或单独加载的“历史记录”图表。 它所取代的页面包含 1,400 个静态文件,总计 103 MB,其中仅网格库(DevExtreme 18.2.3)就占了 64 MB。
IRIS 2026.1 的后端
采用 REST API 代替页面
旧版页面是 %CSP.Page 类,它们会根据请求选择要执行的方法,并将每次请求的结果写入 ^IRISMonitor——这是一个所有用户共用的全局变量。两个用户可能会互相覆盖对方的数据。
现在,该 API 是一个带有显式 URL 映射的 %CSP.REST 类,其下方有一个小型服务层:
| Route | 返回内容 |
|---|---|
| `GET /overview` | 仪表盘指标的当前快照,每项指标均带有状态。 |
| `GET /history/{metric}` | `license`、`csp-sessions` 或 `database-size` 的系列数据,包含 `granularity` (`5min`、`hourly`、`daily`) 以及 `from`/`to`。包含覆盖范围以及该时间段是否为部分时间段。 |
| `GET /processes` | 一个包含流程的页面,带有筛选和排序功能。 |
| `GET /history-collection` | 是否正在记录历史数据、最近一次采样以及各粒度级别的保留时长。 |
首先在openapi.yaml 中编写契约,每条路由都配有测试,当响应偏离契约时测试会失败。SQL 使用绑定参数。任何请求值均不会被执行或拼接进 SQL 语句中,服务器也不会调用请求中获取的任何主机。 不会向每个请求的全局变量中写入任何内容。
角色与单点登录
安装该模块会创建资源 HistoryMonitorRead 和角色 HistoryMonitorViewer。该角色可以使用该资源,读取 %DB_IRISSYS (针对 SYS.Stats.Dashboard)以及 SELECT 这八个 SYS.History 表。仅拥有该角色的用户可以使用监控功能,但无法执行其他任何操作。
该角色的验证在 OnPreDispatch 中进行,该处会以 problem+json 为由返回 403 状态码。 Web 应用程序本身故意不包含任何资源:若包含资源,CSP 会在类运行之前就拒绝没有该角色的用户,并返回 401 HTML 页面而非错误响应。
界面和API共享Cookie路径 /historymonitor/,因此一次登录即可同时访问两者。 静态文件不会开启 CSP 会话,因此位于 /historymonitor/ 处的一个小型登录类会引导用户通过 IRIS 登录表单,并返回其请求的页面。
使用 IPM 进行打包
module.xml 将 Web 应用程序声明为 ,并为界面文件指定了 ,因为 IPM 已弃用 ,并明确指定了 AutheEnabled(32,仅限密码)。 在安装、升级和卸载时,会清除 1.x 版本留下的残留内容:旧类、已编译的页面、页面文件夹以及 ^IRISMonitor。它绝不会删除命名空间或数据库。
我在 IRIS 2026.1 上遇到的问题
以下这些情况曾让我耗费了不少时间,希望对您有所帮助,能让您少花些功夫。
DispatchRequest**在%CSP.REST中是最终值 。**重写时因ERROR #5272: Cannot change final 'Method'报错。负责转发旧地址的类在OnPreDispatch中执行操作,并设置了pContinue = 0。- **一个未经身份验证的 Web 应用程序需要一个数据库角色才能运行你的类。**如果没有角色,在类运行之前,请求就会收到 403 错误。转发应用程序匹配
:%DB_,该规则仅适用于该应用程序。 - 历史记录表以 UTC 为键。
ZDATE和ZTIME均为 UTC 时间。使用$ZDateTime(..., 7)对该值进行格式化会将其从本地时间再次转换为 UTC 时间。 API 将键转换为 ISO 8601 格式的 UTC 时间,而浏览器则将其转换为本地时间。测试中包含一个关于夏令时变更的测试用例。 - **在
%SYS.ProcessQuery上的 SQL 查询未向查看者显示收集器进程。**进程服务会将^$JOB的每个进程作为%SYS.ProcessQuery对象打开,这种方式对查看者角色有效。管理门户中与此相关的页面需要%Admin_Manage。 - **
intersystemsdc镜像的入口脚本。**在我的测试中,其启动后步骤失败并导致 IRIS 停止运行。Dockerfile 和 CI 会直接使用/tini -- /iris-main启动 IRIS。 - **一个 YAML 细节。**OpenAPI 文档中未加引号的
off在生成的 JSON 中变成了布尔值false,而我的枚举[recording, stale, off]拒绝了该值。 测试现在会针对每个状态与合约进行比对。 - 新实例起初报告的数据很少。
SYS.Stats.Dashboard.Sample()仅在系统监控器完成首个周期后(即启动约五分钟后)才会返回一些值。在最初的几分钟内,部分指标显示为不可用(“未报告值”),这确实属实。
默认情况下不会记录历史数据
这一点让我感到意外。在标准的 IRIS 安装环境中,没有历史数据收集器处于活动状态:既没有 %Monitor.System.HistoryPerf 或 %Monitor.System.HistorySys,也没有 %MONAPP 进程。 “历史记录”屏幕为空。重启后,即使类处于活动状态,应用程序监视器也不会自动恢复。
问题不在于数据保留策略。该实例会将5分钟级详细数据保留7天,将每小时汇总数据保留60天,且从不清除每日汇总数据。因此,监控器本身不会保留任何副本。它仅报告当前状态,而该模块也绝不会自动启用数据收集功能。这需要由管理员手动操作:
do ##class(%Monitor.Manager).Activate("%Monitor.System.HistoryPerf")
do ##class(%Monitor.Manager).Activate("%Monitor.System.HistorySys")
do ##class(%Monitor.Manager).StartApp()
并且,为了确保重启后能恢复运行,需在 %SYS 中的 %ZSTART 添加一条记录:
%ZSTART ; startup hooks
quit
SYSTEM ; runs when the instance starts
try { do ##class(%Monitor.Manager).StartApp() } catch {}
quit
在干净的 intersystemsdc/iris-community:2026.1 容器上,第一个样本在 308 秒后到达;重启后,新样本在 310 秒后到达。 该仓库的 Docker 镜像会自行执行相同的步骤;而安装该模块时则不会执行这些步骤。持续集成(CI)会比较安装前后七项收集器和保留设置,若其中任何一项发生变化,则测试失败。
旧地址与升级
2.0.0 版本移除了旧页面。对于所有 2.x 版本,这些页面的地址都会通过 HTTP 302 重定向到新界面:
| 旧网址 | 打开 |
|---|---|
| `/csp/irismonitor/dashboard.csp` | 概述 |
| `/csp/irismonitor/historylicense.csp`, `historycspsessions.csp`, `historydatabase.csp` | 该指标的历史数据 |
| `/csp/irismonitor/systemprocesses.csp` | 流程 |
| 其他内容见 `/csp/irismonitor/` | 概述 |
按常规方式覆盖安装 1.x 版本。此次升级将清除旧页面在模块命名空间中留下的内容。
由 CI 确保准确性
任务名称如下ci.yml:
unit-tests: 141%UnitTest个方法,如果任何类无法编译,任务就会失败。ipm-install:在干净的实例上使用 IPM 0.10.9 安装该模块,并检查已创建的内容,同时确保安装过程未更改任何收集器设置。ipm-upgrade:安装上一版本,打开一个旧页面以保留其数据,随后进行升级,并验证旧页面数据是否已完全清除。web:2.2.0 版本通过了 190 个接口测试,较之前的 159 个有所增加。 新增的 31 项测试涵盖通知规则、监视器和标头控制,包括权限处理、分组、重复项抑制、空闲暂停以及保持“概览”缓存独立。针对 OpenAPI 契约进行验证的无障碍性检查和测试 fixture 仍作为测试套件的一部分。e2e:针对真实实例的浏览器测试,涵盖浅色、深色及 360 像素布局。docker:构建图像并等待其记录历史记录。
合并到 master 会提升模块版本,并将 Docker 镜像发布到 GitHub Container Registry;Open Exchange 会从代码库中获取新版本。
试一试
docker run -d --name iris-history-monitor -p 52773:52773 ghcr.io/diashenrique/iris-history-monitor:latest
然后打开http://localhost:52773/historymonitor/index.html,并以 _SYSTEM / SYS 的身份登录(请在其他人访问该容器之前修改这些信息)。或者,在您自己的实例上:
zpm "install iris-history-monitor"
README文件中包含其余内容,包括“启用历史记录收集”。
若要在本地容器上测试通知功能,请在页眉处点击**“通知”→“启用**”,并在浏览器中允许通知。对于远程实例,请使用 HTTPS;仅使用 http://server:52773 这种明文地址无法启用通知。
尚未完成的工作
- **尚未进行真人可用性测试。**我设定的目标是在5秒内找出“概览”页面上的所有问题。测试脚本已存放在代码库中,但尚未运行。
- **无障碍性仅通过工具进行了检查,尚未经过正式审核。**自动检查(axe)在两种主题下均通过,且仅使用键盘的测试覆盖了每个屏幕的主要任务。但这并非正式审核。
如果您试用后发现内容不清晰或功能失效,请提交一个问题。