告警表达式语法

夜莺 v9 告警表达式语法:用 `$A`、`$B` 引用查询结果做算术 / 比较 / 逻辑运算,构造任意业务/SQL/Prometheus 数据源的复合告警条件。

概述

告警表达式是夜莺通用的告警判定语言,可以引用一个或多个数据源查询的结果(用 $A$B…),用算术/关系/逻辑运算符合成布尔表达式,作为告警是否触发的判定。

侧栏路径:告警规则编辑页 → 查询条件区域 → 选用"表达式模式"。

适用场景:

  • 跨查询联合判定$A.qps > 1000 && $B.error_rate > 0.05(QPS 高 + 错误率高);
  • 比率类指标$B.failed_orders / $A.total_orders > 0.1(失败率 > 10%);
  • SQL / 业务库告警:MySQL 数据源查 SQL,按业务字段判定(详见 MySQL 告警举例);
  • PromQL 不易表达的逻辑:复合关系判定、字符串匹配、值范围等。

核心概念:值字段是数值,标签字段是字符串

这是理解整套表达式语法的前提,先讲清楚再看运算符会顺很多。

表达式的本质是时序数据之间的运算。查询结果(无论来自 Prometheus、MySQL 还是其它数据源)都会先映射成统一的时序格式:

<metric name>{<label name>=<label value>, …}  value

映射靠"查询条件"里的两个字段完成,它们直接决定了每一列在表达式里是什么类型

配置字段 在表达式里的类型 引用方式 能做什么
值字段 数值(float64) $A 算术 + - * /、数值比较、between、时间戳运算
标签字段 字符串 $A.<标签名> 字符串相等/字典序比较、containsmatchesin

一句话记住值字段会被当成数值,只有标签字段才是字符串。

所以"我想对某列做什么运算"反过来决定了"该把这列配在哪":

  • 要做数值比较 / 算术(如 > 1000、求比率)→ 放值字段
  • 要做字符串判定(如等于 "FAILED"、匹配正则、比时间字符串)→ 放标签字段

如果把字符串列(如 FAILED23:55:00)错放进值字段,引擎按数值解析,要么失败要么变成 0,字符串比较不会生效。

举例:一列既能当值也能当标签

MySQL 查询返回:

product_name price
apple 100
banana 50

值字段 = price标签字段 = product_name,被识别成两条时序:

$A.price{product_name=apple}   100
$A.price{product_name=banana}   50

此时:

  • $A(= price)是数值,可写 $A > 80
  • $A.product_name字符串,可写 $A.product_name == "apple"

基于标签的对齐

运算只在相同 Label 集合的数据之间进行,不同 Label 集合的数据无法直接运算。

如果再有 $B 查询返回 cost 字段,只有 product_name 标签也对齐的 series 才会和 $A.price 做运算($A.price - $B.cost)。标签不一致时用"字段重命名"统一两侧的标签 key/value。

支持的数据类型

承接上一节,表达式引擎支持以下几类值,关系运算两侧类型必须一致

类型 来源举例 可用运算
数值(int / float) 值字段 $A、数字字面量 10now().Unix() 算术 + - * /、关系 > < >= <= == !=between
字符串 标签值 $A.host、带引号字面量 "FAILED" == != > <(字典序)、containsmatchesin
布尔 关系/逻辑运算的结果 && || !
数组 字面量 [1, 2, 3]["a", "b"] innot in
日期 / 时间 now()date("2025-01-01")duration("1h") 比较大小、相减得时长、取 .Hour() 等(见下文)

没有"自动类型转换":数值和字符串不能直接比较。例如 $A > "23:55:00"$A 是数值、右侧是字符串,会因类型不匹配判定失败。需要时用 int() / float() / string() / date() 显式转换。

运算符全集

算术运算符(数值)

符号 说明 示例
+ 相加 $A + $B
- 相减 $A - $B
* 相乘 $A * $B
/ 相除 $A / $B
( ) 优先级 ($A - $B) * $C

关系运算符(产出布尔,告警判定的核心)

符号 说明 示例
== 等于 $A == 0
!= 不等于 $A.count != 0
> 大于 $A > $B
< 小于 $A < 0
>= 大于等于 $A >= 100
<= 小于等于 $A <= $B

两侧类型必须一致:值(如 $A)是数值,只能和数值比较;标签(如 $A.host)是字符串,只能和带引号的字符串字面量比较。

字符串(标签值)的比较

前提:要比较的列必须配成标签字段(参见 核心概念)。放在值字段的列是数值,无法做字符串比较。

标签值是字符串,可以和带双引号的字符串字面量做比较。常用于「按状态/时间字符串判定」——比如标签里存的是状态码,或 HH:MM:SS 形式的时间:

表达式 标签值 结果 说明
$A.status == "FAILED" FAILED true 字符串相等
$A.ts == "23:55:00" 23:55:00 true 字符串相等
$A.ts != "23:55:00" 10:00:00 true 字符串不等
$A.ts > "23:55:00" 23:56:00 true 字典序比较
$A.ts < "23:55:00" 10:00:00 true 字典序比较

注意事项:

  • 字面量必须加双引号$A.ts > 23:55:00(不加引号)会被解析为非法语法(冒号被当成运算符)而报错,整条表达式判定失败。
  • > / < 比较的是字典序,不是时间大小。只有当时间是定宽零填充HH:MM:SS 格式时,字典序才与时间先后一致。如果格式不规整(如 9:05:00 未补零),"9:05:00" > "23:55:00" 字典序为 true、时间上却为 false——务必保证格式统一补零;或用 date($A.ts) 解析后再比较(见 日期 / 时间运算)。

逻辑运算符

