LogQL 由三部分组成
一句话总结
LogQL 查询由流选择器 → 日志过滤器 → 聚合三部分组成,查询性能几乎完全由第一部分决定。
为什么需要理解查询范围
绝大多数“Loki 查询很慢”的问题,并不是语法造成的,而是没有缩小范围。如果开头的大括号选中了十万个流,那么后面无论进行什么处理都会很慢。
三个组成部分
sum by (pod) (
rate({namespace="labhub-prod", app="labhub"} |= "error" | json | status >= 500 [5m])
)
^--집계--^ ^------- 스트림 셀렉터 -------^ ^------ 로그 필터 ------^
1)流选择器 {...}——通过已建立索引的标签筛选候选流。至少必须有一个选择条件,而且等号匹配器(=)比正则表达式(=~)便宜得多。
2)日志过滤器 |= != |~ !~——扫描所选流的正文。处理顺序非常重要。
# 나쁨 — 파싱부터 하고 거른다
{app="labhub"} | json | level = "error"
# 좋음 — 문자열로 먼저 줄이고 파싱한다
{app="labhub"} |= "error" | json | level = "error"
|= 只是简单的子字符串检查,成本很低;| json 则需要逐行解析,成本很高。把便宜的操作放在前面,就能大幅减少需要解析的行数。
3)聚合——包括 rate、count_over_time、sum by 等操作,日志会在这里转换成指标。
# 초당 오류 수를 파드별로
sum by (pod) (rate({app="labhub"} |= "error" [5m]))
# 오류 메시지 상위 10개
topk(10, sum by (msg) (count_over_time({app="labhub"} | json [1h])))
三种解析器
| 解析器 | 何时使用 |
|---|---|
| ` | json` |
| ` | logfmt` |
| ` | pattern` |
| ` | regexp` |
解析后生成的字段没有索引。因此,| status >= 500 执行的是扫描,而不是索引查询。如果某个值经常使用,可以考虑在采集时把它提升为标签,但必须先计算基数。
常见误解
“增加标签会让查询变快。” 每增加一个标签,流的数量就会乘以该标签的取值数量。把 status 设为标签会使流增加到 5 倍(2xx/3xx/4xx/5xx/其他);把 user_id 设为标签,则会按用户数量增加。流越多,所有查询都会变慢。
“|~ 正则表达式更方便。” 与 |~ "error|warn" 相比,|= "error" or |= "warn" 便宜得多。正则表达式需要逐行运行引擎。
理解查询执行顺序,就能看见成本
LogQL 是一条流水线,在越靠前的位置缩小数据量,成本永远越低。
{namespace="labhub-prod", app="api"} ← 1. 색인으로 스트림을 고른다 (거의 공짜)
|= "timeout" ← 2. 원문 문자열 필터 (싸다)
| json ← 3. 파싱 (비싸다)
| status >= 500 ← 4. 파싱된 필드로 필터
| line_format "{{.msg}}" ← 5. 출력 성형
改变顺序,结果也会改变。如果先运行 | json,再运行 |= "timeout",就等于
解析全部日志行之后才把其中大部分丢弃。尽量把字符串过滤器提前,是优化
LogQL 的第一条规则。
转换为指标——从日志中生成图表
对日志进行计数,就能得到指标。这在尚未加入监控埋点的系统中尤其有用。
# 5분간 5xx 비율
sum(rate({app="api"} |= "HTTP/1.1" 5" [5m]))
/
sum(rate({app="api"} [5m]))
# 파싱한 필드로 p95 지연
quantile_over_time(0.95,
{app="api"} | json | unwrap duration_ms [5m]) by (route)
不过,不能把这种查询长期放在仪表盘中。扫描日志的成本远高于查询时间序列。 确认某个值确实有用后,应该在应用程序中加入真正的指标,只在调查时使用 基于日志的查询。日志生成的指标是一种侦察工具,用于判断“是否值得添加指标”。
提前确定保留策略与压缩方式
Loki 会把日志组成数据块后写入对象存储。这里必须做出两个决定。
- 保留期限。 30 天是常见的起点。如果存在合规要求,可以只为审计日志设置
更长的保留策略(通过
retention_stream按标签指定)。 - 数据块大小与空闲时间。 流比较安静时,会产生大量小数据块,导致查询
变慢。增大
chunk_idle_period可以生成更大的数据块,但最新日志出现在搜索 结果中的时间也会推迟。
用于告警的查询应采用较短范围(5~15 分钟),并与调查用的长范围查询分开。 如果每分钟都执行一次覆盖 24 小时的查询,成本会悄无声息地增长。
实际工作中真正重要的事
查询缓慢时,首先应该测量的是选中的流数量。
count(count by (pod, container) ({namespace="labhub-prod"}))
Grafana 的查询统计信息(Query inspector)中也会显示 Total bytes processed。如果这个值达到 GB 级,就说明查询范围过大。缩短时间范围或再添加一个标签,通常比调整查询语法更有效。