1. 页面概览

1.1 是什么

「监控可观测」页(对应前端源码 action/web/src/views/apollo/MonitoringPage.vue,页面标题「Apollo 监控可观测」,对齐 TAD-08)是 LightApollo 的自研轻量监控台:内置 HTTP / TCP / exec 三种探针,后端定时对启用的健康检查执行探测并把结果写入 health_check_results,健康状态按「连续失败次数」判定(healthy → degraded → unhealthy),页面负责把这些对象统一收口展示与配置。页面不引外部监控/图表库:健康分布用六张计数卡 + 状态点 chips,结果趋势用纯 CSS 柱状图(柱高=响应耗时、颜色=状态)。

页面自上而下四大板块:健康总览卡片区(项目内全部健康检查的最新状态分布与状态点)、健康检查列表(探针的 CRUD、启停、立即探测与结果趋势入口)、监控配置卡片(metrics/logs/traces 采集开关、采集间隔与日志保留天数,按环境读写)、监控仪表盘区(简版仪表盘清单与创建,内置模板不可删)。弹窗承载新建/编辑健康检查与探测结果趋势。

理解本页的关键是把「一条健康检查(探针)」当成闭环的最小单元:定义(name + check_type + check_config + 归属环境)→ 后端定时探测(或行内「探测」手动触发)→ 按状态机判定 healthy/degraded/unhealthy → 结果写入趋势 → 总览 chips 与计数反映最新状态。页面上每条健康检查都有独立的检查周期、超时与失败阈值参数,探测结果按 checked_at 时间倒序累积,构成「探测 → 落库 → 判定 → 趋势」的闭环。输入是用户填写的探针定义(URL/主机端口/命令等),输出是列表里的状态徽标、总览卡片计数与弹窗里的耗时趋势。

在整个 Apollo 产品流程中,本页处于「部署与漂移 → 健康监控 → 告警自愈」链路的中间位置:部署产生的健康检查(G18 桥接,携带 deployment_id)在本页列表可查、可探测,「告警自愈」页的告警规则绑定这里的健康检查(下拉取 health-checks),规则的触发依据就是 health_check_results 的最新结果。健康检查被禁用或从未探测时,总览分别按 disabledunknown 单独计数,不混入异常统计。

典型使用链路:① 先在「环境管理」侧确认目标环境(本页默认取环境列表第一个);② 点「新建健康检查」按类型填 URL/host:port/command,保存后列表出现新行(默认启用、最近状态 unknown);③ 等后端调度器按 30s 周期自动探测,或直接点行内「探测」手动触发一次,看状态与耗时;④ 连续失败达到失败阈值后状态翻 unhealthy,总览 chips 同步变红;⑤ 点「结果」打开趋势弹窗,看柱状图与明细定位波动;⑥ 到「告警自愈」页建告警规则绑定该健康检查,异常自动生成事件单。

1.2 核心价值/能力表

