news 2026/9/14 23:16:49

Nightingale(n9e)告警订阅规则(alert_subscribe)实战指南:跨团队抄送、告警升级与排障

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Nightingale(n9e)告警订阅规则(alert_subscribe)实战指南:跨团队抄送、告警升级与排障

Nightingale(n9e)告警订阅规则(alert_subscribe)实战指南:跨团队抄送、告警升级与排障

【免费下载链接】nightingaleNightingale is to monitoring and alerting what Grafana is to visualization.项目地址: https://gitcode.com/GitHub_Trending/ni/nightingale

Nightingale 的告警订阅(alert_subscribe)是在通知阶段对告警事件做"复制 + 二次路由"的机制:它按条件筛选事件、克隆一份副本,并交给关联的通知规则继续派发,从而实现跨团队抄送(CC)、告警升级(N 分钟未处理再通知他人)、跨业务组收告警、把零散规则的事件聚合到统一出口等典型场景。本文基于仓库内 SKILL.md 及其配套的 reference.md、troubleshooting.md、http-api.md,并结合引擎源码与数据模型,完整讲解订阅规则的配置字段、新旧两版通知路由、五个可落地的配置示例、创建/编辑/排障工作流以及底层实现原理,读完即可在 n9e 中独立完成订阅规则的搭建与问题排查。

一、先建立正确的"心智模型":订阅发生在通知阶段

