文章 John Pan · 3 小时 前 14m read

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 版本中,我还添加了可选的浏览器通知功能。现在,当您在其他界面工作时,只要相关标签页保持打开状态,监控系统就能在指标恶化时及时提醒您。

The new Overview

有哪些变化

  • 全新的三屏界面,包括“概览”、“历史”和“进程**”,这些界面以静态文件(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 现在
![The old System Dashboard](https://raw.githubusercontent.com/diashenrique/iris-history-monitor/master/images/SystemDashboard.png) ![The new Overview](https://raw.githubusercontent.com/diashenrique/iris-history-monitor/master/images/new-overview-light.png)
![The old System Processes](https://raw.githubusercontent.com/diashenrique/iris-history-monitor/master/images/SystemProcesses.png) ![The new Processes](https://raw.githubusercontent.com/diashenrique/iris-history-monitor/master/images/new-processes.png)

界面

概述

仪表盘中的每项指标及其状态:正常、警告、严重或不可用。需要关注的指标会优先显示,页面每10秒自动刷新一次。**点击“暂停”**按钮可停止刷新,当状态发生变化时,屏幕阅读器会收到提示,而非在每次刷新时都进行提示。

历史记录

许可证使用情况、CSP 会话和数据库大小,可按每 5 分钟、每小时或每天为周期进行统计,以支持缩放的图表和表格形式呈现,并支持导出为 CSV 文件。历史数据表以 UTC 时间为基准;屏幕上显示的时间将转换为您所在时区的当地时间。

有两项新功能。在“粒度”下会显示实例保留该粒度数据的时间长度(“保留:最近 60 天”),当记录停止时,系统会明确标注,并显示最后一次采样的时间:

History with the collection notice

进程

该进程列表支持筛选(命名空间、用户、状态、文本)、排序、分页,并提供单个进程的详细信息对话框以及CSV导出功能。

浏览器通知

此前曾有用户请求添加浏览器通知功能(#6)。我在 2.2.0 版本中根据规范 005 实现了该功能。其目的是让用户无需一直盯着“概览”页面,也能及时发现问题。

在页眉处,点击**“通知”(小铃铛图标),然后选择“开启”**。只有在尚未授予权限的情况下,该操作才会向浏览器请求权限。该设置将保存在当前浏览器中。对话框中还会说明,当网址不安全、权限被阻止或浏览器不支持通知时的情况。

规则设计得刻意简洁:

  • **状态恶化时通知:**从“正常”变为“警告”、从“正常”变为“危急”,或从“警告”变为“危急”。
  • **分组变化:**单次检查中多个指标同时恶化将触发一条通知。点击该通知会将监控标签页置于最前,并打开“概览”页面。
  • **避免干扰:**首次读数、新指标、状态未变或正在改善,以及涉及“不可用”状态的过渡情况均不触发通知。此外,当“概览”页面可见时也不会触发通知;因为相关变化已在该页面显示并提示。

在其下方,一个安装在界面外壳中的监视器会在通知开启且被允许的情况下,每 30 秒读取现有的 GET /overview 数据,包括在“历史记录”或“进程”页面上,以及标签页被隐藏时。它拥有自己的查询缓存,因此“概览”页面的**“暂停”**按钮仍不会改变其数值。 浏览器可能会减慢后台定时器的运行速度;README 文件中描述了隐藏标签页的检查频率约为每分钟一次,而非承诺确切的执行时间。

为避免同一来源的不同标签页之间出现重复数据,我将每个指标的最后一次通知状态和时间存储在 localStorage 中。若同一指标在 10 分钟内出现重复的状态,则该数据将被忽略。此机制依赖于浏览器共享存储的可用性;它并非跨浏览器或跨计算机的协调机制。

存在实际限制。通知需要浏览器权限、支持该功能的浏览器、HTTPS 或 localhost,以及一个打开的监控标签页。浏览器关闭后,不会有服务继续运行。 若八小时内无任何交互,后台通知检查将停止,并会通过一条通知说明暂停原因,因此这些检查不会让被遗忘标签页的 IRIS 会话和许可单元持续占用资源。按键、点击鼠标或将标签页恢复到前台均可恢复检查;首次读取将建立新的基准值。

我添加此功能是为了满足那些需要保持监控窗口打开的用户。对于无人值守的警报,请使用实例的“系统监控”电子邮件警报(^%SYSMONMGR),或由警报管理器读取的 SAM 指标。浏览器通知不能替代独立于浏览器运行的监控。README文件中也描述了这些限制。

语言、主题和小屏幕

界面支持英语、葡萄牙语(巴西)和西班牙语,可选择浅色、深色或系统主题,并且可在手机大小的屏幕上正常运行。

葡萄牙语,深色主题 在手机上
![Overview in Portuguese](https://raw.githubusercontent.com/diashenrique/iris-history-monitor/master/images/new-overview-pt-br.png) ![Overview on a phone](https://raw.githubusercontent.com/diashenrique/iris-history-monitor/master/images/new-overview-mobile.png)

在 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)在两种主题下均通过,且仅使用键盘的测试覆盖了每个屏幕的主要任务。但这并非正式审核。

如果您试用后发现内容不清晰或功能失效,请提交一个问题。