能力说明对应页面操作
三种探针http(GET url + 期望状态码)/ tcp(host+port 连通)/ exec(shell 命令退出码)动态表单按类型切换「新建健康检查」弹窗按 check_type 切换字段
状态机判定本次 OK→healthy;本次失败且最近 (threshold-1) 条全部非 healthy→unhealthy;其余失败→degraded后端 ProbeAndRecord,页面列表展示 last_status
健康总览六张计数卡 + 每检查一颗状态点 chip,一屏掌握项目健康分布页首「健康总览(项目 #1)」卡片
立即探测行内一键手动触发一次,即时看到本次状态/耗时/失败原因行内「探测」按钮
结果趋势纯 CSS 柱状趋势(柱高=response_time_ms,最大 56px)+ 明细表,不依赖图表库行内「结果」按钮弹窗
采集配置metrics/logs/traces 开关 + 采集间隔 + 日志保留天数,按环境维度存储「监控配置」卡片 + 「保存配置」
简版仪表盘6 种模板新建自定义仪表盘,后端幂等补齐内置模板(不可删除)「新建仪表盘」/ 行内「删除」

1.3 一句话总结

监控可观测页用内置 HTTP/TCP/exec 探针把「探测、判定、趋势、配置、仪表盘」串成 Apollo 的应用健康监控闭环,是部署健康度与告警规则触发依据的展示与配置入口。

2. 访问入口

2.1 路由与菜单

路由 path/apollo/monitoring
路由 nameApolloMonitoring
meta.titleApollo 监控可观测
侧边栏入口ApolloLayout 侧边栏「监控」,位于「流水线」之后、「告警自愈」之前
前端源码action/web/src/views/apollo/MonitoringPage.vue
API 客户端action/web/src/api/apolloClient.js
路由注册action/web/src/router/index.js/apollo 子路由 children 下第 458~463 行,component: ApolloMonitoringPage,挂 ApolloLayout

相邻页(侧边栏顺序):流水线 pipelines.html(前)、告警自愈 alerts.html(后,告警规则绑定本页健康检查)、部署与漂移 deployments.html(部署自动登记的 deployment_id 健康检查)、环境管理 environments.html(健康检查与监控配置的归属环境)。

2.2 认证与权限

2.3 端口与 API 前缀

3. 界面布局


+--------------------------------------------------------------+
| Apollo 监控可观测                               [新建健康检查]  |
| [alert-info 顶部说明]  [操作结果提示条(可关闭)]               |
+--------------------------------------------------------------+
| [健康总览(项目 #1)卡片]                                      |
|   healthy 正常 | degraded 降级 | unhealthy 异常 |              |
|   unknown 未探测 | disabled 禁用 | 检查总数 六张计数卡          |
|   (chips 区:每检查 状态点+名称+环境·类型+最近状态,或空态提示)|
+--------------------------------------------------------------+
| [健康检查(N)卡片]                                            |
|   ID|名称|类型|检查配置|间隔|超时|失败阈值|环境|启用|最近状态|操作 |
|   操作列:探测 | 结果 | 编辑 | 删除                            |
+--------------------------------------------------------------+
| [监控配置(环境 #N)卡片]                                      |
|   指标采集(metrics_enabled) 日志采集(logs_enabled)             |
|   链路追踪(traces_enabled) 三开关                              |
|   采集间隔(scrape_interval_seconds) 日志保留(logs_retention_days) |
|                                            [保存配置]          |
+--------------------------------------------------------------+
| [监控仪表盘(N)卡片]                                          |
|   名称输入 + 模板下拉 + [新建仪表盘]                           |
|   ID|名称|模板类型|内置|创建时间|操作                           |
+--------------------------------------------------------------+
| [新建/编辑健康检查弹窗][探测结果趋势弹窗]                       |
+--------------------------------------------------------------+

各板块职责:

板块职责
页头 + 顶部说明 + 提示条标题「Apollo 监控可观测」、「新建健康检查」入口、TAD-08 说明、全局操作结果提示(info/success/error 三型,可关闭)
健康总览卡片GET /projects/1/health-status 拉取各健康检查最新状态,按 last_status 计六类数字并渲染状态点 chips
健康检查列表卡片探针定义全量表格(空态/表格两态),行内含启停 switch 与四枚操作按钮
监控配置卡片metrics/logs/traces 三开关 + 采集间隔 + 日志保留天数的读写表单,按 configEnvId 环境
监控仪表盘卡片简版仪表盘清单、内置徽标与创建/删除入口
新建/编辑健康检查弹窗按 check_type 切换的动态表单(http/tcp/exec),创建与编辑复用
探测结果趋势弹窗最近 30 条结果的纯 CSS 柱状图 + 明细表

4. 交互元素

4.1 页头、顶部说明与操作结果提示条

控件位置含义必填/默认/可用条件操作效果触发后端调用边界与细节
页面标题页头 h2页面身份标识常驻Apollo 监控可观测与源码 h2 逐字一致
「新建健康检查」页头右上(btn-primary)打开新建弹窗始终可用openCreate() 置弹窗可见并重置表单,默认 check_type=http、environment_id=环境列表第一个(环境列表空时回退 1)无(提交才触发)弹窗标题「新建健康检查(项目 #1)」
顶部说明标题下 alert-infoTAD-08 定位说明常驻纯文案文案见 5.4 附注,与源码逐字一致
操作结果提示条说明下方展示最近一次操作结果alert.message 才显示成功用 alert-success、失败用 alert-error 展示;「关闭」按钮清空单槽覆盖:新结果替换旧提示,无历史

页面用一组 ref 维护状态:PROJECT_ID(常量 1)、checks(健康检查列表)、overview/overviewChecks(总览计数与 chips 数据)、configForm(监控配置表单)、dashboards(仪表盘列表)、environments(环境下拉)、busy(当前进行中的操作键,非空时所有行内操作按钮与开关禁用)、alert(提示条单槽)、checkModal/checkForm(健康检查弹窗)、resultsModal(趋势弹窗)。注意 busy 是字符串键(如 probe-3saveCheck),同一时刻只允许一个写操作在途。

页面数据在 onMounted 一次性拉取:先 fetchEnvironments()(失败静默为空列表),随后并行 fetchChecks() / fetchHealthStatus() / fetchConfig() / fetchDashboards()无定时轮询、无手动刷新按钮,各写操作成功后仅刷新受影响的列表(详见 4.8)。

4.2 健康总览卡片区

卡片标题固定 健康总览(项目 #{{ PROJECT_ID }})。挂载即请求 GET /projects/1/health-status,成功后把返回 items 经 parseHealthStatus(兼容扁平数组 / 按环境分组 / {items|checks|results} / {health_checks} 多种形态)摊平为 overviewChecks,并按 last_status(缺省当 unknown)累加六类数字:healthy 正常 / degraded 降级 / unhealthy 异常 / unknown 未探测 / disabled 禁用 / 检查总数。

后端该端点返回 {environment_id, items:[{check_id,check_name,check_type,last_status,last_checked_at}]}(见 5.3),要求必带 ?environment_id=本页当前调用未带该参数,总览在 2026-09-07 版本下恒报「缺少查询参数 environment_id」,详见 7.1 与 8 章缺陷表。

4.3 健康检查列表(行级控件)

卡片标题固定 健康检查({{ checks.length }})。空态 暂无健康检查,点击"新建健康检查"添加(http/tcp/exec 探针)。;有数据渲染表格,列头依次 ID / 名称 / 类型 / 检查配置 / 间隔 / 超时 / 失败阈值 / 环境 / 启用 / 最近状态 / 操作。各列取值:

取值规则(逐字对应源码)
IDhc.id 等宽显示
名称加粗 hc.name
类型type-badge 徽标:type-http(蓝)/ type-tcp(紫)/ type-exec(黄),取 hc.check_type,空显 -
检查配置configSummary(hc):http 显 url(期望 N)(无期望不显括注);tcp 显 host:port;exec 显命令全文;其余 JSON 化;超长省略号(cfg-cell)
间隔formatInterval(hc.interval_seconds):<60 秒显 N 秒,<1 小时显 N 分钟(取整),否则显 N.N 小时,空显 -
超时hc.timeout_seconds + 's',空显 -
失败阈值hc.unhealthy_threshold,空显 -
环境envLabel(hc.environment_id):环境列表命中显 名称(#id),未命中显 #id
启用行内 switch(title 提示「点击禁用/点击启用」)
最近状态status-badge 徽标:healthy 绿(status-online)/ degraded 橙(status-degraded)/ unhealthy 红(status-revoked)/ disabled 与 unknown 灰(status-offline),取 hc.last_status || 'unknown'

4.3.1 启用/停用开关

控件位置含义可用条件操作效果触发后端调用边界与细节
switch启用列切换检查启用态busy 为空;当前值取 !!hc.enabled勾选变化触发 handleToggleEnabled:改为相反态,成功后提示 健康检查 #{id} 已启用(最近状态:{last_status})健康检查 #{id} 已禁用(最近状态:{last_status})(success),并刷新列表与总览POST /health-checks/:id/enable/disable失败提示 禁用/启用健康检查 #{id} 失败:{msg}。禁用后调度器不再探测该检查;最近状态从响应 data.last_status 或旧值读取

注意:开关不是即时切换视图,先发请求再按成功结果提示;busy 期间全页面按钮与开关禁用。禁用中的检查行内「探测」仍可点击,但后端 ProbeAndRecord 会拒绝并返回错误 health check {id} 已禁用,跳过探测(页面提示探测失败,见 7.3)。

4.3.2 行内「探测」按钮

控件位置含义可用条件操作效果触发后端调用边界与细节
「探测」操作列(btn-sm btn-outline)立即执行一次探测并落库busy 为空handleProbebusy='probe-{id}',成功后从响应取结果(兼容 data / data.result / data.health_check_result 多形态),提示 探测完成:状态 {status}{,耗时 {rt} ms}{,原因:{err}}——status 为 healthy 用绿色,否则红色POST /health-checks/:id/probe探测完成后自动刷新列表与总览。禁用中的检查探测会被后端拒绝(见 4.3.1)。目标不可达时状态为 degraded/unhealthy 且提示带原因

该操作直接写一条 health_check_results 并刷新 last_status,等同一次调度周期的手动触发,适合验证配置与排查(见 7.2)。

4.3.3 行内「结果」「编辑」「删除」按钮

控件位置含义可用条件操作效果触发后端调用边界与细节
「结果」操作列打开该检查的结果趋势弹窗busy 为空openResults(hc) 弹窗载入最近 30 条结果(见 4.5)GET /health-checks/:id/results?limit=30弹窗标题 探测结果趋势:{名称}
「编辑」操作列打开编辑弹窗回填表单busy 为空openEdit(hc) 回填 check_config 各字段到动态表单并置 mode='edit'无(提交才触发)弹窗标题 编辑健康检查 #{id}(项目 #1)
「删除」操作列(btn-danger)删除该健康检查busy 为空confirm('确定删除健康检查 "{名称}"(#{id})吗?关联的探测结果将一并清理。') 通过后 DELETE 并提示 健康检查已删除(success),刷新列表与总览DELETE /health-checks/:id确认文案逐字见左;后端级联清理该检查的 health_check_results;失败提示 删除健康检查失败:{msg}

4.4 新建/编辑健康检查弹窗

控件位置含义必填/默认/可用条件操作效果触发后端调用边界与细节
弹窗标题弹窗头标识当前模式create 显示 新建健康检查,edit 显示 编辑健康检查 #{id},均附 (项目 #1)-点遮罩 @click.self 或「关闭」可关闭
名称(name)*表单首行探针名称必填;placeholder 如 api-health / db-port / deploy-check前端空名拦截提示 名称(name)必填-保存 payload name trim
检查类型(check_type)*类型下拉探针类型必填;http/tcp/exec 三选(HTTP 探测/TCP 端口探测/Exec 命令探测切换即切换下方动态表单-创建默认 http
所属环境(environment_id)*环境选择探针归属环境环境列表非空时下拉(名称(#id));为空时降级为数字输入(placeholder 环境 ID(环境列表加载失败时手输)--创建默认取环境列表第一个(空则 1);后端对 0 值兜底写 1
URL(check_config.url)*http 动态区探测地址仅 http 显示;必填--placeholder 如 http://127.0.0.1:18080/healthz
期望状态码(check_config.expect_status)http 动态区期望 HTTP 状态码仅 http;非必填,0=仅探测可达填了才进 payload-placeholder 如 200;>0 时与实际状态码须相等
主机(check_config.host)* / 端口(check_config.port)*tcp 动态区TCP 目标仅 tcp;必填--端口 min=1 max=65535;placeholder 127.0.0.1 / 5432
命令(check_config.command)*exec 动态区shell 命令仅 exec;必填--placeholder 如 curl -s http://127.0.0.1:18080/healthz;exit code 0=OK
探测间隔(interval_seconds)通用三连输入探测周期非必填非空才入 payload-placeholder 默认 30;后端存储但当前调度不按它逐个排程(见 5.4/8)
超时(timeout_seconds)通用三连输入单次超时非必填非空才入 payload-placeholder 默认 5
失败阈值(unhealthy_threshold)通用三连输入连续失败判 unhealthy非必填非空才入 payload-placeholder 默认 3;1=单次失败即异常
「取消」/「创建」或「保存」弹窗底部关闭 / 提交提交时 busy 为空create 调用 POST,成功提示 健康检查创建成功(id={id});edit 调用 PUT,成功提示 健康检查 #{id} 更新成功;均关闭弹窗并刷新列表与总览POST /projects/1/health-checks / PUT /health-checks/:id保存前校验:名称空、配置不完整(见下)会前端拦截;失败提示 保存健康检查失败:{msg}

前端配置完整性校验handleSaveCheck,拦截文案逐字):检查配置不完整(http 需 url;tcp 需 host+port;exec 需 command)——http 判 cfg.url 非空、tcp 判 cfg.host 非空且 cfg.port > 0、exec 判 cfg.command 非空。后端另做 CheckConfig 必填校验,双重防护。

保存 payloadbuildCheckPayload):固定字段 {name, check_type, check_config:{url 或 host+port 或 command}, environment_id};三个通用数字字段仅在非空时追加(转为 Number);expect_status 非空时作为数字入 check_config。编辑保存为全量 PUT,未填的通用字段保持不变。

4.5 探测结果趋势弹窗(「结果」)

控件/元素位置含义取值规则操作效果触发后端调用边界与细节
弹窗标题弹窗头标识检查探测结果趋势:{hc.name}-点遮罩或「关闭」可关
柱状趋势区弹窗上部(.trend-box)最近探测耗时与状态的 CSS 柱状图每柱高=该条 response_time_ms 相对本批最大值的归一化(max(4, round(rt/maxRt*56)),最高 56px),颜色=状态:绿 healthy / 橙 degraded / 红 unhealthy / 灰 unknown;rt=0 也给 4px 底柱纯展示;hover title #序号 状态 {status} @ {时间} · {rt} ms柱高随本批最大值缩放,无固定刻度;caption 文案 柱高 = response_time_ms(最大 {maxRt} ms),颜色 = 状态(绿 healthy / 橙 degraded / 红 unhealthy / 灰 unknown)
明细表弹窗下部最近 30 条结果逐条明细列:# / 状态(status-badge)/ 状态码 / 响应耗时 / 错误信息 / 探测时间;状态码与耗时空显 -,错误信息空显 -纯展示时间 formatTime(RFC3339 去 T/Z 截前 19 位,不转本地时区);空态 暂无探测结果,可先点击"探测"触发一次。

openResults 请求 GET /health-checks/:id/results(Query limit=30),响应经 toList 解析,并求本批 response_time_ms 最大值 maxRt 供柱高归一化;加载失败顶部提示 加载探测结果失败:{msg}。后端结果默认按 checked_at DESC 倒序返回(最新的在最左/最上)。

4.6 监控配置卡片

卡片标题固定 监控配置(环境 #{{ configEnvId }})——configEnvId 由环境列表第一个环境决定(初始 1)。表单默认值:metrics_enabled=true、scrape_interval_seconds=30、logs_enabled=true、logs_retention_days=7、traces_enabled=false;读接口返回后按 data.config || data.monitoring_config || data 回填,其中 metrics/logs 用「!== false 即开」宽松判定。

控件位置含义可用条件操作效果触发后端调用边界与细节
指标采集(metrics_enabled)配置卡第一行 switch-lg指标采集开关常驻v-model 直接改本地表单无(点保存才提交)-
日志采集(logs_enabled)同左日志采集开关常驻同左-
链路追踪(traces_enabled)同左链路采集开关常驻同左默认关
采集间隔(scrape_interval_seconds,秒)第二行数字输入 min=1采集周期常驻仅本地保存前须 >0
日志保留(logs_retention_days,天)同左 min=1日志保留天数常驻仅本地保存前须 >0
「保存配置」卡片右下(btn-primary)提交监控配置busy 为空;进行中按钮显示 保存中...handleSaveConfig 先本地校验后 PUT,成功提示 监控配置保存成功(环境 #{configEnvId})(success)PUT /projects/1/monitoring-config(Query environment_id={configEnvId}校验拦截文案:采集间隔(scrape_interval_seconds)必须大于 0日志保留(logs_retention_days)必须大于 0;失败提示 保存监控配置失败:{msg}

读配置走 GET /projects/1/monitoring-config(Query environment_id={configEnvId}),加载失败顶部提示 加载监控配置失败:{msg}。后端 GetOrCreateConfig 在不存在时按默认值幂等建行(project_id+environment_id 唯一),因此该配置只影响项目 1 的目标环境,其它环境配置独立存储(见 8)。

4.7 监控仪表盘卡片

卡片标题固定 监控仪表盘({{ dashboards.length }})。卡片内含创建行 + 表格:

控件位置含义可用条件操作效果触发后端调用边界与细节
名称输入创建行仪表盘名称placeholder 仪表盘名称,如 订单服务概览;回车(@keyup.enter)等同点新建--名称为空时新建按钮禁用
模板下拉创建行dashboard template_type六选项:自定义(custom) / Go 应用(go_app) / Java 应用(java_app) / Nginx(nginx) / Redis(redis) / K8s Pod(k8s_pod)--默认 custom
「新建仪表盘」创建行(btn-primary)创建自定义仪表盘busy 为空且名称非空 trimhandleCreateDashboard:POST 成功提示 仪表盘创建成功(id={id})(success)、清空名称并刷新列表;进行中显示 创建中...POST /projects/1/dashboards名称空兜底提示 仪表盘名称必填;失败提示 创建仪表盘失败:{msg}
表格行ID/名称/模板类型/内置/创建时间/操作内置列徽标 内置(灰 status-offline)/ 自定义(绿 status-online)-模板类型按模板 label 显示-时间 formatTime(created_at)
「删除」操作列(btn-danger)删除仪表盘busy 为空且非内置(!!d.is_builtin 禁用,title 内置仪表盘不允许删除confirm('确定删除仪表盘 "{名称}"(#{id})吗?') 通过后 DELETE,成功提示 仪表盘已删除(success)并刷新DELETE /dashboards/:idis_builtin 为 true 时按钮禁用、点击直接 return;失败提示 删除仪表盘失败:{msg}

列表请求 GET /projects/1/dashboards:后端先幂等补齐内置模板(Go 应用概览 go_app、Nginx 接入概览 nginx,is_builtin=true,见 5.4)再按 id 升序返回。空态 暂无仪表盘。;内置与自定义同名不冲突(无唯一约束),可「新建同名自定义」。

4.8 全局互斥与数据刷新

全页唯一「写互斥位」是字符串 busy:任何写操作开始置为对应操作键(saveCheck/saveConfig/saveDash/probe-{id}/enable-{id}/disable-{id}/delCheck-{id}/delDash-{id} 等),结束时在 finally 清空。busy 非空时:列表四枚操作按钮与行内 switch、弹窗提交按钮、监控配置保存按钮、仪表盘删除按钮全部禁用(:disabled="!!busy")——避免并发写把同一检查状态改乱。注意 打开弹窗/查看结果不受 busy 约束(只读)。

各写操作成功后的刷新面:健康检查相关的保存/探测/启停/删除都会 fetchChecks() + fetchHealthStatus();监控配置保存只提交不重拉配置;仪表盘创建/删除只 fetchDashboards()。页面初始加载顺序与失败策略见 4.1。

5. 后端关联

5.1 API 客户端

action/web/src/api/apolloClient.js:axios 实例 baseURL /apollo-api/v1、timeout 30000ms、请求拦截器对所有请求附加 Authorization: Bearer <token>(token 取 apollo_token,无则回退 aip_token)。响应拦截器:HTTP 2xx 且 code===0 时把 response.data 解包为业务数据;HTTP 2xx 但 code!==0 拒绝;4xx/5xx 提取 body.message || body.error 并把 message 别名写入 error.response.data.error 后 reject——401 清 token 跳 /apollo/login,403 alert('无权限执行该操作')。页面 catch 统一以 err.response?.data?.message || err.response?.data?.error || err.message || '未知错误' 展示后端错误。

5.2 端点表

以下路径均为相对 baseURL /apollo-api/v1(Vite rewrite 后后端收 /api/v1/...)。项目 id 页面恒用 1。权限点映射见 server.go 注册(894~908 行)。

方法路径权限请求体/Query页面触发点
GET/projects/1/health-statusPermMonitoringRead后端要求 Query environment_id(当前页面未传,见 7.1/8)健康总览卡片
GET/projects/1/health-checksPermMonitoringRead可选 Query deployment_id(部署关联过滤,页面未用)健康检查列表
POST/projects/1/health-checksPermMonitoringWritebody HealthCheckRequest(见 4.4)新建健康检查
GET/health-checks/:idPermMonitoringRead-(页面未调用,后端有)
PUT/health-checks/:idPermMonitoringWritebody HealthCheckRequest编辑健康检查
DELETE/health-checks/:idPermMonitoringWrite-删除健康检查
POST/health-checks/:id/enablePermMonitoringExecute-启用开关
POST/health-checks/:id/disablePermMonitoringExecute-停用开关
POST/health-checks/:id/probePermMonitoringExecute-「探测」
GET/health-checks/:id/resultsPermMonitoringReadQuery limit(默认 20、上限 1000;页面传 30)「结果」趋势弹窗
GET/projects/1/monitoring-configPermMonitoringReadQuery environment_id(必填)监控配置读
PUT/projects/1/monitoring-configPermMonitoringWriteQuery environment_id(必填);body MonitoringConfigRequest(指针字段部分更新)「保存配置」
GET/projects/1/dashboardsPermMonitoringRead-仪表盘列表
POST/projects/1/dashboardsPermMonitoringWritebody DashboardRequest{name, template_type}「新建仪表盘」
DELETE/dashboards/:idPermMonitoringWrite-删除仪表盘
GET/projects/1/environments--环境下拉(决定 configEnvId 与弹窗默认环境)

5.3 响应结构示例

统一 envelope 解包后(code===0 的 data)。健康总览GET /projects/1/health-status?environment_id=1,真实后端形状——页面当前未传参恒 400,示例供接口正确调用时参考):


{
  "environment_id": 1,
  "items": [
    { "check_id": 3, "check_name": "api-health", "check_type": "http", "last_status": "healthy", "last_checked_at": "2026-09-07T03:21:00+08:00" },
    { "check_id": 5, "check_name": "db-port", "check_type": "tcp", "last_status": "degraded", "last_checked_at": "2026-09-07T03:21:30+08:00" }
  ]
}

字段含义(monitoring.EnvironmentStatusItem):check_id 健康检查主键;check_name 名称;check_type 探针类型 http/tcp/exec;last_status 最近判定状态(healthy/degraded/unhealthy/disabled/unknown);last_checked_at 最近一次探测时间(无结果则缺省)。本页 parseHealthStatus 对扁平数组 / {environments:[{items:[]}]} / {items} / {health_checks} 均能摊平,取 last_status 计数。

健康检查列表GET /projects/1/health-checks,envelope data 为数组,单项即 HealthCheck 行):


{
  "id": 3,
  "project_id": 1,
  "environment_id": 1,
  "name": "api-health",
  "check_type": "http",
  "check_config": { "url": "http://127.0.0.1:18080/healthz", "expect_status": 200 },
  "interval_seconds": 30,
  "timeout_seconds": 5,
  "unhealthy_threshold": 3,
  "enabled": true,
  "last_status": "healthy",
  "created_at": "2026-09-07T02:00:00+08:00",
  "updated_at": "2026-09-07T03:21:00+08:00"
}

立即探测 / 单条结果POST /health-checks/:id/probeGET .../results 元素同构,envelope data 为 HealthCheckResult 或数组):


{
  "id": 91,
  "health_check_id": 3,
  "status": "healthy",
  "status_code": 200,
  "response_time_ms": 8,
  "error_message": "",
  "checked_at": "2026-09-07T03:21:30+08:00"
}

字段含义:status_code 非 HTTP 探测恒 0;error_message 失败原因(HTTP 报 HTTP 探测失败: .../状态码不符: 期望 200, 实际 500、TCP 报 TCP 探测失败: ...、exec 报 exec 探测失败: ...);checked_at 探测时间。

监控配置GET/PUT /projects/1/monitoring-config?environment_id=1):


{
  "id": 1,
  "project_id": 1,
  "environment_id": 1,
  "metrics_enabled": true,
  "scrape_interval_seconds": 30,
  "logs_enabled": true,
  "logs_retention_days": 7,
  "traces_enabled": false,
  "created_at": "2026-09-07T01:00:00+08:00",
  "updated_at": "2026-09-07T03:20:00+08:00"
}

仪表盘列表GET /projects/1/dashboards,后端先幂等补齐内置模板再返回,envelope data 为数组):


[
  { "id": 1, "project_id": 1, "name": "Go 应用概览", "template_type": "go_app", "dashboard_json": { "title": "Go 应用概览", "charts": [] }, "is_builtin": true, "created_at": "2026-09-07T01:00:00+08:00", "updated_at": "2026-09-07T01:00:00+08:00" },
  { "id": 2, "project_id": 1, "name": "Nginx 接入概览", "template_type": "nginx", "dashboard_json": { "title": "Nginx 接入概览", "charts": [] }, "is_builtin": true, "created_at": "2026-09-07T01:00:00+08:00", "updated_at": "2026-09-07T01:00:00+08:00" }
]

is_builtin=true 的行前端禁用删除;新建自定义返回 data 为创建的 Dashboard(含新 id),页面取 data.id || data.dashboard.id

错误示例(envelope fail):健康总览缺 environment_id 时后端返回 HTTP 400、code=40001message=缺少查询参数 environment_id(测试 M-23 佐证,见 7.1)。

5.4 关键机制

三种探针的实现语义monitoring/probe.goRunProbe 按 check_type 分发):

健康状态机ProbeAndRecord):单次探测结果经三次判定后写 health_check_results 并刷新 health_checks.last_status:本次 OK→healthy;本次失败且最近 (threshold-1) 条结果全部非 healthyunhealthy(连续失败链达成);其余失败→degraded。特例 threshold=1:单次失败即 unhealthy(不查历史)。探测前若检查已禁用,直接返回错误 health check {id} 已禁用,跳过探测 不落库。默认常量:DefaultScrapeIntervalSeconds=30DefaultTimeoutSeconds=5DefaultUnhealthyThreshold=3DefaultLogsRetentionDays=7(monitoring/models.go)。后端兜底逻辑与页面默认值一致。

调度monitoring.Scheduler 在 server 启动处以 30s 间隔 Start(ctx, 30*time.Second),每 tick 调 RunOnce——把全部 enabled=true 的健康检查按 id 升序各探测一轮(单条失败不中断整轮,仅 logger.Warn)。因此「每检查的 interval_seconds」在列表可见,但当前不参与按检查独立排程,实际节奏由全局 30s tick 驱动(见 8 边界)。

监控配置按环境存储monitoring_configsproject_id+environment_id 唯一索引;GetOrCreateConfig 不存在时按默认值幂等建行,UpdateConfig 用指针字段做部分更新(请求体缺失字段不改动)。页面 configEnvId 取环境列表第一个 id,GET/PUT 均带该 environment_id。健康检查创建时后端对 environment_id=0 兜底写 1(与配置默认环境口径一致),故本页总览即便补上参数也只反映默认环境 #1 的检查。

内置仪表盘EnsureBuiltinDashboardsGET /projects/1/dashboards 时对缺失的内置模板幂等建行——内置定义两个:Go 应用概览(template_type=go_app,结构 JSON title 为 Go 应用概览)与 Nginx 接入概览(nginx,title 为 Nginx 接入概览),均 is_builtin=trueDeleteDashboard 对内置行返回拒绝(前端已禁用按钮双保险),新建自定义仅存 name+template_type(未提供 dashboard_json 用空模板)。

总览宽松解析是兼容性设计parseHealthStatus 同时兼容「扁平数组」「按环境分组(对象数组内嵌 items)」「{items|checks|results}」「{health_checks}」四种后端形态,把环境分组里每条子项合并 environment 字段后摊平。这与 deployments/monitoring 跨模块复用 health-status 的字段演进有关;当前 Apollo 后端实际只回 {environment_id, items} 一种形态。

跨页关联:部署与健康(G18)——部署产生/关联的健康检查带 deployment_idGET /health-checks?deployment_id=x 可按部署过滤查询(页面未用,供「部署与漂移」侧追溯);告警绑定——「告警自愈」页新建规则的健康检查下拉即本页资源,alert 包按表名直查 health_checks/health_check_results,评估引擎消费最新一条结果判定触发(见 alerts.html 5.4)。

6. 权限与安全

7. 常见问题与排错

以下现象均由 MonitoringPage.vue 的 catch 分支、后端 handler/测试与状态机代码归纳,可溯源复现;确属前端缺陷的两条见 8 章,并已记入临时缺陷报告。

  1. 顶部提示「加载健康总览失败:缺少查询参数 environment_id」,健康总览计数恒为 0:原因是 fetchHealthStatus() 调用 GET /projects/1/health-status 时未带 ?environment_id=,而后端 handleHealthStatusparseEnvIDQuery 强制要求该参数(缺失返回 40001「缺少查询参数 environment_id」,测试 M-23 佐证)。处理:这是 2026-09-07 版本的 web/src 前端缺陷(未按后端契约传参),用户侧暂无法绕过——总览恒失败、chips 恒空,健康检查状态请以列表「最近状态」列为准;修复需在 fetchHealthStatus{ params: { environment_id: configEnvId.value } }

  2. 探测返回 degraded/unhealthy,或最近状态一直 unknown:原因是目标不可达(http 连接失败/状态码不符、tcp 端口不通、exec 命令非零退出/超时),或尚未到首个调度 tick(unknown=从未产生结果)。处理:先点行内「探测」看提示原因(如 HTTP 探测失败: ...状态码不符: 期望 200, 实际 500TCP 探测失败: ...);核对 URL/端口/命令与目标服务存活;确认检查处于启用态。exec 探测失败还需确认命令在 Apollo 后端所在机器可用(本页探测在服务端进程内执行,不是浏览器)。

  3. 点「探测」提示「探测健康检查 #{id} 失败:health check {id} 已禁用,跳过探测」:原因是该行开关处于关闭态,后端 ProbeAndRecord 拒绝对禁用检查探测。处理:先打开启用开关(切换后提示最近状态)再探测;或编辑后由调度器下轮自动探测。

  4. 新建健康检查提示「检查配置不完整(http 需 url;tcp 需 host+port;exec 需 command)」:原因是当前 check_type 对应的动态字段缺填(如 tcp 缺 port、exec 缺 command),前端 handleSaveCheck 拦截。处理:切回对应类型补齐必填字段(http→url、tcp→host+port、exec→command)再保存;expect_status/host/port 类型不符时 Number 转换也可能被后端拒绝。

  5. 「保存配置」被拦截或提示失败:原因是 scrape_interval_secondslogs_retention_days 为空/≤0(前端先拦 采集间隔(scrape_interval_seconds)必须大于 0 / 日志保留(logs_retention_days)必须大于 0),或后端 PUT 校验失败。处理:确认两个数字字段 >0;若已 >0 仍失败,看错误 message 与 401/403(权限不足或 token 失效)。

  6. 内置仪表盘无法删除、删除按钮禁用:原因是该行 is_builtin=true(内置模板),按钮 title 内置仪表盘不允许删除:disabled="!!busy || !!d.is_builtin"。处理:仅可删除自定义仪表盘;内置模板无需删,可直接新建同名自定义。

  7. 「结果」趋势弹窗柱子形状看不懂或没有数据:原因是柱高按本批最大 response_time_ms 归一化(最高 56px),长尾耗时会让多数柱子趋同;空态 暂无探测结果,可先点击"探测"触发一次。。处理:把鼠标移到柱子上看 hover title(序号/状态/时间/耗时);确认检查最近探测过、且列表刷新成功;仅最近 30 条可见,更早历史需后端配合分页。

  8. 删除健康检查后「告警自愈」页规则里的下拉/绑定怎么办:健康检查被删后,绑定它的告警规则 health_check_id 悬空,规则评估时查询无结果即跳过(EvaluateHealthCheck 找不到最新结果直接 return,不触发)。处理:到「告警自愈」页把失效规则改为绑定现存检查、停用或删除,避免规则形同虚设。

8. 已知缺陷与边界

缺陷/边界说明
健康总览缺参恒失败(web/src 可修)fetchHealthStatus() 未传后端必填的 environment_id,总览恒 400「缺少查询参数 environment_id」,计数与 chips 恒空;修复:GET 补 { params: { environment_id: configEnvId.value } }。已记临时缺陷报告 batch-p5
项目 id 硬编码PROJECT_ID = 1 常量,注释标明「后续可改为从 URL 参数 /project_id 读取」
监控配置单环境配置读写固定针对 configEnvId(环境列表第一个),多环境需切环境列表顺序或改代码;其它环境无 UI 入口
探测节奏与 interval_seconds 不符后端调度按全局 30s tick 对所有 enabled 检查各探测一轮,单检查 interval_seconds 仅存储展示、不参与独立排程;页面「间隔」列勿理解为精确周期
结果趋势无分页openResults 固定 limit=30,弹窗不提供翻页,更早历史不可见(后端 ListResults 默认 20、上限 1000,可手动改 limit 但页面写死 30)
纯 CSS 柱状局限柱高最大 56px、按本批最大值归一化,无固定刻度/坐标轴,长尾耗时柱形趋同
exec/file 探针边界exec 在 Apollo 后端进程内执行命令、file 探针仅后端支持;本页未暴露 file 形态 UI,exec 命令可用性依赖部署环境
总览宽松解析parseHealthStatus 兼容多种后端形态属兼容性设计,但也掩盖契约漂移(见第 1 条缺参缺陷直到接口测试才暴露)
仪表盘为简版仅清单/创建/删除与内置补齐,无图表渲染、无编辑能力;内置 dashboard_json 为空 charts 模板
列表/总览无轮询onMounted 拉取一次后仅写操作成功刷新;长时间停留页面的状态不自动更新,需重新进入或操作触发
时间展示为 UTCformatTime 对 RFC3339 去 T/Z 截前 19 位展示,不转本地时区,跨时区观察有偏差

附录 A:速查——页面文案与关键常量清单

场景源码取值/文案(逐字)
页面标题Apollo 监控可观测
顶部说明TAD-08 自研轻量监控:内置 HTTP/TCP/exec 探针定时探测并落库,健康状态按连续失败次数判定(healthy → degraded → unhealthy);本项目 id 暂用常量 1。
总览卡片标题健康总览(项目 #{{ PROJECT_ID }})
计数卡标签healthy 正常 / degraded 降级 / unhealthy 异常 / unknown 未探测 / disabled 禁用 / 检查总数
列表卡片标题健康检查({{ checks.length }})
总览/列表空态暂无健康检查,点击"新建健康检查"添加。 / 暂无健康检查,点击"新建健康检查"添加(http/tcp/exec 探针)。
类型选项HTTP 探测 / TCP 端口探测 / Exec 命令探测
配置不完整提示检查配置不完整(http 需 url;tcp 需 host+port;exec 需 command)
探测成功提示探测完成:状态 {status}{,耗时 {rt} ms}{,原因:{err}}
启停提示健康检查 #{id} 已启用(最近状态:{status}) / 健康检查 #{id} 已禁用(最近状态:{status})
删除确认确定删除健康检查 "{name}"(#{id})吗?关联的探测结果将一并清理。
配置保存成功监控配置保存成功(环境 #{configEnvId})
健康检查保存/更新成功健康检查创建成功(id={newId})(无 id 时不带括号部分)/ 健康检查 #{id} 更新成功
加载/保存失败提示加载健康总览失败:{msg} / 加载健康检查列表失败:{msg} / 保存监控配置失败:{msg} / 保存健康检查失败:{msg} / 加载仪表盘列表失败:{msg}
配置校验采集间隔(scrape_interval_seconds)必须大于 0 / 日志保留(logs_retention_days)必须大于 0
仪表盘空态暂无仪表盘。
删除确认(仪表盘)确定删除仪表盘 "{name}"(#{id})吗?
内置禁用提示title 内置仪表盘不允许删除
趋势 caption柱高 = response_time_ms(最大 {maxRt} ms),颜色 = 状态(绿 healthy / 橙 degraded / 红 unhealthy / 灰 unknown)
结果空态暂无探测结果,可先点击"探测"触发一次。
默认常量interval_seconds=30、timeout_seconds=5、unhealthy_threshold=3、logs_retention_days=7
模板枚举custom / go_app / java_app / nginx / redis / k8s_pod