2 触发器表达式
概述
触发器中使用的表达式非常灵活。 您可以使用它们来创建有关受监控统计数据的复杂逻辑测试。
一个简单的表达式使用一个应用于监控项并带有某些参数的函数。 该函数返回一个结果,然后使用运算符和常量将其与阈值进行比较。
简单且实用的表达式语法为 function(/host/key,parameter)<operator><constant>。
例如:
min(/Zabbix server/net.if.in[eth0,bytes],5m)>100K
如果在最近五分钟内接收到的字节数始终高于 100 KB,则会触发。
虽然语法完全相同,但从功能角度来看,触发器表达式有两种类型:
- 问题表达式 - 定义问题的条件
- 恢复表达式(可选)- 定义问题解决的附加条件
仅定义问题表达式时,该表达式将同时用作问题阈值和问题恢复阈值。 只要问题表达式的计算结果为 TRUE,就表示存在问题。 只要问题表达式的计算结果为 FALSE,问题就已解决。
当同时定义问题表达式和补充的恢复表达式时,问题解决会变得更复杂:不仅问题表达式必须为 FALSE,而且恢复表达式也必须为 TRUE。 这对于创建 hysteresis 并避免触发器抖动非常有用。
在恢复表达式中使用 {TRIGGER.VALUE} 宏没有意义,因为该表达式仅在触发器处于“Problem”状态时才会被计算。因此,在计算表达式时,{TRIGGER.VALUE} 始终会解析为“1”(表示“Problem”状态)。
函数
函数可用于计算已收集的值(平均值、最小值、最大值、总和)、查找字符串、引用当前时间以及其他因素。
可查看支持的函数完整列表。
通常,函数返回数值以供比较。 当返回字符串时,可以使用 = 和 <> 运算符进行比较(参见示例)。
函数参数
函数参数允许指定:
- 主机和监控项键值(仅引用主机监控项历史数据的函数)
- 函数特定参数
- 其他表达式(对于引用主机监控项历史数据的函数不可用,示例请参见其他表达式)
主机和监控项键值可以指定为 /host/key。
在第一个参数中省略主机名(即如 function(//key,parameter,...) 所示)仅在某些上下文中受支持:
在这些上下文中,您还可以使用 {HOST.HOST} 宏。
在 事件名称 字段和 "触发器" 地图元素中,可以使用 {HOST.HOST<1-9>} 来引用触发器表达式中的特定监控项。
当在这些上下文中省略主机名或将其替换为 {HOST.HOST} 时,引用将指向触发器表达式中的第一个监控项或图形中的第一个监控项。
在这些受支持的上下文之外,在触发器表达式中省略主机名将导致错误。
有关 Event name 宏中双斜杠用法的示例,请参见示例 18。
所引用的监控项必须处于受支持的状态(nodata() 函数除外,该函数也会针对不受支持的监控项进行计算)。
虽然在触发器中,作为函数参数的其他触发器表达式仅限于非历史函数,但这一限制不适用于计算型监控项。
函数特定参数
函数特定参数位于 监控项 键之后,并且与 监控项 键之间用逗号分隔。 有关这些参数的完整列表,请参见 supported functions。
大多数数值函数接受时间作为参数。 您可以使用秒或 time suffixes 来表示时间。 以井号开头的参数具有不同的含义:
| 表达式 | 描述 |
|---|---|
| sum(/主机/key,10m) | 最近 10 分钟内的值的总和。 |
| sum(/主机/key,#10) | 最近十个值的总和。 |
带有井号的参数在函数 last 中具有不同的含义 - 它们表示第 N 个先前的值,假设有如下值(从最近到最远): 30、70、20、60、50:
last(/host/key,#2)将返回 '70'last(/host/key,#5)将返回 '50'
时间偏移
支持使用时间或值数量作为函数参数进行可选的时间偏移。
该参数允许引用过去某一时间段的数据。
时间偏移以 now 开始 - 表示当前时间,后接 +N<time unit> 或 -N<time unit> - 用于增加或减少 N 个时间单位。
例如,avg(/host/key,1h:now-1d) 将返回一天前一小时的平均值。
仅 trend functions 支持以月 (M) 和年 (y) 指定时间偏移。其他函数支持秒 (s)、分钟 (m)、小时 (h)、天 (d) 和周 (w)。
使用绝对时间周期的时间偏移
时间偏移参数支持绝对时间周期,例如,一天从午夜到午夜,一周从周一到周日,一月从该月的第一天到最后一天。
绝对时间周期的时间偏移以 now 开始 - 表示当前时间,后接任意数量的时间操作:/<time unit> - 定义时间单位的起始和结束,例如,一天从午夜到午夜,+N<time unit> 或 -N<time unit> - 用于增加或减少 N 个时间单位。
请注意,时间偏移的值可以大于或等于 0,而时间周期的最小值为 1。
| 参数 | 描述 |
|---|---|
| 1d:now/d | 昨天 |
| 1d:now/d+1d | 今天 |
| 2d:now/d+1d | 最近 2 天 |
| 1w:now/w | 上周 |
| 1w:now/w+1w | 本周 |
其他表达式
函数参数可以包含其他表达式,如下列语法所示:
min(min(/host/key,1h),min(/host2/key2,1h)*10)
请注意,若函数引用监控项历史数据,则不能使用其他表达式。 例如,下列语法是不被允许的:
min(/host/key,#5*10)
运算符
以下运算符受触发器支持,按执行优先级从高到低排列:
| 优先级 | 运算符 | 定义 | 未知值 说明 | 强制将操作数转换为浮点数 1 |
|---|---|---|---|---|
| 1 | - | 一元负号 | -未知 → 未知 | 是 |
| 2 | not | 逻辑非 | not 未知 → 未知 | 是 |
| 3 | * | 乘法 | 0 * 未知 → 未知 (是,未知,不是 0 - 以免在算术运算中丢失 未知) 1.2 * 未知 → 未知 |
是 |
| / | 除法 | 未知 / 0 → 错误 未知 / 1.2 → 未知 0.0 / 未知 → 未知 |
是 | |
| 4 | + | 算术加法 | 1.2 + 未知 → 未知 | 是 |
| - | 算术减法 | 1.2 - 未知 → 未知 | 是 | |
| 5 | < | 小于。该运算符定义为: A<B ⇔ (A<B-0.000001) |
1.2 < 未知 → 未知 | 是 |
| <= | 小于或等于。该运算符定义为: A<=B ⇔ (A≤B+0.000001) |
未知 <= 未知 → 未知 | 是 | |
| > | 大于。该运算符定义为: A>B ⇔ (A>B+0.000001) |
是 | ||
| >= | 大于或等于。该运算符定义为: A>=B ⇔ (A≥B-0.000001) |
是 | ||
| 6 | = | 等于。该运算符定义为: A=B ⇔ (A≥B-0.000001) and (A≤B+0.000001) |
否 1 | |
| <> | 不等于。该运算符定义为: A<>B ⇔ (A<B-0.000001) or (A>B+0.000001) |
否 1 | ||
| 7 | and | 逻辑与 | 0 and 未知 → 0 1 and 未知 → 未知 未知 and 未知 → 未知 |
是 |
| 8 | or | 逻辑或 | 1 or 未知 → 1 0 or 未知 → 未知 未知 or 未知 → 未知 |
是 |
1 如果满足以下条件,字符串操作数仍会转换为数值:
- 另一个操作数是数值
- 对该操作数使用了 = 或 <> 以外的运算符
对于关系运算符(<、<=、>、>=):如果转换失败(例如对 ""),表达式结果为未知(不会回退为字符串比较)。
对于相等运算符(=、<>):如果转换失败,则按原始字符串进行比较。
not、and 和 or 运算符区分大小写,且必须使用小写。 它们前后还必须有空格或括号。
除一元 - 和 not 外,所有运算符都具有从左到右的结合性。 一元 - 和 not 不具有结合性(这意味着应使用 -(-1) 和 not (not 1),而不是 --1 和 not not 1)。
求值结果:
- <、<=、>、>=、=、<> 运算符在触发器表达式中,如果指定关系为真,则结果为 '1';如果为假,则结果为 '0'。 如果至少有一个操作数为未知,则结果为未知;
- 对于已知操作数,and 在两个操作数都与 '0' 比较不相等时返回 '1';否则返回 '0';对于未知操作数,只有当其中一个操作数与 '0' 比较相等时,and 才返回 '0';否则返回 'Unknown';
- 对于已知操作数,or 在任一操作数与 '0' 比较不相等时返回 '1';否则返回 '0';对于未知操作数,只有当其中一个操作数与 '0' 比较不相等时,or 才返回 '1';否则返回 'Unknown';
- 逻辑非运算符 not 对于已知操作数的结果为 '0',当其操作数的值与 '0' 比较不相等时;当其操作数的值与 '0' 比较相等时,结果为 '1'。 对于未知操作数,not 返回 'Unknown'。
值缓存
触发器评估所需的值由Zabbix server缓存。
由于这个原因,在服务器重启后的某个时间段内,触发器评估会导致数据库负载升高。
当监控项的历史值被移除时(无论是手动还是通过Housekeeper),value cache都不会被清除,因此服务器将继续使用缓存中的值,直到这些值的年龄超过触发器函数中定义的时间周期,或者服务器被重启。
如果缓存中没有最近的数据,并且函数中没有定义查询周期,则Zabbix默认会回溯最多一周的时间,以从数据库中query历史值。
触发器示例
示例1
Zabbix server 上的处理器负载过高。
last(/Zabbix server/system.cpu.load[all,avg1])>5
通过使用 'last()' 函数,我们引用了最近的值。
/Zabbix server/system.cpu.load[all,avg1] 表示被监控参数的简称。
它指定了 主机 是 'Zabbix server',并且被监控的键是 'system.cpu.load[all,avg1]'。
最后,>5 表示当从 Zabbix server 获取的最近处理器负载测量值大于 5 时,触发器处于 PROBLEM 状态。
示例2
www.example.com 过载。
last(/www.example.com/system.cpu.load[all,avg1])>5 or min(/www.example.com/system.cpu.load[all,avg1],10m)>2
当当前处理器负载超过 5 或者在最近 10 分钟内处理器负载曾超过 2 时,该表达式为真。
示例3
/etc/passwd 已被更改。
last(/www.example.com/vfs.file.cksum[/etc/passwd],#1)<>last(/www.example.com/vfs.file.cksum[/etc/passwd],#2)
当 /etc/passwd 校验和的上一个值与最新值不同时,该表达式为真。
类似的表达式可用于监控重要文件的更改,例如 /etc/passwd、/etc/inetd.conf、/kernel 等。
示例4
有人正在从互联网下载一个大型file。
min函数的使用:
min(/www.example.com/net.if.in[eth0,bytes],5m)>100K
当在最近的5分钟内,eth0接口接收的字节数超过100 KB时,该表达式为真。
示例5
集群SMTP服务器的两个节点均处于宕机状态。
注意在一个表达式中使用了两个不同的主机:
last(/smtp1.example.com/net.tcp.service[smtp])=0 and last(/smtp2.example.com/net.tcp.service[smtp])=0
当smtp1.example.com和smtp2.example.com上的两个SMTP服务器均宕机时,该表达式为真。
示例6
Zabbix agent 需要升级。
使用函数 find():
find(/example.example.com/agent.version,,"like","beta8")=1
如果 Zabbix agent 具有 version beta8,则表达式为真。
示例 7
服务器不可达。
count(/example.example.com/icmpping,30m,,"0")>5
如果在最近 30 分钟内,主机 "example.example.com" 不可达超过 5 次,则此表达式为真。
示例 8
最近 3 分钟内没有心跳。
使用函数 nodata():
nodata(/example.example.com/tick,3m)=1
要使用此触发器,必须将 'tick' 定义为 Zabbix trapper 监控项。 主机 应该使用 zabbix_sender 定期发送此 监控项 的数据。 如果在 180 秒内未接收到数据,则触发器值变为 PROBLEM。
注意:'nodata' 可用于任何类型的 监控项。
示例 9
夜间 CPU 活动。
使用函数 time():
min(/Zabbix server/system.cpu.load[all,avg1],5m)>2 and time()<060000
该触发器只能在夜间(00:00 - 06:00)改变其状态为问题。
示例 10
任何时间的 CPU 活动,除了特定时间段。
使用 time() 函数和 not 运算符:
min(/zabbix/system.cpu.load[all,avg1],5m)>2
and not (dayofweek()=7 and time()>230000)
and not (dayofweek()=1 and time()<010000)
该触发器可能在任意时间切换到问题状态,除了每周的周换时间(星期日,2:00 - 星期一,01:00)的 23 小时期间。
示例 11
检查客户端本地时间是否与 Zabbix 服务器时间同步。
使用 fuzzytime() 函数:
fuzzytime(/MySQL_DB/system.localtime,10s)=0
当服务器 MySQL_DB 上的本地时间与 Zabbix 服务器时间相差超过 10 秒时,触发器将变为问题状态。
请注意,system.localtime 必须为 Zabbix agent 配置为 被动检查;在 Zabbix agent 2 上,它也可以配置为主动检查。
示例 12
将今天的平均负载与昨天同一时间的平均负载进行比较(使用时间偏移 now-1d)。
avg(/server/system.cpu.load,1h)/avg(/server/system.cpu.load,1h:now-1d)>2
如果最近一小时的平均负载超过昨天同一小时平均负载的两倍,触发器将被触发。
示例 13
使用另一个 监控项 的值来 get 触发器阈值:
last(/Template PfSense/hrStorageFree[{#SNMPVALUE}])<last(/Template PfSense/hrStorageSize[{#SNMPVALUE}])*0.1
当可用存储空间低于 10 百分比时,触发器将被触发。
示例 14
使用评估结果来get超过阈值的触发器数量:
(last(/server1/system.cpu.load[all,avg1])>5) + (last(/server2/system.cpu.load[all,avg1])>5) + (last(/server3/system.cpu.load[all,avg1])>5)>=2
如果表达式中至少有两个触发器处于问题状态,触发器将被触发。
示例 15
比较两个 string 的 监控项 值 - 此处的操作数是返回字符串的函数。
问题:如果 Ubuntu 的 create 在不同的 主机 上的 version 不同,则触发告警。
last(/NY Zabbix server/vfs.file.contents[/etc/os-release])<>last(/LA Zabbix server/vfs.file.contents[/etc/os-release])
示例 16
比较两个字符串值 - 操作数为:
- 返回字符串的函数
- 宏和字符串的组合
问题:检测 DNS 查询中的变化
监控项键为:
net.dns.record[192.0.2.1,{$WEBSITE_NAME},{$DNS_RESOURCE_RECORD_TYPE},2,1]
其宏定义为:
{$WEBSITE_NAME} = example.com
{$DNS_RESOURCE_RECORD_TYPE} = MX
并且通常返回:
example.com MX 0 mail.example.com
因此,用于检测 DNS 查询结果是否偏离预期结果的触发器表达式为:
last(/Zabbix server/net.dns.record[192.0.2.1,{$WEBSITE_NAME},{$DNS_RESOURCE_RECORD_TYPE},2,1])<>"{$WEBSITE_NAME} {$DNS_RESOURCE_RECORD_TYPE} 0 mail.{$WEBSITE_NAME}"
请注意第二个操作数周围的引号。
示例 17
比较两个字符串值 - 操作数为:
- 一个返回字符串的函数
- 一个包含特殊字符 \ 和 " 的字符串常量
问题:检测 /tmp/hello 文件内容是否等于:
\" //hello ?\"
选项 1. 直接写入字符串:
last(/Zabbix server/vfs.file.contents[/tmp/hello])="\\\" //hello ?\\\""
请注意,当字符串直接比较时,\ 和 " 字符会被转义。
选项 2. 使用宏
{$HELLO_MACRO} = \" //hello ?\"
在表达式中:
last(/Zabbix server/vfs.file.contents[/tmp/hello])={$HELLO_MACRO}
示例 18
比较长期时间段。
问题:上个月Exchange服务器负载增加了超过10%
trendavg(/Exchange/system.cpu.load,1M:now/M)>1.1*trendavg(/Exchange/system.cpu.load,1M:now/M-1M)
此外,您还可以在触发器配置中使用Event name字段来构建有意义的告警信息。例如,以收到类似以下内容的消息:
"Load of Exchange server increased by 24% in July (0.69) comparing to June (0.56)"
事件名称必须定义为:
Load of {HOST.HOST} server increased by {{?100*trendavg(//system.cpu.load,1M:now/M)/trendavg(//system.cpu.load,1M:now/M-1M)}.fmtnum(0)}% in {{TIME}.fmttime(%B,-1M)} ({{?trendavg(//system.cpu.load,1M:now/M)}.fmtnum(2)}) comparing to {{TIME}.fmttime(%B,-2M)} ({{?trendavg(//system.cpu.load,1M:now/M-1M)}.fmtnum(2)})
对于此类问题,在触发器配置中允许手动关闭也是很有用的。
有触发器表达式的示例可能对其他人有用?请使用示例建议表单将其发送给 Zabbix 开发人员。
迟滞
有时在问题状态和恢复状态之间需要一个间隔,而不仅仅是简单的阈值。
例如,如果我们希望定义一个触发器,当服务器机房温度超过 20°C 时报告问题,并且希望它保持在问题状态直到温度降至 15°C 以下,那么仅在 20°C 设置一个简单的触发器阈值是不够的。
相反,我们首先需要为问题事件定义一个触发器表达式(温度高于 20°C)。
然后我们需要定义一个额外的恢复条件(温度低于 15°C)。
这可以通过在 defining 触发器时定义一个额外的 恢复表达式 参数来实现。
在这种情况下,问题恢复将分为两个步骤:
- 首先,问题表达式(温度高于 20°C)必须评估为 FALSE
- 其次,恢复表达式(温度低于 15°C)必须评估为 TRUE
只有当问题事件首先被解决时,才会评估恢复表达式。
如果问题表达式仍然为 TRUE,仅恢复表达式为 TRUE 并不会解决该问题!
示例1
服务器机房温度过高。
问题表达式:
last(/server/temp)>20
恢复表达式:
last(/server/temp)<=15
示例2
可用磁盘空间过低。
问题表达式:最近 5 分钟内小于 10GB
max(/server/vfs.fs.size[/,free],5m)<10G
恢复表达式:最近 10 分钟内大于 40GB
min(/server/vfs.fs.size[/,free],10m)>40G
带有未知操作数的表达式
通常,表达式中的未知操作数(例如不支持的监控项)会立即将触发器值置为 Unknown。
但是,在某些情况下,未知操作数(不支持的监控项、函数错误)也会被允许参与表达式求值:
- 无论引用的监控项是否受支持,
nodata()函数都会被求值。 - 包含 OR 和 AND 的逻辑表达式在两种情况下,即使存在未知操作数,也可以求值为已知值:
- 情况 1:"
1 or some_function(unsupported_item1) or some_function(unsupported_item2) or ..." 可以求值为已知结果('1'或 "Problem"), - 情况 2:"
0 and some_function(unsupported_item1) and some_function(unsupported_item2) and ..." 可以求值为已知结果('0'或 "OK")。
Zabbix 会尝试通过将不支持的监控项视为未知操作数来求值此类逻辑表达式。 在上述两种情况下,会产生一个已知值(分别为 "Problem" 或 "OK");在所有其他情况下,触发器将求值为Unknown。
- 情况 1:"
- 如果对受支持监控项的函数求值出错,则该函数值会变为
Unknown,并在后续表达式求值中作为未知操作数参与。
请注意,未知操作数只有在上述逻辑表达式中才可能“消失”。
在算术表达式中,未知操作数总是会导致结果为 Unknown(除以 0 的情况除外)。
求值结果为 Unknown 的表达式不会改变触发器状态("Problem/OK")。
因此,如果它原本是 "Problem"(参见情况 1),即使已知部分恢复(1 变为 0),它仍会保持在相同的问题状态,因为此时表达式会被求值为 Unknown,而这不会改变触发器状态。
如果包含多个不支持监控项的触发器表达式求值为 Unknown,则前端中的错误消息会引用最后一个被求值的不支持监控项。