告警表达式语法
夜莺 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 不易表达的逻辑:复合关系判定、字符串匹配、值范围等。
工作机制:基于标签的对齐
表达式的本质是时序数据之间的运算。运算逻辑:在相同 Label 集合的数据之间做计算,不同 Label 集合的数据无法直接运算。
时序数据格式:<metric name>{<label name>=<label value>, …} value
在告警规则的"查询条件"里有两个关键字段配合:
| 字段 | 作用 |
|---|---|
| 值字段 | 指定查询结果的哪一列作为运算"值"(相当于 Metric Name) |
| 标签字段 | 指定查询结果的哪些列作为标签 — 决定 series 怎么对齐 |
举例:MySQL 查询返回结果:
| product_name | price |
|---|---|
| apple | 100 |
| banana | 50 |
设置 值字段 = price、标签字段 = product_name,会被识别成时序:
$A.price{product_name=apple} 100
$A.price{product_name=banana} 50
如果再有 $B 查询返回 cost 字段,只有 product_name 标签也对齐的 series才会和 $A.price 做运算($A.price - $B.cost)。
标签不一致时用"字段重命名"统一两侧的标签 key/value。
运算符全集
算术运算符
| 符号 | 说明 | 示例 |
|---|---|---|
+ |
相加 | $A + $B |
- |
相减 | $A - $B |
* |
相乘 | $A * $B |
/ |
相除 | $A / $B |
( ) |
优先级 | ($A - $B) * $C |
关系运算符(产出布尔,告警判定的核心)
| 符号 | 说明 | 示例 |
|---|---|---|
== |
等于 | $A == 0 |
!= |
不等于 | $A.count != 0 |
> |
大于 | $A > $B |
< |
小于 | $A < 0 |
>= |
大于等于 | $A >= 100 |
<= |
小于等于 | $A <= $B |
逻辑运算符
| 符号 | 说明 | 示例 |
|---|---|---|
&& |
与 — 两边都 true 才 true | $A > 0 && $B < 10 |
|| |
或 — 任一边 true 即 true | $A + $B > 20 || $A * $B < 50 |
集合运算
| 符号 | 说明 | 示例 |
|---|---|---|
in |
元素是否在数组里 | $A in ["admin", "moderator"] |
not in |
元素不在数组里 | $A not in [1, 2, 3] |
字符串包含
| 符号 | 说明 | 示例 |
|---|---|---|
contains |
字符串包含 | $A contains $B,$A=“hello world” $B=“world” → true |
not contains |
字符串不包含 | 同上反逻辑 |
正则匹配
| 符号 | 说明 | 示例 |
|---|---|---|
matches |
字符串是否匹配正则 | $A matches "^[0-9]+$",$A=“123” → true |
值范围
| 符号 | 说明 | 示例 |
|---|---|---|
between |
在区间内(闭区间) | between($A, [100, 200]),$A=155 → true |
not between |
不在区间内 | not between($A, [100, 200]) |
注意:
between适用数值类型(int / float),不支持字符串。
时间戳运算
| 表达式 | 说明 | 示例 |
|---|---|---|
now().Unix() |
当前 Unix 秒数 | now().Unix() - $A.timestamp > 60(超过 60s 未更新) |
时间戳仅支持加减运算,不能乘除。
实操:常用告警表达式
| 场景 | 表达式 |
|---|---|
| QPS 高 + 错误率高 | $A.qps > 1000 && $B.error_rate > 0.05 |
| 失败率超过阈值 | $B.failed / ($A.total + $B.failed) * 100 > 5 |
| 数据 60s 未更新(异常停止) | now().Unix() - $A.last_update_ts > 60 |
| 状态属于异常集合 | $A.status in ["FAILED", "TIMEOUT", "ERROR"] |
| 订单金额超出区间 | between($A.amount, [10000, 1000000]) == false |
| 多条件联合(订单已取消且金额大) | $A.status == "Order Canceled" && $A.total_amount > 100 |
更详细的 SQL 类业务告警场景见 MySQL 告警举例。
常见问题
Q1:表达式可以引用几个查询?
A:理论上没硬上限,但建议 ≤ 3 个($A / $B / $C)。查询数越多对齐越复杂、表达式越难维护、性能开销也越大。
Q2:两个查询的标签不一致,表达式不生效,怎么办?
A:常见对齐技巧:
- 在查询本身的"标签字段"里对齐字段名(重命名字段统一);
- 用聚合把不需要的标签去掉(如 PromQL 用
sum without (instance)); - 检查返回数据的实际标签集合(数据预览功能可看)。
Q3:== 比浮点数为什么经常不准?
A:浮点比较的常见陷阱。例如 0.1 + 0.2 == 0.3 在大多数语言里是 false。建议用:
- 范围判定:
between($A, [0.299, 0.301]); - 差值阈值:
$A - 0.3 < 0.001 && $A - 0.3 > -0.001。
Q4:表达式里能调用自定义函数吗?
A:目前不支持用户定义函数。内置的运算符 + 函数(如 between、now()、contains、matches)覆盖绝大多数场景。需要复杂逻辑建议在数据源那边(SQL/PromQL)就把结果算好,表达式里只做布尔判定。