炬鲸 OBSERVE 内置类 SQL 检索语法,配合采集端 Pipeline 字段解析与倒排索引,在十亿级日志上实现秒级聚合查询。本文介绍语法用法、字段来源与查询性能优化建议。
运维排查线上问题,一半时间耗在找日志上。grep 大文件、翻正则、等滚动刷新——这些操作在几百台机器、每天几十亿条日志面前基本失效。日志平台的核心价值不是"存得下",而是"查得快、查得准"。
炬鲸 OBSERVE 的日志检索没有走传统的关键词加过滤条件路线,而是直接内置了一套类 SQL 的查询语法。你不需要记住某个平台私有的 DSL,会用 SQL 就能上手。对熟悉 MySQL 或 ClickHouse 的工程师来说,学习成本几乎为零。
以 Nginx 访问日志为例,一条结构化日志包含 status、upstream_time、request_uri 等字段。按状态码统计最近一小时:
SELECT status, count(*) AS cnt
FROM nginx_access
WHERE time >= now() - interval '1 hour'
GROUP BY status
ORDER BY cnt DESC
再比如找出响应最慢的 20 个接口:
SELECT request_uri, max(upstream_time) AS slowest
FROM nginx_access
WHERE upstream_time > 2
GROUP BY request_uri
ORDER BY slowest DESC
LIMIT 20
语法覆盖了 SELECT、WHERE、GROUP BY、ORDER BY、LIMIT,以及 count、sum、avg、max、min、percentile 等聚合函数。要算接口耗时的 P99,直接写 percentile(upstream_time, 99) 即可,不用把数据拉到本地再算。
子查询和 JOIN 也已上线。跨日志流做关联分析时,可以像这样把访问日志和业务日志按请求 ID 关联:
SELECT a.request_id, b.user_id, a.upstream_time
FROM nginx_access a
JOIN app_log b ON a.request_id = b.request_id
WHERE a.status >= 500
AND a.time >= now() - interval '10 minute'
SQL 能查的前提是字段结构化。炬鲸 OBSERVE 在采集端内置了 Pipeline 解析器,支持 JSON、正则、分隔符、key-value 四种解析方式。以 Go 服务为例,配置一行采集规则即可把 stdout 的 JSON 日志拆成独立字段:
input:
tail:
paths: ["/var/log/app/*.log"]
pipeline:
- json:
source: message
- geoip:
source: remote_ip
解析后的字段进入倒排索引,同时保留原始 message,方便回看上下文。字段类型(string、number、ip、boolean)会自动推断:"200" 这类字符串会被识别为 number,"10.0.0.1" 识别为 ip,这样数值字段才能参与排序和聚合,IP 字段才能按网段过滤。类型推断错了也没关系,在字段管理页可以手动覆盖某个字段的类型。
习惯 grep 的工程师第一次用会不习惯两件事:一是结果默认按时间倒序,而不是按文件里的出现顺序;二是查询有超时限制(默认 30 秒),一条 LIKE '%x%' 的裸查询在十亿级数据上会被直接拦下来。这不是限制,是保护——它逼着你在查询前先想清楚时间范围和关键词。
如果之前用的是 Elasticsearch 的 Query DSL,迁移成本也很低。OBSERVE 的查询面板支持"SQL 模式"和"可视化模式"一键切换:不会写 SQL 就点选字段和条件,面板自动生成 SQL,生成的 SQL 还能复制出来贴到告警规则里复用。
为了让 SQL 在十亿级日志上仍能秒级返回,检索层做了三件事:按时间分片(bucket)的列式存储、字段级倒排索引、以及查询计划下推到分片。实际使用中,一个小时的日志按 status 聚合,P95 延迟在 1 秒以内。
几个实践建议:
LIKE '%keyword%' 这种前缀通配,改用倒排索引支持的关键词匹配;这套检索引擎同时给告警和链路两个模块复用,你在告警规则里写的过滤表达式,和检索语法是一致的,不用学两套东西。