2. Loki基础回顾:LogQL核心语法、标签匹配、时间范围与聚合操作
LogQL 是 Loki 的查询语言,本质上是将 PromQL 的标签选择与日志行过滤、解析、聚合能力相结合。在变更窗口场景中,LogQL 用于从海量日志中快速定位特定变更事件、统计错误率、计算持续时间。本节从核心语法出发,逐步覆盖标签匹配、时间范围与聚合操作,为后续告警路由模板打下语法基础。
2.1 LogQL 核心语法结构
LogQL 查询由两个主要阶段组成:日志流选择器(Log Stream Selector)和管道表达式(Pipeline Expression)。流选择器通过标签匹配确定要查询的日志源;管道表达式对选中的日志行进行过滤、解析、计算。
| 阶段 | 作用 | 示例 |
|---|---|---|
| 日志流选择器 | 基于标签键值对筛选日志流 | {app="nginx", env="prod"} |
| 行过滤器 | 对日志内容进行包含/排除匹配 | |= "ERROR"、!= "timeout" |
| 解析器 | 从日志行中提取结构化字段 | | json、| logfmt、| regexp |
| 标签/行格式化 | 修改或新增标签、行内容 | | label_format、| line_format |
| 聚合函数 | 对提取的指标进行数学聚合 | sum by(host) (rate(...)) |
service、version、deploy_id 等变更标识,便于在告警路由中按变更维度聚合。
2.2 标签匹配:精确、正则与多条件组合
标签匹配是 LogQL 查询的入口。Loki 支持四种匹配操作符,用于在流选择器中筛选日志流:
- 精确匹配(
=): 标签值必须完全等于给定字符串。{app="api-gateway"} - 精确不匹配(
!=): 排除标签值等于给定字符串的日志流。{app!="debug"} - 正则匹配(
=~): 标签值匹配给定的 RE2 正则表达式。{app=~"api-.*"} - 正则不匹配(
!~): 排除标签值匹配给定正则的日志流。{app!~"test-.*"}
多个标签条件使用逗号分隔,逻辑关系为 AND。例如:
{app="order-service", env="staging", deploy_id=~"v2\\..*"}
该查询只选择 app=order-service、env=staging 且 deploy_id 以 v2. 开头的日志流。
=),避免高基数标签(如 user_id、request_id)作为流选择器条件。正则匹配会降低查询性能,在告警查询中应谨慎使用。
2.3 时间范围:时间与相对时间
LogQL 查询必须指定时间范围。Loki 支持两种方式:
| 方式 | 语法 | 示例 | 适用场景 |
|---|---|---|---|
| 相对时间 | HTTP API 参数 start/end 或 Grafana 时间选择器 |
最近 15 分钟、昨天 10:00-11:00 | 实时告警、临时排查 |
| 时间 | HTTP API 参数 start/end 使用 Unix 时间戳(纳秒) |
start=1718000000000000000 |
历史回放、变更审计 |
在变更窗口场景中,时间范围通常与变更开始/结束时间对齐。例如,查询变更窗口内(2024-06-10 14:00:00 至 14:30:00)的错误日志:
{app="checkout", env="prod"}
|= "ERROR"
| json
配合 Grafana 或 API 设置 start=1718013600000000000、end=1718015400000000000。
2.4 聚合操作:从日志到指标
聚合操作将日志行转换为时间序列指标,是告警规则的核心。LogQL 提供两类聚合:基于日志计数的聚合和基于提取字段的聚合。
2.4.1 常用聚合函数
| 函数 | 作用 | 示例 |
|---|---|---|
rate |
计算日志行每秒出现速率 | rate({app="nginx"} |= "5\\d{2}" [5m]) |
count_over_time |
计算指定时间窗口内的日志行总数 | count_over_time({app="api"} |= "timeout" [1h]) |
sum by |
按标签分组求和 | sum by(host) (rate({app="web"} |= "4\\d{2}" [5m])) |
avg by |
按标签分组求平均值 | avg by(service) (rate({job="worker"} |= "retry" [10m])) |
max by |
按标签分组求最大值 | max by(region) (count_over_time({app="db"} |= "slow" [5m])) |
quantile_over_time |
计算提取字段的分位数 | quantile_over_time(0.99, {app="api"} | json | unwrap duration [5m]) |
2.4.2 聚合流程示例
以下查询统计变更窗口内每个服务的 5xx 错误速率,并按 service 和 version 分组:
sum by(service, version) (
rate(
{app="microservice", env="prod"}
|= "5\\d{2}"
| json
[5m]
)
)
该查询的管道执行顺序:
- 流选择器匹配
app=microservice且env=prod的日志流。 - 行过滤器保留包含 5xx 状态码的日志行。
| json解析器将 JSON 格式的日志行转为结构化字段。rate(... [5m])计算每 5 分钟窗口内的每秒速率。sum by(service, version)按服务名和版本号分组求和。
version 或 deploy_id 标签作为分组维度,便于在告警路由中将异常流量精确关联到特定变更版本。
2.5 LogQL 查询执行流程
下图展示了 LogQL 查询从标签匹配到聚合输出的完整执行链路:
2.6 变更窗口场景中的 LogQL 模式
在变更窗口告警中,以下三种 LogQL 模式最为常用:
| 模式 | 查询示例 | 告警用途 |
|---|---|---|
| 错误速率突增 | sum by(service) (rate({app="myapp"} |= "5\\d{2}" [5m])) > 0.1 |
检测变更后 5xx 错误率超过阈值 |
| 日志量异常 | sum by(host) (count_over_time({app="myapp"} [5m])) > 10000 |
检测日志暴增,可能为死循环或异常流量 |
| 延迟分位数 | quantile_over_time(0.99, {app="myapp"} | json | unwrap response_time [5m]) > 2000 |
检测 p99 响应时间超过 2 秒 |