符号 说明 示例
&& 与 — 两边都 true 才 true $A > 0 && $B < 10
|| 或 — 任一边 true 即 true $A + $B > 20 || $A * $B < 50

集合运算

符号 说明 示例
in 元素是否在数组里 $A.status in ["admin", "moderator"]
not in 元素不在数组里 $A not in [1, 2, 3]

字符串包含 / 正则(标签值)

符号 说明 示例
contains 字符串包含 $A.msg contains "error" → true
not contains 字符串不包含 同上反逻辑
matches 字符串是否匹配正则 $A.code matches "^[0-9]+$"$A.code="123" → true

值范围(数值)

符号 说明 示例
between 在区间内(闭区间) between($A, [100, 200])$A=155 → true
not between 不在区间内 not between($A, [100, 200])

注意between 适用数值类型(int / float),不支持字符串。

日期 / 时间运算

引擎内置日期/时间类型,可用以下函数构造,再做比较或相减:

函数 / 方法 说明 示例
now() 当前时间(时间对象) now().Unix()now().Hour()
now().Unix() 当前 Unix 秒数(数值) now().Unix() - $A.timestamp > 60(超过 60s 未更新)
date("...") 解析时间字符串为时间对象,支持 2025-01-012025-01-01T12:00:00Z date($A.d) > date("2025-01-01")
duration("...") 解析时长,支持 s/m/h now() - $A.start_ts > duration("1h")
.Hour() / .Minute() / .Unix() / .Sub() 时间对象的方法 now().Hour() >= 22(22 点后)

常见用法:

场景 表达式
数据超过 60s 未更新 now().Unix() - $A.last_update_ts > 60
标签里的日期早于某天 date($A.d) < date("2025-01-01")
两个时间相差超过 24 小时 now() - date($A.created_at) > duration("24h")
当前时刻在某小时之后 now().Hour() >= 22

注意:

  • 时间对象之间可以比较大小、相减(得到时长),但不能乘除。
  • 标签里的时间/日期是字符串,要先用 date(...) 解析成时间对象再比较;直接用字符串 > 走的是字典序(见上文「字符串(标签值)的比较」),仅在定宽零填充格式下才与时间一致。
  • 标签里的 Unix 时间戳字符串,用 int($A.ts) 转成数值再和数字比较。

实操:常用告警表达式

场景 表达式 值/标签配置要点
QPS 高 + 错误率高 $A.qps > 1000 && $B.error_rate > 0.05 qps、error_rate 放值字段
失败率超过阈值 $B.failed / ($A.total + $B.failed) * 100 > 5 failed、total 放值字段
数据 60s 未更新(异常停止) now().Unix() - $A.last_update_ts > 60 时间戳放值字段(数值)
状态属于异常集合 $A.status in ["FAILED", "TIMEOUT", "ERROR"] status 放标签字段(字符串)
订单金额超出区间 between($A.amount, [10000, 1000000]) == false amount 放值字段
已取消且金额大 $A.status == "Order Canceled" && $A.total_amount > 100 status 放标签、total_amount 放

更详细的 SQL 类业务告警场景见 MySQL 告警举例

常见问题

Q1:表达式可以引用几个查询?

A:理论上没硬上限,但建议 ≤ 3 个$A / $B / $C)。查询数越多对齐越复杂、表达式越难维护、性能开销也越大。

Q2:为什么字符串比较(如 == "FAILED")不生效?

A:最常见原因是把该列配在了"值字段"——值字段会被当成数值解析,字符串会失败或变成 0。要做字符串判定,必须把该列配成标签字段,再用 $A.<标签名> 引用。参见 核心概念

Q3:两个查询的标签不一致,表达式不生效,怎么办?

A:常见对齐技巧:

  • 在查询本身的"标签字段"里对齐字段名(重命名字段统一);
  • 用聚合把不需要的标签去掉(如 PromQL 用 sum without (instance));
  • 检查返回数据的实际标签集合(数据预览功能可看)。

Q4:== 比浮点数为什么经常不准?

A:浮点比较的常见陷阱。例如 0.1 + 0.2 == 0.3 在大多数语言里是 false。建议用:

  • 范围判定:between($A, [0.299, 0.301])
  • 差值阈值:$A - 0.3 < 0.001 && $A - 0.3 > -0.001

Q5:能不能写 $A > 23:55:00 这样按时间判定?

A:不能直接这么写。23:55:00 不加引号会被当成非法语法(: 被识别为运算符)导致编译失败;即便加引号成 "23:55:00",指标的 $A 是数值,和字符串比较会类型不匹配。两种写法都会让表达式判定失败(且界面无明显报错,仅后端日志记录)。

正确做法有几种:

  • 和标签里的日期/时间比:把该列配成标签字段,先用 date(...) 解析再比较,如 date($A.ts) > date("2025-01-01");若标签是定宽零填充的 HH:MM:SS,也可直接字符串比较 $A.ts > "23:55:00"(字典序,见上文「字符串(标签值)的比较」)。
  • 和当前时刻比:用 now(),如 now().Unix() - $A.ts > 60(值字段是 Unix 时间戳)、now().Hour() >= 22(22 点后)、now() - date($A.created_at) > duration("24h")
  • 限定告警只在某时间段生效:用告警规则的「生效时间」配置,而不是写进表达式。

Q6:表达式里能调用自定义函数吗?

A:目前不支持用户定义函数。内置的运算符 + 函数(如 betweennow()containsmatches)覆盖绝大多数场景。需要复杂逻辑建议在数据源那边(SQL/PromQL)就把结果算好,表达式里只做布尔判定。

参考资料

更新时间 2026-05-20

快猫星云 联系方式 快猫星云 联系方式
快猫星云 联系方式
快猫星云 联系方式
快猫星云 联系方式
快猫星云