Prometheus告警规则单元测试:如何Mock hour()函数?
解决Prometheus告警规则测试中Mock hour()函数的问题
首先先修正你告警规则里的语法错误:(hour()) < 16 > 7 是无效的PromQL表达式,正确的工作时段判断应该写成 hour() > 7 and hour() < 16,否则规则本身无法正常执行。
针对Mock hour() 函数的需求,核心思路是通过控制规则的评估时间来间接控制hour()的返回值——因为hour()函数的结果完全依赖于Prometheus执行规则时的当前时间(或者说评估时间)。下面是两种常用的实现方式:
1. 使用promtool进行规则测试(官方工具)
promtool是Prometheus官方提供的规则测试工具,你可以通过指定evaluation_time来模拟不同的时间点,从而让hour()返回预期的小时数。
示例测试文件
创建一个alert_test.yaml测试文件,包含两种场景的测试:
groups: - name: service_alerts rules: - alert: CounterStuckDuringWorkHours expr: round(sum(increase(counter_total{cluster="prod",service="xxx"}[1h])),1) == 0 and hour() > 7 and hour() < 16 for: 0m labels: severity: warning annotations: summary: "[prod] xxx服务计数器1小时无增长" # 测试场景1:工作时段内计数器无增长,预期触发告警 tests: - interval: 1m evaluation_time: "2024-05-20T09:30:00Z" # 模拟上午9点30分,hour()返回9 input_series: - series: 'counter_total{cluster="prod",service="xxx"}' values: '25 25 25 25 25 25 25' # 连续60个1分钟样本数值不变(这里简化为7个),increase计算为0 alert_rule_test: - alertname: CounterStuckDuringWorkHours eval_time: 2024-05-20T09:30:00Z exp_alerts: - labels: severity: warning annotations: summary: "[prod] xxx服务计数器1小时无增长" # 测试场景2:非工作时段计数器无增长,预期不触发告警 - interval: 1m evaluation_time: "2024-05-20T18:00:00Z" # 模拟下午6点,hour()返回18 input_series: - series: 'counter_total{cluster="prod",service="xxx"}' values: '25 25 25 25 25 25 25' alert_rule_test: - alertname: CounterStuckDuringWorkHours eval_time: 2024-05-20T18:00:00Z exp_alerts: [] # 预期无告警输出
运行测试
执行以下命令即可运行测试:
promtool test rules alert_test.yaml
2. 自定义代码测试(如Go测试框架)
如果你的团队用自定义代码编写测试(比如基于Prometheus的Go客户端库),可以通过Mock系统时间来控制hour()的返回值:
- 使用Go的
time包Mock工具(比如github.com/benbjohnson/clock),将当前时间设置为你需要的测试时间点。 - 在评估告警规则前,把Prometheus的评估时间替换为Mock的时间,这样
hour()就会返回对应时间的小时数。
举个简化的Go测试片段:
package alerttests import ( "testing" "time" "github.com/benbjohnson/clock" "github.com/prometheus/prometheus/model/labels" "github.com/prometheus/prometheus/promql" ) func TestCounterStuckAlert(t *testing.T) { // 创建Mock时钟,设置为工作时段 mockClock := clock.NewMock() mockClock.Set(time.Date(2024, 5, 20, 10, 0, 0, 0, time.UTC)) // 构造测试样本数据(计数器1小时无增长) series := promql.NewSeriesIterator(&promql.Series{ Labels: labels.FromStrings("cluster", "prod", "service", "xxx"), Points: []promql.Point{{T: mockClock.Now().Unix() - 3600, V: 10}, {T: mockClock.Now().Unix(), V: 10}}, }) // 评估告警规则时使用Mock时间 // 省略规则解析和评估的具体代码,核心是让评估逻辑使用mockClock.Now()作为当前时间 }
关键注意点
hour()函数返回的是UTC时间的小时数,如果你需要的是本地时间,得用hour(timezone("Asia/Shanghai"))这类表达式,测试时也要对应设置UTC时间到目标时区的工作时段。- 测试
increase()函数时,要确保输入的样本时间范围覆盖规则里的[1h]窗口,否则increase()会返回空值,影响测试结果。
内容的提问来源于stack exchange,提问作者Mr. Invisible
相关产品推荐
相关产品推荐

