20分钟掌握日志查询
核心原理
LogsQL 做三件事:过滤 → 提取 → 统计
查询执行流程:
_time:1h error | extract_regexp ... | stats by (url) count()
↓ ↓ ↓
1. 扫描索引 2. 逐条提取字段 3. 内存聚合
10亿条 → 1万条 → 100条
关键:必须先指定 _time(否则扫描全库会超时),越早过滤越快。
1分钟:基础查询
_time:5m # 看最近 5 分钟的日志
_time:1h error # 找包含 error 的日志
_time:1h service_name:"api" error -debug # 组合过滤
时间范围:5m 1h 1d 7d 30d
立即试试:打开 Web UI (http://your-server:9428/select/vmui),输入 _time:5m 看看有什么日志。
2分钟:过滤语法
字段匹配(索引加速)
service_name:"api" # 精确匹配
log_type:"error" # 日志类型
host:"server1" # 主机名
文本搜索
error # 关键词
"cannot open" # 短语
err* # 通配符
-debug # 排除
逻辑组合
error timeout # AND(空格)
error OR warning # OR
(error OR warning) AND service_name:"api" -debug
高级时间
_time:[2026-02-24, 2026-02-25) # 指定日期
_time:30d AND _time:week_range[Tue, Tue] # 只要周二
3分钟:统计分析
计数和分组
_time:1h error | stats count() # 有多少条
_time:1h error | stats by (service_name) count() as cnt # 按服务分组
Top N
_time:1h error
| stats by (service_name) count() as cnt
| sort by (cnt) # 默认降序
| limit 10
时间趋势
_time:1d error | stats by (_time:1h) count() # 每小时错误数
统计函数
count() # 计数
sum(field) # 求和
avg(field) # 平均
min(field) max(field) # 最小/最大
count_uniq(field) # 去重计数
4分钟:提取字段
日志是文本,用正则提取结构化字段:
_time:1h
| extract_regexp '"url":"(?P<url>[^"]+)"' # 提取 url
| extract_regexp '"cost":(?P<cost>[0-9]+)' # 提取 cost
| stats by (url) avg(cost) as avg_ms # 统计平均耗时
| sort by (avg_ms) | limit 10
常用正则:
'"url":"(?P<url>[^"]+)"' # JSON 字符串
'"cost":(?P<cost>[0-9]+)' # JSON 数字
'ip=(?P<ip>([0-9]+[.]){3}[0-9]+)' # IP 地址
技巧:先用 | limit 10 看原始日志,确认正则能匹配。
实战场景
故障排查
哪个服务错误最多?
_time:5m (error OR exception)
| stats by (service_name) count() as cnt
| sort by (cnt)
| limit 10
某个接口为什么慢?
_time:1h service_name:"api"
| extract_regexp '"url":"(?P<url>[^"]+)"'
| extract_regexp '"cost":(?P<cost>[0-9]+)'
| stats by (url) avg(cost) as avg_ms, max(cost) as max_ms
| sort by (avg_ms)
| limit 10
周期性问题(每周二都慢)
_time:30d log_type:"slow" AND _time:week_range[Tue, Tue]
| extract_regexp '"url":"(?P<url>[^"]+)"'
| stats by (_time:1d, url) count()
| sort by (_time)
性能分析
哪些用户受影响?
_time:1h error
| extract_regexp '"user_id":"(?P<user_id>[^"]+)"'
| extract_regexp '"url":"(?P<url>[^"]+)"'
| stats by (url) count() as errors, count_uniq(user_id) as users
| sort by (users)
| limit 20
流量趋势
_time:1d
| stats by (_time:10m) count()
| sort by (_time)
调试技巧
查询慢或超时?
- 缩小时间:
_time:1h而不是_time:30d - 先用索引字段:
service_name:"xxx" log_type:"error" - 测试时加
| limit 10
反模式:
❌ _time:30d | extract_regexp ... # 先扫描 30 天,慢!
✅ _time:30d service_name:"api" | extract_regexp ... # 先过滤,快!
查询报错?
- 去掉管道,只留过滤器
- 逐个加管道,定位问题
- 用
| limit 10检查字段名
常见错误:
missing whitespace→ 检查括号、引号cannot parse→ 正则表达式有问题field not found→ 字段名错误
正则不匹配?
- 先
_time:5m | limit 10看原始日志 - 在 regex101.com 调试正则
- 确认能匹配后再加到查询
速查:我想...
| 我想... | 查询 |
|---|---|
| 看看最近有什么日志 | _time:5m |
| 找错误日志 | _time:1h error |
| 找特定服务的错误 | _time:1h service_name:"api" error |
| 统计错误数量 | _time:1h error | stats count() |
| 找错误最多的服务 | _time:1h error | stats by (service_name) count() as cnt | sort by (cnt) | limit 10 |
| 看每小时错误趋势 | _time:1d error | stats by (_time:1h) count() |
| 找最慢的接口 | _time:1h | extract_regexp '"url":"(?P<url>[^"]+)"' | extract_regexp '"cost":(?P<cost>[0-9]+)' | stats by (url) avg(cost) as avg_ms | sort by (avg_ms) | limit 10 |
| 找周二的慢日志 | _time:30d AND _time:week_range[Tue, Tue] log_type:"slow" |
| 看有多少不同的用户 | _time:1h | extract_regexp '"user_id":"(?P<user_id>[^"]+)"' | stats count_uniq(user_id) |
语法速查
| 功能 | 语法 |
|---|---|
| 时间 | _time:5m _time:1h _time:1d _time:7d |
| 时间范围 | _time:[2026-02-24, 2026-02-25) |
| 周几 | _time:week_range[Tue, Tue] |
| 字段匹配 | service_name:"api" host:"server1" |
| 关键词 | error "cannot open" err* |
| 逻辑 | a b (AND) a OR b -a (NOT) |
| 提取 | extract_regexp '"url":"(?P<url>[^"]+)"' |
| 统计 | stats count() stats by (field) count() |
| 聚合 | avg() sum() min() max() count_uniq() |
| 排序 | sort by (field) sort by (field) asc |
| 限制 | limit 10 |
记住
公式:过滤器 | 管道 = 先找日志,再加工
必须:每个查询都要有 _time
最佳实践:
- 总是加时间过滤
- 优先用索引字段(
service_name、log_type) - 先过滤再处理
- 测试时用
limit限制结果