# 日志规则

对 ElasticSearch、OpenSearch、Loki、VictoriaLogs 的日志条数或匹配做告警——四种都能告警，但只有三种能在日志检索里预览。

最后校对于 2026-10-10

这页办完你会得到：一条按周期数日志行数、多了就报警的规则， 四种日志存储里你用的是哪种都行。

日志告警最后都归结为同一件事——**把一个时间窗的日志变成一个数，然后拿它比较**。 四种数据源的差别在于「比较写在哪儿」。

## 四种数据源，两种规则形态[​](#四种数据源两种规则形态 "四种数据源，两种规则形态的直接链接")

| 数据源        | 阈值写在哪                                | 表达式写法               |
| ------------- | ----------------------------------------- | ------------------------ |
| Elasticsearch | 单独的**判定条件**区块                    | `$A > 100`               |
| OpenSearch    | 同 Elasticsearch                          | `$A > 100`               |
| VictoriaLogs  | 同一个区块                                | `$A.<stats 别名> > 100`  |
| Loki          | **写在 LogQL 里**，和 Prometheus 规则一样 | 没有——它就是查询的一部分 |

Loki 是那个例外，单独放在下面讲。其余三种共用 [判定周期与恢复](https://n9e.github.io/zh/docs/alerting/evaluation.md)里描述的那套判定条件模型。

## Elasticsearch 与 OpenSearch[​](#elasticsearch-与-opensearch "Elasticsearch 与 OpenSearch的直接链接")

两者用同一套表单，OpenSearch 只是把「索引模式」那个选项藏了。

### 把日志变成一个数[​](#把日志变成一个数 "把日志变成一个数的直接链接")

**查询统计**下面是一张查询卡片：

| 字段              | 怎么填                                                                                                        |
| ----------------- | ------------------------------------------------------------------------------------------------------------- |
| 索引              | 要查的索引。`logs-2026.01.01`、逗号分隔的多个，或者 `logs-*` 这样的通配符                                     |
| 过滤条件          | 一条 **Lucene** 查询——`level:ERROR AND service:checkout`。规则表单里不提供 KQL                                |
| 日期字段          | 时间戳字段，默认 `@timestamp`                                                                                 |
| 时间间隔          | 时间窗，单位秒，默认 60。它既是聚合桶大小，也是这次查询往回看多久                                             |
| 数值提取          | 这个窗怎么变成一个数：`count`，或者对某个数值**字段**做 `avg` / `sum` / `max` / `min` / `p90` / `p95` / `p99` |
| Group By          | 可选。按某个 term 字段拆开，每个取值各出一条事件，用 `匹配个数` 和 `文档最小值` 控制规模                      |
| 辅助配置 → Offset | 把窗口整体往前挪 N 秒，适合写入有延迟的数据源                                                                 |

`count` 是最常见的：「命中了多少行」。那几个分位数是给「对日志里的某个数值告警」用的， 比如每行里记的请求耗时。

**Group By 是把「一条告警」变成「每个服务一条告警」的关键。** 不用它， 一条统计所有服务错误数的规则只会响一次，而且不告诉你是哪个服务。 配上 `Group By: service，匹配个数 10`，每个超阈值的服务各出一条事件， 带着 `service=<名字>` 这个标签，路由就能拿它用了。

### 判定条件[​](#判定条件 "判定条件的直接链接")

查询下面是**判定条件 → 阈值判断**。每条判定条件带自己的级别； 和指标规则一样，多条判定条件加上**级别抑制**，就能做出分档阈值而不重复发消息。

判定条件有两种写法：

* **简单模式**——选查询、选比较符、填一个数；
* **表达式模式**——自己写表达式，用到多条查询时就得用它：

```
$A > 100 && $B < 10
```

Elasticsearch 里引用一条查询用裸的 `$A`。多加几张查询卡片就有了 `$B`、`$C`，可以组合。

有两件事会**静默失效**，所以要用[模拟触发](https://n9e.github.io/zh/docs/alerting/test-fire.md)验一下：

* 引用了一个已经不存在的查询别名；
* 拿标签和数字比。标签是字符串——`$A.service == 'checkout'` 没问题， `$A.service > 10` 是类型错误，而引擎对算不出来的表达式一律当「不满足」处理，一声不吭。

## VictoriaLogs[​](#victorialogs "VictoriaLogs的直接链接")

查询是一条 LogsQL，而且必须以 `stats` 管道结尾——规则是以即时 stats 查询的方式跑它的， 纯搜索返回的东西用不了：

```
_msg:error | stats count() as value
```

你给这个统计起的别名，就是判定条件里引用它的方式：

```
$A.value > 20
```

查询里没有 `_time` 过滤时表单会给一条警告，这条警告值得听：不加的话， 每个评估周期都会把库里所有数据扫一遍。

```
_time:5m AND _msg:error | stats count() as value
```

## Loki[​](#loki "Loki的直接链接")

Loki 规则的形态像 Prometheus 规则，不像其他日志规则。每条查询是一个 **LogQL** 输入框， 阈值写在里面，每条查询各带自己的级别：

```
count_over_time({job="myapp"} |= "error" [5m]) > 10
```

由此带来的后果，全都和[指标规则](https://n9e.github.io/zh/docs/alerting/metric-rules.md)一样：

* 查询必须返回瞬时向量，所以要有 `count_over_time`、`rate` 或别的区间聚合—— 光写一个日志选择器不行；
* 返回一条序列产生一条事件，所以粒度由 `sum by (job) (...)` 决定；
* 没有单独的判定条件区块，因此 Loki 规则上**没有恢复条件、也没有数据缺失开关**；
* 恢复的含义就是查询不再返回那条序列。

加第二条查询会出现**级别抑制**开关，和指标规则一样。

## 恢复的默认值在这里不一样[​](#恢复的默认值在这里不一样 "恢复的默认值在这里不一样的直接链接")

Elasticsearch、OpenSearch 和 VictoriaLogs 规则的每条判定条件上都有一个**恢复条件**下拉， 而这个选择对日志比对别处更要紧：

* **查不到数据就恢复**——查不到错误日志，通常就真的说明问题过去了。日志类数据源表单默认选的就是它。
* **必须查到数据且不满足告警条件才恢复**——查询什么都没返回时告警会保持。 当「一条日志都没有」意味着采集链路断了、而不是服务变健康时，这个才对。

如果「日志管道静默」本身就是你想知道的事，那就用告警条件那一步单独的**数据缺失**开关， 而不是靠恢复方式。它有自己的级别，于是「错误变多了」和「日志不来了」就成了两种能区分的事件。

## 保存之前先验证[​](#保存之前先验证 "保存之前先验证的直接链接")

每张查询卡片上都有**数据预览**，会真的跑一次查询，把结果值列成表。保存前用一下—— 这是唯一能看出你的过滤条件到底有没有命中东西的地方。

Elasticsearch、Loki 和 VictoriaLogs 还可以先在**数据查询 → 日志**里交互式地试， 调过滤条件更方便。**OpenSearch 在日志检索里没有**， 所以 OpenSearch 的查询只能在规则表单的数据预览里验。

然后用[模拟触发](https://n9e.github.io/zh/docs/alerting/test-fire.md)拿真实数据核一下判定表达式，保存之后再看 [判定执行记录](https://n9e.github.io/zh/docs/alerting/execution-records.md)，看每个周期到底返回了什么。

## 下一步[​](#下一步 "下一步的直接链接")

* 时间参数与恢复的细节：[判定周期与恢复](https://n9e.github.io/zh/docs/alerting/evaluation.md)
* 改成对数据库告警：[SQL 规则](https://n9e.github.io/zh/docs/alerting/sql-rules.md)
* 先把存储接进来：[Elasticsearch](https://n9e.github.io/zh/docs/data/elasticsearch.md)、[Loki 与 VictoriaLogs](https://n9e.github.io/zh/docs/data/loki-victorialogs.md)
