- 可观测性
- 后端
- 前端
- 运维
【免费下载链接】kener
Stunning status pages, batteries included!
Incident Updates(事件更新,在 Kener 中亦称 comments)是 Kener 状态页系统中向用户传达事件进展、并驱动事件状态流转的核心机制。本文基于 Kener v4 文档与仓库源码,完整讲解更新的组成要素(消息、状态、时间戳)、创建/编辑/删除的完整操作流程、ACTIVE/INACTIVE 可见性控制、公开页时间线渲染方式以及订阅通知链路,并给出可落地的更新频率与文案最佳实践。读完本文,你将掌握如何用更新驱动一次事件从 INVESTIGATING 到 RESOLVED 的完整生命周期,并能结合源码理解状态与end_date_time的联动逻辑。
什么是 Incident Updates
Incident Update 是一条带时间戳的消息,它同时承担多种职责:
- 描述当前状态或已采取的行动;
- 改变事件的当前状态(state);
- 出现在公开状态页上;
- 形成完整的事件审计轨迹(audit trail);
- 与用户透明沟通进展。
在数据库层面,更新以 "comment"(评论)的形式存储在incident_comments表中(因此文档与源码中经常混用 "update" 与 "comment" 两个词),但在状态页上被渲染为一条按时间倒序排列的时间线。
从源码结构看,Kener 将"事件更新"设计为事件生命周期中的唯一状态变更入口:incidentController.ts中的AddIncidentComment是事件公开沟通的唯一咽喉点,注释明确说明"订阅者通知完全由事件评论驱动"——无论是来自告警、仪表盘还是 API 创建的事件,只有发布评论才会触发订阅者邮件,告警队列本身不会重复推送事件通知(见 incidentController.ts)。
Update 的组成要素
每条更新由三个必填要素构成:
Message(消息,必填)
用户实际看到的更新正文。支持完整的 Markdown 格式化,包括:
- 加粗与斜体
- 标题(Headers)
- 列表(有序与无序)
- 链接
- 代码块
- 表格
示例:
We've identified the root cause as a database connection pool exhaustion. **Actions Taken:** - Increased connection pool size from 100 to 500 - Restarted application servers - Enabled connection pool monitoring **Next Steps:** - Monitor error rates for 15 minutes - Verify connection pool health值得注意:Kener 在前端渲染时并非直接输出 Markdown,而是先经mdToHTML转换为 HTML,再通过SveltePurify组件安全地注入页面,从而兼顾富文本效果与 XSS 防护(见 IncidentItem.svelte)。
State(状态,必填)
该更新发布后事件所处的状态。这是最关键的要素,因为:
- 事件的当前状态 = 最近一条更新的状态;
- 在更新中修改状态,事件状态随之改变;
- 状态的推进驱动着事件的整个生命周期。
可用状态定义于 global-constants.ts 的IncidentState联合类型:
| 状态 | 含义 |
|---|---|
| INVESTIGATING | 初步调查中 |
| IDENTIFIED | 已定位根因 |
| MONITORING | 已应用修复,观察稳定性 |
| RESOLVED | 问题完全解决 |
重要联动:当把状态设置为RESOLVED时,Kener 会自动将事件的end_date_time设置为该更新的时间戳。源码中AddIncidentComment的逻辑如下(见 incidentController.ts):
if (state === GC.RESOLVED) { incidentUpdate.end_date_time = commented_at; } else { if (incidentExists.state === GC.RESOLVED) { await db.setIncidentEndTimeToNull(incident_id); } }即:状态变为 RESOLVED 时写入结束时间;反过来,如果事件当前已是 RESOLVED 而新更新将状态改回非 RESOLVED,则清空end_date_time(事件重新开放)。这一逻辑同样存在于编辑更新的UpdateCommentByID(见 incidentController.ts)。
Timestamp(时间戳,必填)
该更新发生的时间。
- 默认值:当前日期与时间;
- 可修改:可用于回溯补记(backdating)、让时间线对齐真实事件、事后补录更新;
- 展示:按你的本地时区显示,但底层以 UTC 存储。
数据库层面对应commented_at整数列(UNIX 时间戳)。值得一提的是,v4 API 在写入时间戳时会通过GetMinuteStartTimestampUTC将其规范化到分钟起点(见 comments/+server.ts/api/v4/incidents/[incident_id]/comments/+server.ts#L79-L95)),未传时间戳时则取当前时刻的分钟起点。
数据表结构
incident_comments表由初始迁移创建(见 migrations/20250111153517_init.ts):
| 列 | 类型 | 说明 |
|---|---|---|
id | increments, 主键 | 更新 ID |
comment | text, 非空 | 更新正文(Markdown) |
incident_id | integer, 非空 | 所属事件 |
commented_at | integer, 非空 | 更新时间戳(UTC) |
created_at/updated_at | timestamp | 记录创建与最后修改时间 |
status | string, 默认ACTIVE | 可见性状态 |
state | string, 默认INVESTIGATING | 更新时的状态 |
对应的 TypeScript 类型IncidentCommentRecord定义在 src/lib/server/types/db.ts。
创建更新
创建事件时附带 Initial Update
新建事件时,可选择性提供初始更新(Initial Update):
- 填写事件详情;
- 在 "Initial Update" 字段中添加文本(支持 Markdown);
- 该更新自动以状态INVESTIGATING创建;
- 时间戳与事件开始时间一致。
源码中的CreateNewIncidentWithCommentAndMonitor正是如此实现:创建事件后立即以GC.INVESTIGATING状态、data.start_date_time为时间戳写入初始评论(见 incidentController.ts)。
为已有事件添加更新
- 进入事件详情页;
- 点击Add Update按钮;
- 输入消息(支持 Markdown);
- 选择新的状态;
- 可选地调整时间戳;
- 点击Add Update提交。
提交后发生的事:
- 更新被加入事件时间线;
- 事件状态变为该更新的状态;
- 若状态为 RESOLVED,则
end_date_time被设置为更新时间戳; - 更新立即出现在状态页上;
- 若配置了订阅系统,订阅者收到通知。
通过 v4 API 创建
Kener 的 v4 API 提供了等价能力。向POST /api/v4/incidents/{incident_id}/comments发送 JSON:
{ "comment": "We've identified the root cause as a database connection pool exhaustion.", "state": "IDENTIFIED", "timestamp": 1700000000 }服务端校验规则(见 comments/+server.ts/api/v4/incidents/[incident_id]/comments/+server.ts#L50-L139)):
comment必填,且必须是非空字符串;timestamp可选,缺省取当前时间;必须是合法的 UTC 秒级时间戳,并会被规范化到分钟起点;state必填,且必须是INVESTIGATING | IDENTIFIED | MONITORING | RESOLVED之一,否则返回 400;- 创建成功后返回 201,同时以该评论的状态更新事件的
state与(必要时)end_date_time。
编辑更新
你可以编辑任意已有更新:
- 在事件时间线中找到目标更新;
- 点击Edit(铅笔图标)按钮;
- 修改消息;
- 如需要,修改状态;
- 如需要,调整时间戳;
- 点击Save保存。
关键的状态变化:
变为 RESOLVED:
- 将事件的
end_date_time设为该更新时间戳; - 在状态页上关闭事件;
- 触发解决类通知。
从 RESOLVED 改回其他状态:
- 清空事件的
end_date_time; - 重新打开事件;
- 状态页上重新显示为进行中。
对应的服务端实现UpdateCommentByID会先校验事件与评论均存在,然后执行数据库更新,并根据新状态同步事件的state/end_date_time(见 incidentController.ts)。
注意两条编辑路径的差异:仪表盘 UI 的编辑会同步事件状态;而 v4 API 的
PATCH /api/v4/incidents/{incident_id}/comments/{comment_id}在更新评论本身时不会改变事件的状态字段,仅当需要时在评论内调整其state(见 comments/[comment_id]/+server.ts)。在设计自动化流程时需留意这一区别。
删除更新
删除更新同样在时间线中操作:
- 在时间线中找到目标更新;
- 点击Delete(垃圾桶图标)按钮;
- 确认删除。
警告:此操作不可撤销(前端 UI 路径为永久移除)。
删除后的影响:
- 更新被永久移除;
- 事件状态回退到前一条更新的状态;
- 时间线自动调整;
- 如果删除的是最近一条更新,事件将采用倒数第二条更新的状态;
- 特殊情况:如果删除了全部更新,事件保留创建时的原始状态。
需要区分的是:仪表盘 UI 的删除是硬删除,而 v4 API 的DELETE是软删除——将评论status置为DELETED,数据库记录仍然保留,且 GET 查询时 DELETED 评论按 404 处理(见 comments/[comment_id]/+server.ts)。在 v4 API 删除事件时,其下所有评论同样会被标记为DELETED(见 incidents/[incident_id]/+server.ts)。
更新的可见性状态(Update Status)
每条更新带有一个status字段用于控制可见性:
ACTIVE(默认)
更新在公开状态页与事件时间线上可见。这是默认状态,数据库列默认值为ACTIVE。
INACTIVE
更新对公众隐藏,但仍保留在数据库中。
典型使用场景:
- 面向内部、不打算给用户看的备注;
- 可能包含敏感信息的更新;
- 草稿性质的更新;
- 历史归档记录。
如何设置:目前只能通过 API 调用管理(UpdateCommentStatusByID,见 incidentController.ts),仪表盘 UI 支持未来可能补充。
除文档提到的 ACTIVE / INACTIVE 外,源码中还存在第三种状态DELETED(软删除标记),前述 v4 API 的删除即写入该值。公开页面与公共 API 在拉取评论时只返回status = "ACTIVE"的记录——数据仓库层getActiveIncidentComments通过andWhere("status", "ACTIVE")过滤(见 repositories/incidents.ts),控制器层GetIncidentActiveComments亦只返回活动评论(见 incidentController.ts)。
时间线展示
在公开状态页上,更新按以下方式展示:
- 排序:最新在前(时间倒序)。底层查询以
commented_at降序排列,getActiveIncidentComments还会以id降序作为次级排序(见 repositories/incidents.ts)。 - 展示信息:
- 状态徽章(按状态着色);
- 时间戳;
- 消息内容(渲染后的 Markdown)。
- 样式:
- 每条更新独立成卡片;
- 使用与状态匹配的颜色;
- 清晰的视觉分隔。
前端实现位于事件详情页 incidents/[incident_id]/+page.svelte 与首页组件 IncidentItem.svelte,状态徽章通过text-{comment.state.toLowerCase()}绑定状态色。
更新通知(订阅系统)
当配置了订阅系统(详见 订阅文档/docs/content/v4/subscriptions.md))后:
订阅者会收到:
- 新更新的邮件通知;
- 邮件主题包含事件 ID 与状态,格式为
[#${incident.id}:${comment.state}] ${incident.title}; - 更新消息的 HTML 渲染正文;
- 指向完整事件页面的链接(
cta_url指向/incidents/{incident.id},按钮文案 "View Incident")。
通知触发的时机:
- 新增更新;
- 状态变化;
- 事件创建(伴随初始更新);
- 事件解决。
底层实现:AddIncidentComment在写入评论并同步事件状态后调用notifySubscribersOfComment,将title、cta_url、cta_text、update_text(mdToHTML渲染结果)、update_subject、update_id、event_type: "incidents"等变量推入subscriberQueue;每个评论使用稳定的去重 IDsubscriber-incidents-${comment.id},确保队列重试或重复推送时订阅者只会收到一次通知(见 incidentController.ts)。
更新最佳实践
更新频率
关键事件(Critical):
- 每 15–30 分钟更新一次;
- 即使没有进展也要更新(例如 "Still investigating...");
- 保持用户知情。
重大事件(Major):
- 每 30–60 分钟更新一次;
- 在有显著进展时更新;
- 在状态转换时更新。
轻微事件(Minor):
- 状态变化时更新;
- 应用修复时更新;
- 解决时更新。
内容指南
要清晰(Be Clear):使用简单的语言;避免行话和缩写;必要时解释技术术语。
要具体(Be Specific):说明什么受影响,而不只是你在做什么;说明用户会看到什么;给出可能的预估时间。
要诚实(Be Honest):存在不确定性时如实承认;不要承诺做不到的事;预估变化时及时更新。
好的示例:
## Investigating (Good) We're seeing 20% of API requests failing with 503 errors. Our team is investigating the cause. Retries should succeed, but you may experience delays.## Identified (Good) We've identified a database replication lag as the cause. The secondary database is catching up. ETA for full resolution: 15 minutes.## Monitoring (Good) Database replication is back in sync. Error rates have dropped to normal levels. We're monitoring for the next 20 minutes to ensure stability before resolving.## Resolved (Good) All systems are operating normally. Total incident duration: 47 minutes. **Root Cause:** Database replication lag due to a large batch import. **Prevention:** We've implemented rate limiting on batch imports and improved monitoring.反面示例:
## Bad - Too Vague We're working on it.## Bad - Too Technical Increased innodb_buffer_pool_size and optimized query Q47392.## Bad - Unprofessional Really sorry about this mess! Not sure what happened.状态推进
如无必要,不要跳级:
- 帮助用户理解进展;
- 提供更细致的时间线;
- 透明度更高。
允许跳级的情形:
- INVESTIGATING → RESOLVED(快速修复);
- IDENTIFIED → RESOLVED(无需观察期)。
状态可以回退:
- MONITORING → INVESTIGATING(问题复现);
- RESOLVED → INVESTIGATING(事件重新打开);
- 复杂事件的正常情况。
从源码看,状态回退是显式支持的:事件已处于 RESOLVED 时,任何非 RESOLVED 状态的新更新都会调用db.setIncidentEndTimeToNull清空结束时间(见 incidentController.ts),事件在状态页重新显示为进行中。
Markdown 使用建议
善用结构:
## Summary Brief overview of current status **Impact:** - What's affected - Who's affected **Next Steps:** - What we're doing - Expected timeline善用列表:便于扫读;行动项更清晰;可读性更好。
用加粗突出重点:突出重要信息;吸引用户注意力;但不要过度使用。
链接到资源:指向相关监控的状态页;指向临时规避方案的文档;指向支持渠道。
特殊使用场景
回溯补记更新(Backdating)
如果是在事后创建更新:
- 添加更新;
- 将时间戳调整到实际发生时间;
- 保持时间线准确;
- 保留历史事实。
示例:问题发生在上午 10:00,但你在下午 2:00 才补录。将各条更新的时间戳分别设为 10:00 AM、10:30 AM 等。
一次补录多条更新
对于进展密集的复杂事件:
- 按时间顺序添加更新;
- 调整时间戳使其合理分布;
- 确保状态推进符合逻辑;
- 最近一条更新决定当前状态。
解决类更新(Resolution Updates)
解决事件时,建议包含以下内容:
摘要(Summary):修复了什么;解决验证情况;置信度。
根因(Root Cause):事件由什么引起;为什么发生;技术细节(可选)。
预防(Prevention):防止复发的措施;监控改进;流程变更。
完整示例:
All services are fully operational. The incident has been resolved. **Root Cause:** A misconfigured load balancer was routing traffic to unhealthy backend servers. **Resolution:** - Fixed load balancer health check configuration - Restarted affected backend servers - Verified traffic routing correctly **Prevention:** - Added health check validation to deployment pipeline - Implemented automated health check monitoring - Updated runbooks for faster diagnosis **Total Duration:** 1 hour 15 minutes值得一提的是,Kener 的自动生成事件(告警驱动)同样以结构化 Markdown 更新为输出:事件创建时IncidentCreateAlertMarkdown会生成包含 Monitor Name、Monitor Tag、Incident Status、Severity、Alert Type、Alert Value、Failure Threshold 等字段的 Markdown 表格;告警解除时ClosureCommentAlertMarkdown会追加Total duration: X minutes与完整告警详情表格(见 incidentController.ts)。这是理解"自动生成的事件如何维护时间线"的最佳源码参考。
下一步
- 事件对监控状态的影响/docs/content/v4/incidents/impact-on-monitoring.md) —— 事件状态如何影响监控状态展示
- 创建与管理事件/docs/content/v4/incidents/creating-managing.md) —— 回到事件管理基础
- 自动生成的事件/docs/content/v4/incidents/auto-generated.md) —— 告警如何自动创建并更新事件
- 可观测性
- 后端
- 前端
- 运维
【免费下载链接】kener
Stunning status pages, batteries included!
相关推荐
开源RGB控制的技术哲学:从厂商锁定到硬件民主化
开源RGB控制的技术哲学:从厂商锁定到硬件民主化 在当今的计算机硬件生态中,RGB灯光控制呈现出一个悖论:硬件制造商追求差异化的同时,却为用户创造了碎片化的软件
桌面应用硬件开发智能硬件NetBox Notification 模型深度解析:订阅与事件规则驱动的用户通知机制
NetBox Notification 模型深度解析:订阅与事件规则驱动的用户通知机制 导读 NetBox 内置了一套面向用户的站内通知系统,用于在对象发生变更
后端网络数据建模wp-calypso 推送通知状态管理:Redux 状态机与 Service Worker 订阅流程深度解析
wp calypso 推送通知状态管理:Redux 状态机与 Service Worker 订阅流程深度解析 导读 本文以 wp calypso 仓库中的 pu
前端CMS
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考