3. LogQL 基础语法:日志流选择器、行过滤器与 PromQL 对比
LogQL 是 Loki 的查询语言,语法上受 PromQL 启发,但面向日志数据而非时序指标。理解 LogQL 的核心在于掌握两个基本构件:日志流选择器(Log Stream Selector)和行过滤器(Line Filter)。本章将拆解这两个概念,并与 PromQL 进行对比,帮助你快速建立 LogQL 的思维模型。
3.1 日志流选择器:{job=~"..."}
日志流选择器用于从 Loki 中筛选出特定的日志流。它的语法与 PromQL 的标签匹配器完全一致,使用花括号 {} 包裹标签键值对。所有 LogQL 查询都必须以日志流选择器开头。
| 匹配器类型 | 符号 | 示例 | 说明 |
|---|---|---|---|
| 精确匹配 | = |
{job="api-server"} |
标签值完全等于 api-server |
| 精确否定 | != |
{job!="debug"} |
排除标签值为 debug 的流 |
| 正则匹配 | =~ |
{job=~"api-.*"} |
匹配正则表达式 api-.* |
| 正则否定 | !~ |
{job!~"staging|dev"} |
排除匹配正则的流 |
实战建议:在生产环境中,建议至少使用
{job=~"..."} 或 {namespace=~"..."} 作为选择器,避免全量扫描所有日志流。选择器越精确,查询性能越高。
多个标签条件可以组合使用,用逗号分隔,相当于逻辑 AND:
{job=~"api-.*", namespace="production", env!~"staging|dev"}
3.2 行过滤器:|=、!=、|~
行过滤器在日志流选择器筛选出的日志行基础上,进一步对日志内容进行过滤。它使用管道符 | 连接,作用于日志行的文本内容。
| 过滤器类型 | 符号 | 示例 | 说明 |
|---|---|---|---|
| 包含 | |= |
{job="api"} |= "ERROR" |
日志行包含子串 ERROR |
| 不包含 | != |
{job="api"} != "DEBUG" |
日志行不包含子串 DEBUG |
| 正则匹配 | |~ |
{job="api"} |~ "5[0-9]{2}" |
日志行匹配正则(如 HTTP 5xx) |
| 正则不匹配 | !~ |
{job="api"} !~ "health|metrics" |
日志行不匹配正则 |
性能注意:
|= 和 != 是子串匹配,性能优于正则。只有在需要复杂模式匹配时才使用 |~ 和 !~。正则表达式应尽量简洁,避免回溯爆炸。
行过滤器可以串联使用,形成过滤链:
{job="api-server"} |= "ERROR" |~ "5[0-9]{2}" != "timeout"
上述查询的含义:选择 job="api-server" 的日志流 → 包含 ERROR → 匹配 HTTP 5xx 状态码 → 排除包含 timeout 的行。
3.3 LogQL 与 PromQL 语法对比
LogQL 的设计借鉴了 PromQL,但两者面向的数据类型不同。下表从核心概念到语法细节进行对比:
| 对比维度 | PromQL(指标) | LogQL(日志) |
|---|---|---|
| 数据对象 | 时间序列(指标值 + 标签) | 日志流(日志行 + 标签) |
| 选择器语法 | {job="api"} |
{job="api"}(完全相同) |
| 过滤方式 | 标签匹配 + 值范围(如 > 0.5) |
标签匹配 + 行内容过滤(|=、|~) |
| 聚合函数 | rate()、sum()、avg() 等 |
rate()、sum()、avg()(语法一致) |
| 管道操作 | 无管道概念 | 管道 | 串联过滤、解析、格式化 |
| 日志解析 | 不支持 | | json、| logfmt、| regexp |
| 返回类型 | 数值向量或标量 | 日志行(LogQL 查询)或指标向量(LogQL 指标查询) |
核心差异总结:PromQL 操作的是数值,LogQL 操作的是文本。LogQL 的管道机制是其独有的强大特性,允许在查询过程中对日志行进行解析、提取和转换,这是 PromQL 无法做到的。
3.4 从 PromQL 到 LogQL 的思维转换
如果你已经熟悉 PromQL,以下对照表可以帮助你快速迁移:
| PromQL 场景 | 对应的 LogQL 写法 | 说明 |
|---|---|---|
{job="api"} |
{job="api"} |
选择器语法完全一致 |
rate({job="api"}[5m]) |
rate({job="api"} |= "ERROR" [5m]) |
LogQL 的 rate() 计算日志行出现速率 |
sum by(host) (rate(...)) |
sum by(host) (rate({job="api"} |= "ERROR" [5m])) |
聚合语法完全一致 |
| 无对应 | {job="api"} |= "ERROR" | json |
LogQL 独有:解析 JSON 日志行 |
| 无对应 | {job="api"} | logfmt | duration > 1s |
LogQL 独有:解析 logfmt 并过滤字段 |
3.5 实战案例:从日志中提取 Golden Signals 指标
以下是一个完整的 LogQL 查询示例,展示如何从 API 日志中提取错误率指标:
# 步骤 1:选择日志流 + 过滤错误行
{job="api-server", namespace="production"}
|= "ERROR"
|~ "5[0-9]{2}"
# 步骤 2:解析 JSON 日志体,提取 status_code 和 duration
| json
| status_code >= 500
# 步骤 3:计算 5 分钟内的错误速率
| rate($__interval)
# 完整查询(Grafana 中使用):
rate(
{job="api-server", namespace="production"}
|= "ERROR"
|~ "5[0-9]{2}"
| json
| status_code >= 500
[5m]
)
Golden Signals 映射:上述查询直接对应 错误率(Error Rate)信号。类似地,你可以用
| json | duration > 1s 提取 延迟(Latency),用 rate({job="api-server"}[5m]) 提取 流量(Traffic)。
3.6 常见错误与避坑指南
- 选择器遗漏:LogQL 查询必须以
{...}开头,不能直接写|= "ERROR"。 - 正则性能陷阱:
|~ ".*ERROR.*"等价于|= "ERROR",但正则版本性能差 10 倍以上。能用子串匹配就不要用正则。 - 管道顺序:高过滤性的条件(如
|= "ERROR")应放在管道前面,尽早减少数据量。 - 标签基数爆炸:不要将高基数标签(如
user_id、request_id)作为 Loki 标签,应放在日志行内通过| json解析。 - PromQL 思维惯性:不要试图在 LogQL 中做数值运算(如
a + b),LogQL 的数值运算能力有限,复杂计算应使用 PromQL 或 Grafana 的 Transform。
生产环境检查清单:
- 每个查询是否都有明确的日志流选择器?
- 行过滤器是否优先使用
|=而非|~? - 是否避免了在标签中使用高基数属性?
- 管道顺序是否按过滤强度从高到低排列?