关键词搜索在字段过滤、聚合统计和多条件组合时力不从心。本文介绍炬鲸 OBSERVE 的 SQL 日志检索:自动字段提取、类 SQL 查询语法,以及三个定位线上问题的真实查询示例。
排查线上问题时,最常见的动作是「grep 一下」。但 grep 式检索有三个明显的天花板。一是没法按字段过滤:想看 status>=500 且耗时超过 2 秒的请求,关键词只能匹配到日志里恰好出现的那几个词,漏检和误检都很多。二是没法聚合:想知道过去一小时 5xx 错误按服务、按接口怎么分布,关键词搜索给不了答案。三是多条件组合要么靠正则硬拼,要么靠人工一页页翻,既慢又容易漏。真正的代价在故障时最明显:工程师一边手写正则,错误率一边往上爬,一条匹配到无关行的查询就能把整个团队带偏。
日志本质上是结构化的——时间戳、级别、服务名、trace_id、状态码、耗时,这些字段就躺在每一行里,只是没被拆开。把它们拆开,日志就能像数据库表一样被查询,排障的思路也会从「找一行日志」变成「写一条查询」。用 SQL 查日志,本质是把运维排障的经验固化成可复用、可分享的查询,而不是每次靠个人手感去 grep。
炬鲸 OBSERVE 在日志接入时做两件事:一是解析常见格式(JSON、Nginx、Spring Boot 默认格式)自动抽出字段;二是对非标准格式支持自定义 Grok / 正则解析规则。下面是一条 Nginx 访问日志的解析配置:
log_format main '$remote_addr [$time_local] $request $status $body_bytes_sent $request_time';
接入后,每行日志会生成 remote_addr、status、request_time、request 等列。字段类型也会被推断:status 是数值、request_time 是浮点、time_local 是时间戳,这样聚合和区间过滤才有意义——没有数值类型,HAVING avg_rt > 2 这种比较就会退化成字符串比较,悄悄返回错误结果。如果是 JSON 日志,字段天然就是键值对,几乎零配置;如果是老系统的自定义文本格式,写一条 Grok 规则即可,比如把方括号里的时间戳抽成 time_local,一条规则可以在所有共享同一格式的服务里复用。
场景一:找慢接口。按接口聚合平均耗时,只看超过阈值的:
SELECT request, avg(request_time) AS avg_rt, count(*) AS cnt
FROM nginx_access
WHERE time_local >= now() - interval 1 hour
GROUP BY request
HAVING avg_rt > 2
ORDER BY avg_rt DESC
LIMIT 20
场景二:追踪一次故障的完整链路。日志里带上 trace_id,配合链路追踪可以直接下钻:
SELECT * FROM app_log
WHERE trace_id = 'a1b2c3d4e5f6'
ORDER BY time_local ASC
场景三:告警前的异常排查。找出最近 10 分钟错误率突增的服务:
SELECT service, count(*) FILTER (WHERE level = 'ERROR') AS err_cnt
FROM app_log
WHERE time_local >= now() - interval 10 minute
GROUP BY service
ORDER BY err_cnt DESC
一条已保存查询不必停在被动查询。给它挂上调度周期和阈值,就能变成告警:比如每 5 分钟跑一次「慢接口」查询,任何接口平均耗时超过 2 秒就通知;或每分钟跑一次「按服务错误率」,某服务 10 分钟内错误数超过 20 就告警。好处是精准——告警里能直接点名是哪个接口、具体多少个错误,被叫醒的人打开告警就知道该看哪,而不是看到一条笼统的「5xx 升高」。
两个机制让这件事可靠:一是固定滚动窗口跑查询(比如最近 5 分钟),让每次结果可比;二是把「查不到数据」也当成信号——查询突然返回零行,可能是系统挂了,而不是一切正常。把阈值触发和零结果两种情况都写进告警条件。
第一,日志格式从接入第一天就定好规范,统一用 JSON 输出、字段名固定,避免后续改解析规则。第二,耗时、状态码、trace_id 这三个字段务必落库,这是绝大多数排障查询的入口。第三,把常用查询存成「已保存查询」,团队成员直接复用,别让每个人重写一遍 SQL。第四,查询语句务必加上时间范围过滤,避免全表扫描拖慢检索。第五,搭一个小的查询库——「慢接口」「按服务错误率」「单条链路」——再把这些查询挂到仪表盘和告警通知里,让被叫醒的人直接落到相关查询上,而不是从零开始。
SQL 检索不是替代关键词搜索,而是补上它够不着的部分:字段级过滤、聚合、关联。等你习惯了用 SQL 查日志,就回不去纯 grep 了。