介绍炬鲸的 SQL 式日志检索能力:用类 SQL 语法对日志做过滤、聚合、分组和时间分桶,替代 grep 与正则的反复试错,附常用查询范式与性能建议。
排查线上问题时,多数人的第一反应是 grep 加管道:先 grep error,再 grep -v 掉噪音,接着 awk 取字段,最后 sort | uniq -c 做统计。这套流程在几十兆日志里还能应付,一旦碰上每小时 TB 级、跨几十个节点的日志,grep 的单机扫描会变得极慢,而且管道越写越长,下一次排查又得重写一遍。
炬鲸把日志检索做成了类 SQL 的查询语言。你会得到三样东西:一套稳定的查询语法(不用记正则转义)、一次能跑完整份数据的聚合能力(不是抽样)、以及可复用的查询(存下来下次直接用)。
最常用的语句是 SELECT + WHERE,字段来自日志解析后提取的结构化字段,比如 level、service、host、message 以及自定义字段。
SELECT timestamp, host, message
FROM logs
WHERE level = 'ERROR'
AND service = 'order-service'
AND timestamp >= now() - 1h
ORDER BY timestamp DESC
LIMIT 100
字段比较支持 =、!=、>、<、IN、LIKE。要模糊匹配,用 LIKE '%timeout%' 或 message CONTAINS 'connection refused'。IN 适合一次圈定多个服务:service IN ('order-service', 'pay-service')。
一个容易踩的坑:字段名必须用解析后的标准名。如果日志里写的是 loglevel,解析后映射成了 level,查询时用 loglevel 会返回空结果而不是报错。建议先看一条样例日志确认字段。
grep 只能告诉你有多少行匹配,聚合才能告诉你问题集中在哪。下面这个查询统计过去一小时各服务的错误数分布:
SELECT service, count(*) AS err_count
FROM logs
WHERE level = 'ERROR'
AND timestamp >= now() - 1h
GROUP BY service
ORDER BY err_count DESC
再做一层下钻,看某个错误在哪些主机、哪个版本上集中出现:
SELECT host, version, count(*) AS cnt
FROM logs
WHERE service = 'order-service'
AND level = 'ERROR'
GROUP BY host, version
ORDER BY cnt DESC
LIMIT 20
聚合函数除 count 外,还支持 sum、avg、min、max、p50、p99、uniq(近似去重)。p99 和 uniq 在做性能分析时尤其有用:比如 SELECT p99(duration) FROM logs WHERE service = 'api-gateway' 能直接看出尾延迟。
按时间分桶是把「当前有没有问题」变成「问题什么时候开始的」的关键:
SELECT time_bucket(timestamp, '5m') AS t, count(*) AS cnt
FROM logs
WHERE service = 'order-service' AND level = 'ERROR'
GROUP BY t
ORDER BY t
time_bucket 支持 s、m、h、d 单位。把结果画成折线,一眼就能看出错误是从哪个时间点开始飙升的,再结合那段时间的发布记录,基本能锁定诱因。
几个存下来就能直接上手的查询:错误飙升(上面的 GROUP BY 查询,共享给值班同事);慢请求 SELECT p99(duration), count(*) FROM logs WHERE service = 'api-gateway' GROUP BY route ORDER BY p99 DESC;鉴权失败 WHERE message CONTAINS 'invalid credentials' GROUP BY host,通常能揪出某一台节点上出错的凭证轮换。
WHERE 里的时间字段加范围限定,避免全量扫描。service =)放在前面。LIMIT 而不是 ,SELECT 会把整条原始日志拖回来,慢且占内存。SQL 式检索的价值不在于「又学了一种语法」,而在于把排查流程从临时管道变成可沉淀、可复用、可协作的资产。