订阅规则不是一个独立的告警源,它依附在已有告警事件之上,属于通知阶段的能力。在 alert/dispatch/dispatch.go 中,Dispatch在完成事件本身的派发后,会调用handleSubs处理订阅:

  • 原始事件照常走它自己的通知链路,不受订阅影响;
  • 每一条匹配的订阅都会克隆一份事件副本,按订阅配置改写后,再走一遍通知链路(dispatch.go#L723-L729 的if !isSubscribe { e.handleSubs(event) }体现了这一点);
  • 因此订阅是加法语义:不拦截、不替换原始通知。如果同一个人同时被配在两条链路上,会收到两遍,这是设计使然。

从引擎匹配链(dispatch.go#L752-L789 的subMatches)可以确认,订阅的匹配条件是全部 AND,并按固定顺序检查:enabled → datasource → prod → cate → tags → 业务组名 → 持续时长 → 严重级别,任何一个门不通过,该订阅即被跳过。

还有三个容易被误解的点,先明确下来:

  • group_id只是管理归属(权限),不参与事件匹配。订阅天然可以接收跨业务组的事件;想"只订阅某个业务组的事件",要使用busi_groups过滤条件。
  • 新版路由(notify_version=1:克隆事件的出口被改写为订阅指定的notify_rule_ids;克隆事件的callbacks 默认被清空(见 models/alert_subscribe.go#L450-L477 的ModifyEvent),防止再次命中原规则的回调。
  • 配置变更最长 9 秒内生效:内存缓存轮询周期为 9 秒。在 memsto/alert_subscribe_cache.go#L110-L118 的loopSyncAlertSubscribes中可以看到duration := time.Duration(9000) * time.Millisecond的轮询实现,无需重启服务。

二、核心配置结构:一个 JSON 对象说清楚

订阅规则的配置是一个单一 JSON 对象(不是数组),核心结构如下:

{ "name": "Subscription rule name", "note": "Notes", "disabled": 0, "prod": "", "cate": "", "datasource_ids": [], "cluster": "0", "rule_ids": [], "severities": [1, 2, 3], "for_duration": 0, "tags": [], "busi_groups": [], "extra_config": {}, "notify_version": 1, "notify_rule_ids": [] }

逐字段的完整说明见 reference.md,核心要点如下:

  • severities必填:新旧版本校验都会强制要求非空(models/alert_subscribe.go#L116-L118 中if len(s.SeveritiesJson) == 0 { return errors.New("severities is required") });[1,2,3]表示全部严重级别。
  • 新版必须notify_version=1+ 非空notify_rule_ids:先用list_notify_rules拿通知规则 ID;若还没有合适的通知规则,先通过create_notify_rule创建再回来。注意:版本 1 下,旧版的 redefine 类字段(redefine_severity/redefine_channels/webhooks等)会被校验主动清空(见下文新旧版本对比)。
  • rule_ids为空 = 订阅所有告警规则的事件;只想订阅特定规则时,用list_alert_rules拿 ID。
  • for_duration(秒)= 仅当告警持续时长超过该值时转发,是"告警升级"的开关;0= 不限。
  • 多个tags/busi_groups条目之间全部是 ANDbusi_groups匹配的是事件的业务组名称(key 约定写成"groups",实际匹配由 func/value 驱动)。
  • prod/cate参与匹配但语义较弱prod非空时为精确匹配;cate只有填"host"时才有真实过滤效果,其它值等价于不过滤。拿不准时两者都留空字符串。
  • datasource_ids空数组 = 所有数据源cluster固定为"0"(V5 遗留字段)。

三、过滤条件的完整字段表

tags/busi_groups的元素结构统一为:

{ "key": "tag name", "func": "match operator", "value": "match value" }

支持的运算符(func):

func含义value 示例
==精确匹配"web01"
!=不相等"web01"
=~正则匹配"web.*"
!~正则不匹配"web.*"
in在列表中(空格分隔"web01 web02 web03"
not in不在列表中(空格分隔)"web01 web02"

常用 tag:ident(机器标识)、rulename(告警规则名)、__name__(指标名)以及自定义业务标签。

严重级别取值:

含义
1一级告警(Critical)
2二级告警(Warning)
3三级告警(Info)

完整字段表(数据模型见 models/alert_subscribe.go 的AlertSubscribe):

字段类型必填说明
namestring订阅规则名
notestring备注
disabledint0=启用(默认),1=禁用。禁用的订阅在缓存层直接被过滤,完全不参与匹配
group_idint64管理归属业务组(决定谁能查看/编辑);不参与事件匹配
prodstring产品类型,非空时对事件的 RuleProd精确匹配;空 = 不过滤。常见值"metric"/"logging"/"host"
catestring数据源类型,当前实现只有"host"有真实过滤效果;空 = 不过滤
datasource_idsint[]数据源 ID 列表;空数组或含0= 全部(工具会自动归一化为[0]);事件无数据源(dsId=0)时跳过此过滤
clusterstring恒为"0"(V5 遗留字段)
rule_idsint[]订阅的告警规则 ID 列表;空 = 订阅所有告警规则的事件(全局订阅)
severitiesint[]订阅的严重级别,新旧版本均校验非空;[1,2,3]= 全部
for_durationint64秒。仅当告警持续时长(trigger_time - first_trigger_time超过该值才转发——用于告警升级(如300= 持续 5 分钟未恢复才转发);0= 不限
tagsarray事件标签过滤,多条 AND
busi_groupsarray按事件的业务组名称过滤,多条 AND;元素{"key":"groups","func":"=~","value":"production.*"},key 按约定写"groups"(实现中 key 不参与匹配,func/value 匹配事件的 GroupName)

四、通知配置:新版 vs 旧版

字段类型说明
notify_versionint1=新版(推荐,通过通知规则转发);0=旧版(直接填用户组 + 渠道)
notify_rule_idsint[]新版必填非空:克隆事件的出口被改写为这些通知规则

新版(notify_version=1)校验会清空所有旧版字段user_group_idsredefine_channels/new_channelsredefine_webhooks/webhooksredefine_severity/new_severity。这一点在 models/alert_subscribe.go#L120-L133 的Verify中有直接实现:版本 1 下这几个字段被逐一置零/置空。换句话说,改写严重级别/渠道是旧版能力;新版要改级别/渠道,请在通知规则层做路由(对应 notify-rule-copilot)。

旧版(notify_version=0)字段,仅在维护存量配置时才会遇到:

字段说明
user_group_ids接收用户组 ID,空格分隔字符串(如"1 2");指定了就必须同时指定new_channels
redefine_severity/new_severity为 1 时,克隆事件的严重级别改为 new_severity
redefine_channels/new_channels为 1 时,克隆事件的通知渠道改为 new_channels(空格分隔)
redefine_webhooks/webhooks为 1 时,克隆事件的回调改为 webhooks(JSON 数组);未启用时克隆事件的 callbacks 会被清空(防止再次命中原始回调)——这条对新版同样适用
extra_config扩展配置 JSON 对象,引擎没有固定用途,照抄{}即可

五、五个可直接落地的完整示例

示例 1:订阅所有 Critical 告警并转发到值班通知规则(最常见)

{ "name": "Subscribe to all Critical alerts", "note": "CC all critical alerts to the on-call chain", "disabled": 0, "prod": "", "cate": "", "datasource_ids": [], "cluster": "0", "rule_ids": [], "severities": [1], "for_duration": 0, "tags": [], "busi_groups": [], "extra_config": {}, "notify_version": 1, "notify_rule_ids": [1] }

示例 2:按标签订阅特定机器组的告警

{ "name": "Subscribe to web cluster alerts", "note": "CC alerts from all web-prefixed machines to the web team", "disabled": 0, "prod": "metric", "cate": "", "datasource_ids": [], "cluster": "0", "rule_ids": [], "severities": [1, 2, 3], "for_duration": 0, "tags": [ {"key": "ident", "func": "=~", "value": "web.*"} ], "busi_groups": [], "extra_config": {}, "notify_version": 1, "notify_rule_ids": [1] }

示例 3:告警升级——指定规则持续 5 分钟未恢复后通知二线

{ "name": "CPU alert escalation", "note": "CPU-related alerts that persist unrecovered for 5 minutes, escalate to second-line", "disabled": 0, "prod": "metric", "cate": "", "datasource_ids": [1], "cluster": "0", "rule_ids": [10, 11], "severities": [1, 2], "for_duration": 300, "tags": [], "busi_groups": [], "extra_config": {}, "notify_version": 1, "notify_rule_ids": [2] }

示例 4:跨业务组订阅——只接收 "production" 业务组下的数据库告警

{ "name": "Subscribe to production database alerts", "note": "CC database-related alerts under the production business group to the DBA", "disabled": 0, "prod": "metric", "cate": "", "datasource_ids": [], "cluster": "0", "rule_ids": [], "severities": [1, 2], "for_duration": 0, "tags": [ {"key": "rulename", "func": "=~", "value": ".*database.*|.*MySQL.*|.*Redis.*"} ], "busi_groups": [ {"key": "groups", "func": "=~", "value": "production.*"} ], "extra_config": {}, "notify_version": 1, "notify_rule_ids": [2, 3] }

示例 5:聚合告警事件,统一转发到工单系统

{ "name": "Forward alerts to the ticketing system", "note": "Warning-and-above alerts are uniformly forwarded to the internal ticketing system via a notification rule", "disabled": 0, "prod": "", "cate": "", "datasource_ids": [], "cluster": "0", "rule_ids": [], "severities": [1, 2], "for_duration": 0, "tags": [], "busi_groups": [], "extra_config": {}, "notify_version": 1, "notify_rule_ids": [5] }

六、工作流一:创建订阅规则

  1. 确定业务组(管理归属):用list_busi_groupsgroup_id。如果用户已点名业务组,或前端已弹出业务组表单,直接使用其 ID,不必再问。
  2. 确定关联通知规则:用list_notify_rulesnotify_rule_ids(新版路由的通知出口)。
  3. (可选)限定订阅范围:只想订阅某些告警规则,用list_alert_rulesrule_ids;想按标签/业务组/严重级别/时长收窄,填对应过滤字段。
  4. 调用create_alert_subscribe:把第 1 步的业务组作为group_id传入(也可以放进 config 里),config单个 JSON 对象字符串(不是数组)。如果省略group_id,工具会自动弹出业务组选择表单,用户选完后恢复本次创建。
  5. 汇报结果:工具返回{id, name, group_id, disabled, notify_rule_ids, url},简要汇报订阅条件和通知出口即可;把规则名以内链形式展示:<name>(url 是返回的/alert-subscribes/edit/<id>),用户可直接点击进入配置页核对。

七、工作流二:编辑与排障

  1. list_alert_subscribes/get_alert_subscribe_detail拿到规则 ID 和当前状态,确认要改什么。
  2. 调用update_alert_subscribe基于提案(proposal):调用后立即向用户展示变更清单并暂停,用户确认后系统自动持久化——确认步骤不经过你):id必填,config只含要改的字段(增量补丁:未指定的字段保持原值;数组字段如 tags/severities/rule_ids/notify_rule_ids/busi_groups/datasource_ids提供即整体替换——先拿 detail 里的现有数组,在其基础上构造完整的新数组再传入)。常见操作:
    • 临时禁用= 传 config{"disabled":1}(缓存层直接过滤,立即完全失效);恢复 ={"disabled":0}
    • 调整升级阈值={"for_duration":600}切换通知出口={"notify_rule_ids":[...]}(先用list_notify_rules确认 ID);
    • 业务组(管理归属)不可变更;删除没有站内工具——让用户在 UI 完成(告警管理 → 订阅规则)。
  3. 当用户说"订阅了但没收到任何东西"时,按 troubleshooting.md 中的链路逐门检查,并主动指出最可能失败的门(常见:for_duration 设太大、busi_groups 名称对不上、notify_rule_ids 指向的通知规则本身配置不对);确认是某个门的配置问题后,可直接用update_alert_subscribe修复。

八、排障链路:"订阅了但没收到任何东西"

引擎匹配链在 alert/dispatch/dispatch.go 的handleSub/subMatches中按以下顺序求值,任何一个门失败即跳过该订阅,请按序检查:

#检查项常见失败原因
0缓存同步刚改完规则,内存缓存最多滞后9 秒
1disabled规则被禁用(disabled=1,缓存层直接过滤)
2数据源datasource_ids不是"全部"且事件datasource_id不在列表中
3prodprod非空且不等于事件的 RuleProd(注意是精确匹配)
4cate填了"host"但事件不是主机类型(其它值不会导致不匹配)
5标签多条tags是 AND;in值写成逗号分隔会导致不匹配(必须空格分隔)
6业务组名称busi_groups匹配事件的业务组名称——按名称硬绑的条件在业务组改名后会失去目标;正则没覆盖全名也会失配
7时长for_duration大于事件已持续时长(trigger_time - first_trigger_time)——刚触发的告警必然不匹配,必须等持续足够长后的下一轮通知周期
8严重级别severities未包含事件的严重级别
9下游出口以上全过、克隆事件已产生,但notify_rule_ids指向的通知规则本身配置不对(渠道/接收人/适用属性不匹配)→ 转交 notify-rule-copilot 排障

另外先确认源头:原始告警事件必须真的产生——订阅不会凭空变出事件;如果事件被静默(在求值阶段被拦截),它根本到不了订阅这一步。

九、行为语义速查(回答用户问题时可引用)

行为语义
订阅会替换原始通知吗不会。原始事件照常走自己的通知链路;订阅克隆一份副本额外转发。如果同一个人被配在两条链路上会收到两遍——这是设计使然
会再次命中回调(webhooks)吗不会。克隆事件的 callbacks默认清空(models/alert_subscribe.go#L460-L467 的ModifyEvent);只有旧版显式redefine_webhooks=1时才携带订阅自己的 webhooks
订阅范围受 group_id 限制吗不受。group_id只是管理归属(权限);订阅天然接收所有业务组的事件,用busi_groups/rule_ids/tags收窄
for_duration如何生效比较事件的trigger_time - first_trigger_time。事件首次触发时差值为 0 必然不匹配;只有告警反复通知到第 N 次、差值超过阈值后,那次事件的克隆才被转发——所以"升级"依赖告警规则本身配置了重复通知
新版能改写严重级别/渠道吗不能。notify_version=1Verify会清空 redefine_* 字段;请在通知规则层做级别/渠道路由
恢复事件也会被订阅吗走同样的匹配链;恢复通知是否发送取决于下游通知规则的配置(如is_recovered属性过滤)
变更多久生效缓存每 9 秒轮询一次,最多 9 秒,无需重启
怎么判断事件是否被订阅转发克隆事件携带sub_rule_id(订阅规则 ID),在通知记录/事件详情中可见

十、其他常见坑

现象原因处理
创建报severities is required新旧版本都必填至少填[1,2,3]
创建报no notify rules selectednotify_version=1notify_rule_ids为空先用list_notify_rules拿 ID;没有就先创建通知规则
创建报new_channels is required旧版指定了user_group_ids但没填new_channelsnew_channels,或改用新版
保存后 redefine 字段全没了新版 Verify 主动清空旧版字段预期行为,不是数据丢失
升级订阅从不触发告警规则没有配置重复通知(notify_repeat_step=0),事件不会第二次来让用户检查告警规则的重复通知间隔
全局订阅事件量爆炸rule_ids/tags/busi_groups 全空 = 复制所有事件至少加一层过滤;或在下游通知规则收窄
业务组改名后订阅失配busi_groups按名称匹配改用=~正则,或改名时同步订阅

十一、验证方法

  • 站内get_alert_subscribe_detail核对字段;get_notify_rule_detail核对下游出口。
  • HTTP(给用户命令)POST /api/n9e/alert-subscribe/alert-subscribes-tryrun用历史事件 ID + 订阅草稿试运行,显示在哪个步骤匹配失败;新版甚至会执行通知规则的真实测试发送。先试跑、再保存
  • 真实验证:触发一条匹配的告警,然后去"历史告警 → 详情"查看是否出现携带sub_rule_id的克隆事件及其通知记录。

十二、HTTP API(供外部 A2A Agent / 给用户 curl 命令时使用)

站内 AI 助手不得使用这些端点(应使用内置 FC 工具)。认证:Authorization: Bearer <token>。路由定义在 center/router/router.go。

操作方法路径说明
列表(跨业务组)GET/api/n9e/busi-groups/alert-subscribes当前用户可见业务组下的订阅
列表(单业务组)GET/api/n9e/busi-group/:id/alert-subscribes
详情GET/api/n9e/alert-subscribe/:sid
创建POST/api/n9e/busi-group/:id/alert-subscribesBody 为单个AlertSubscribe JSON 对象;group_id 取自 URL
更新PUT/api/n9e/busi-group/:id/alert-subscribesBody 为数组[{...}](与创建相反);按显式字段列表更新,但仍建议先 GET 详情、改完整对象再 PUT
删除DELETE/api/n9e/busi-group/:id/alert-subscribesBody:{"ids":[1,2,3]}
试跑POST/api/n9e/alert-subscribe/alert-subscribes-tryrunBody:{"event_id":<历史事件 ID>,"config":{...订阅草稿...}};逐门校验匹配,新版还会执行通知规则的真实测试发送

权限:创建/更新/删除需要业务组读写权限(bgrw)+ 对应的/alert-subscribes/*菜单权限;列表只需只读权限。

直接改库(最后手段):表alert_subscribe,其中tags/busi_groups/webhooks/extra_config/notify_rule_ids等是 JSON/序列化字段;内存缓存约 9 秒自动重载,无需重启;改前先备份。

十三、源码级原理:一次订阅转发的完整链路

把文档中的流程落到源码上,订阅的完整生命周期是:

  1. 求值产出事件:告警规则求值产生AlertCurEvent,进入 alert/dispatch/dispatch.go 的Dispatch派发流程。
  2. 原始通知照常发送HandleEventNotifyNotifyGroupDispatchGlobalWebhookDispatchEventCallbacksDispatch等 handler 合并出通知目标并go e.Send(...)异步发送(dispatch.go#L706-L728)。
  3. 收集订阅:非订阅事件会调用handleSubs,通过collectSubscribesalertSubscribeCache取出"规则维度订阅"(key=RuleId)和"全局订阅"(key=0)两类候选(dispatch.go#L738-L750)。
  4. 逐门匹配subMatches依次检查IsDisabled → MatchCluster(数据源) → MatchProd → MatchCate → MatchTags → MatchGroupsName → ForDuration 时长 → SeveritiesJson 严重级别,全过才继续(dispatch.go#L752-L789)。
  5. 克隆与改写handleSub值传递拷贝事件副本(避免污染原事件),调用sub.ModifyEvent(&event)改写副本(清空 callbacks、写入 notify_rule_ids / notify_groups 等),打上event.SubRuleId = sub.Id标记,记录 subscribe 日志后再次进入HandleEventNotify(&event, true)走通知链路(dispatch.go#L791-L803)。isSubscribe=true时 handler 集合只剩NotifyGroupDispatchEventCallbacksDispatch(dispatch.go#L706-L710)。
  6. 缓存热加载:订阅表由 memsto/alert_subscribe_cache.go 的SyncAlertSubscribes启动,后台loopSyncAlertSubscribes每 9 秒全量同步一次(先查统计、有变化才重载),因此配置改动最长 9 秒生效、无需重启。

理解这条链路后,"订阅只是通知阶段的加法路由"这一心智模型就有了代码级支撑:它不干预告警求值,只在事件已经产生之后做"复制 + 二次派发",这也是为什么升级、跨组抄送、聚合转发这些场景都由它承载。

【免费下载链接】nightingaleNightingale is to monitoring and alerting what Grafana is to visualization.项目地址: https://gitcode.com/GitHub_Trending/ni/nightingale

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/14 23:16:13

GUI多线程时间显示:Tkinter与Java Swing实现与踩坑记录

接手这个练习的时候&#xff0c;我先盯着需求看了两遍。第一行显示学号、姓名&#xff0c;第二行显示带星期的完整时间&#xff0c;并且每隔1秒自动刷新一次&#xff0c;典型的图形用户界面加多线程练手题目。很多同学一看到“多线程”三个字就紧张&#xff0c;实际上这个需求真…

作者头像 李华
网站建设 2026/9/14 23:14:58

如何做好缺陷根因分析(RCA):从“救火”到“防火”的完整指南

摘要&#xff1a;缺陷根因分析&#xff08;Root Cause Analysis&#xff0c;RCA&#xff09;是测试团队从“执行层”走向“质量运营”的分水岭。本文系统讲解 RCA 的标准流程、5 Why 分析法、鱼骨图、故障树等工具的正确用法&#xff0c;结合一线大厂实战案例&#xff0c;拆解常…

作者头像 李华
网站建设 2026/9/14 23:14:39

沈阳网站建设tlmh报价揭秘:3个步骤避开5000元溢价陷阱

沈阳网站建设tlmh报价揭秘:3个步骤避开5000元溢价陷阱 在沈阳做网站建设,最怕的就是被坑。刚问完报价,对方张口就是“高端定制”,转头一看,功能跟你之前花两三千块做的模板站没两样,甚至还没人家稳。很多老板心里都嘀咕: 沈阳网站建设tlmh到底多少钱…

作者头像 李华
网站建设 2026/9/14 23:12:55

Grafana API Token获取与安全管理指南

1. Grafana API Token 获取方法详解在监控和可视化领域&#xff0c;Grafana 作为业界领先的开源工具&#xff0c;其 API 功能为自动化运维提供了极大便利。而获取有效的 API Token 则是调用这些接口的首要步骤。本文将详细介绍三种主流获取方式及其适用场景。1.1 服务账户 Toke…

作者头像 李华
网站建设 2026/9/14 23:10:27

PDF盖章和盖骑缝章工具(PDF盖章助手)

链接&#xff1a;https://pan.quark.cn/s/d17ad2c23cd3【软件介绍】&#xff1a;PDF 盖章和盖骑缝章工具 v1.1 是一款轻巧实用的本地离线 PDF 电子图章添加与多页骑缝章排版生成利器。软件全面支持透明 PNG 印章导入、正片叠底自然透字融合、可视化拖拽缩放与旋转角度微调&…

作者头像 李华