关于 observability-logs-search
使用 ES|QL 迭代筛选 Elasticsearch 日志的调查技能。它帮助可观测性用户结合时间趋势和消息分类逐步缩小结果,定位错误或根因。
主要功能
- 01使用 ES|QL 搜索日志
- 02查看时间序列直方图和计数
- 03采样并分类日志消息
- 04用 KQL 迭代排除噪声
- 05查看关联日志或切换实体
适用场景
- 01在事件调查中缩小日志范围
- 02查找服务日志激增的原因
- 03从结果中过滤重复的正常消息
- 04检查关键事件前后的日志
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ elastic/ agent-skills --skill observability-logs-search安装后在 Claude Code 中输入/observability-logs-search确认已加载。
附带文件references/log-search-reference.md
详细介绍
日志搜索
搜索和筛选日志,以支持事件调查。此工作流与 Kibana Discover 类似:应用时间范围和范围筛选条件,然后逐步添加排除筛选条件(NOT),直到剩下少量有价值的日志子集——可能是根本原因,也可能是关键文档。你也可以选择在上下文中查看日志(该文档前后的日志),或切换到其他实体并开始新的搜索。仅使用 ES|QL(POST /_query);不要使用 Query DSL。
不适用的场景
- 指标或链路追踪 — 请使用专用的指标或链路追踪工具。
参数约定
对 Observability 日志搜索使用一致的名称:
| 参数 | 类型 | 描述 |
|---|---|---|
start | string | 时间范围的起始时间(Elasticsearch 日期数学表达式,例如 now-1h) |
end | string | 时间范围的结束时间(例如 now) |
kqlFilter | string | 用于缩小结果范围的 KQL 查询字符串。请勿使用 query、filter 或 kql。 |
limit | number | 要返回的日志样本数量上限(例如 10–100) |
groupBy | string | 可选的直方图分组字段(例如 log.level、service.name) |
实体筛选条件请使用 ECS 字段名称:service.name、host.name、service.environment、kubernetes.pod.name、
kubernetes.namespace。仅查询 ECS 名称;Observability 索引中的 OpenTelemetry 别名会自动映射。
精简上下文
保持上下文窗口较小。在查询的样本分支中,仅保留一部分字段;默认不要返回完整文档。使用 KEEP 的小型摘要(例如 10 个文档)约占用 1000 个 token;单个完整 JSON 文档就可能超过 4000 个 token。
推荐的日志样本 KEEP 列表:
message、error.message、service.name、container.name、host.name、container.id、agent.name、
kubernetes.container.name、kubernetes.node.name、kubernetes.namespace、kubernetes.pod.name
**消息回退顺序:**如果字段存在,请使用以下顺序中第一个非空字段:body.text(OTel)、message、error.message、
event.original、exception.message、error.exception.message、attributes.exception.message(OTel)。Observability
索引模板通常会为这些字段设置别名;构建单个用于显示的“消息”时,请优先采用此顺序。
**限制样本数量:**每次查询默认仅获取少量样本(10–20 条日志)。上限为 500;不要在一次调用中获取数千条日志。 每个筛选步骤的目的只是决定下一次调用——只有最终缩小范围后的结果才应保留在上下文中并进行摘要。
漏斗式工作流
必须迭代。 不要在查询一次后就停止。持续添加 NOT 排除噪声,直到剩余的日志模式少于 20 种
(不同的消息类别)。**迭代时始终保留完整筛选条件:**将新的 NOT 添加到之前的 KQL 中;不要“缩小视野”或删除之前的排除条件。
- **第 1 轮 — 广泛查询:**仅使用范围筛选条件(例如
service.name: advertService)和时间范围运行查询。获取 总数、直方图、日志样本和消息分类(常见模式 + 罕见模式)。 - 检查:查看直方图(何时出现峰值或下降)、示例消息和分类后的模式(fork4 = 数量最多的模式,fork5 = 罕见模式)。如果直方图显示某个时间点出现明显峰值,则在下一轮中缩小该峰值附近的时间范围(t_start、t_end)。统计剩余的不同日志 模式数量(根据分类结果);找出需要排除的高频噪声。
- 第 2 轮 — 排除噪声:在 KQL 筛选条件中为主要噪声模式添加
NOT子句。使用完整筛选条件(所有之前的 NOT 和新添加的 NOT)重新运行查询。 - 重复:持续添加
NOT子句,并使用完整筛选条件重新运行查询。不要在一两轮后就停止。继续执行,直到剩余日志模式少于 20 种(使用分类结果统计不同的消息类别)。此时剩余内容足够少,可以将其解读为值得关注的内容(错误、异常、根本原因)。 - **切换分析对象(可选):**漏斗筛选出特定实体(例如
container.id、host.name)后,再运行一次针对该实体的查询,查看它的“临终遗言”或周边上下文。 - **退一步(如有需要):**如果漏斗未能揭示根本原因,可以考虑结合上下文查看日志(关键文档前后的日志),或改用其他实体并开始全新搜索。
如果停止时日志模式仍不少于 20 种,你报告的将是噪声,而不是实际故障。每个中间结果都只用于决定下一次调用;只有最终筛选后的结果才应保留在上下文中并进行总结。
用于日志搜索的 ES|QL 模式
仅使用 ES|QL(POST /_query);不要使用 Query DSL。始终在一次请求中返回:时间序列直方图、总数、少量日志样本和消息分类(常见模式和罕见模式)。直方图是主要信号,可显示何时出现峰值或下降,并指导下一步筛选。使用 FORK 在单个查询中计算趋势、总数、样本和分类。
**FORK 输出解读:**响应包含多个结果集,由 _fork 列(或等效内容)标识。映射如下:fork1 = 趋势(每个时间桶的数量),fork2 = 总数(单行),fork3 = 日志样本,fork4 = 常见消息模式(按数量排序的前 20 种,来自最多 10k 条日志),fork5 = 罕见消息模式(按数量排序的后 20 种,来自最多 10k 条日志)。使用 fork1 找出应缩小时间范围的时段;使用 fork2 查看剩余噪声的数量;使用 fork3 决定下一步添加哪些 NOT;使用 fork4 和 fork5 查看剩余不同日志模式的数量,并选择下一步要添加的排除条件——持续迭代,直到剩余日志模式少于 20 种。
KQL 指南
- 当目标文本按预期进行分词时,优先使用短语查询以获得更高的精确度(例如:
message: "GET /health"、service.name: "advertService")。 - 如果目标不会被分词为单个词项,请使用通配符(例如:
message: *Returning*、message: *WARNING*)。不要在引号括起来的短语中使用通配符。 - 使用明确指定字段的 KQL:
service.name: "payment-api"、message: "GET /health"、NOT kubernetes.namespace: "kube-system"、error.message: * AND NOT message: "Known benign warning"。 - 按
log.level筛选(例如log.level: error)可能有用,但通常不可靠:许多日志缺少或包含错误的级别元数据(例如,所有日志的级别都是 "info",或者级别只出现在消息文本中)。排查故障时,优先按消息内容或error.message缩小范围;将log.level视为线索,而非可靠的筛选条件。 - 随机搜索 "error" 等词语的全文也通常不可靠:它会匹配无害的提及(例如 "no error"、"error code 0",以及提及该词的堆栈跟踪)。优先按服务或实体限定范围,并通过对实际消息模式逐步添加 NOT 排除项来迭代,而不要依赖单个关键词。
使用直方图、样本和分类进行基本日志搜索
加入消息分类,以便统计不同的日志模式并逐步迭代,直到剩余模式少于 20 个。使用五路 FORK:趋势、总量、样本、常见模式、罕见模式。
POST /_query
{
"query": "FROM logs-* METADATA _id, _index | WHERE @timestamp >= TO_DATETIME(\"2025-03-06T10:00:00.000Z\") AND @timestamp <= TO_DATETIME(\"2025-03-06T11:00:00.000Z\") | FORK (STATS count = COUNT(*) BY bucket = BUCKET(@timestamp, 1m) | SORT bucket) (STATS total = COUNT(*)) (SORT @timestamp DESC | LIMIT 10 | KEEP _id, _index, message, error.message, service.name, container.name, host.name, kubernetes.container.name, kubernetes.node.name, kubernetes.namespace, kubernetes.pod.name) (LIMIT 10000 | STATS COUNT(*) BY CATEGORIZE(message) | SORT `COUNT(*)` DESC | LIMIT 20) (LIMIT 10000 | STATS COUNT(*) BY CATEGORIZE(message) | SORT `COUNT(*)` ASC | LIMIT 20)"
}
- fork4(常见):按数量列出前 20 个消息模式,最多分析 10,000 条日志——用于添加 NOT 排除项,以过滤主要噪声。
- fork5(罕见):按数量列出后 20 个消息模式——有助于在大海捞针时发现目标。
统计 fork4、fork5 和整体分类中的不同模式数量,并继续迭代,直到剩余的日志模式少于 20 个。
根据需要调整索引模式(例如 logs-*、logs-*-*)、时间范围和分桶大小(例如 30s、5m、1h)。样本 LIMIT 保持较小(默认 10–20;上限为 500)。使用 KEEP,让样本分支只返回汇总字段,而不是完整文档。
添加 KQL 筛选条件
使用 KQL("...") 缩小结果范围。在 ES|QL 中,KQL 表达式是一个由双引号括起来的字符串。
**请求正文中的转义:**查询位于 JSON 中,因此 ES|QL 字符串中的每个双引号都必须转义。使用 \" 包裹 KQL 表达式。如果 KQL 表达式本身包含双引号(例如短语 message: "GET /health"),则在 JSON 中将这些引号转义为 \\\",这样 KQL 解析器才能收到字面引号字符。
POST /_query
{
"query": "FROM logs-* METADATA _id, _index | WHERE @timestamp >= TO_DATETIME(\"2025-03-06T10:00:00.000Z\") AND @timestamp <= TO_DATETIME(\"2025-03-06T11:00:00.000Z\") | WHERE KQL(\"service.name: checkout AND log.level: error\") | FORK (STATS count = COUNT(*) BY bucket = BUCKET(@timestamp, 1m) | SORT bucket) (STATS total = COUNT(*)) (SORT @timestamp DESC | LIMIT 10 | KEEP _id, _index, message, error.message, service.name, host.name, kubernetes.pod.name) (LIMIT 10000 | STATS COUNT(*) BY CATEGORIZE(message) | SORT `COUNT(*)` DESC | LIMIT 20) (LIMIT 10000 | STATS COUNT(*) BY CATEGORIZE(message) | SORT `COUNT(*)` ASC | LIMIT 20)"
}
使用 NOT 排除噪声
通过排除已知噪声来逐步缩小范围。在请求正文中,使用 \"...\" 包裹 KQL 字符串,并将 KQL 表达式中的任何引号转义为 \\\":
"query": "... | WHERE KQL(\"NOT message: \\\"GET /health\\\" AND NOT kubernetes.namespace: \\\"kube-system\\\"\") | ..."
"query": "... | WHERE KQL(\"error.message: * AND NOT message: \\\"Known benign warning\\\"\") | ..."
按维度分组的直方图
按第二个维度(例如 log.level、service.name)拆分趋势,以查看哪些级别或实体导致了突增:
STATS count = COUNT(*) BY bucket = BUCKET(@timestamp, 1m), log.level
限制响应中的分组值数量,以避免数据量激增(例如,按数量列出前 N 个,其余归为 _other)。
示例
某个服务最近一小时的日志
POST /_query
{
"query": "FROM logs-* METADATA _id, _index | WHERE @timestamp >= NOW() - 1 hour AND @timestamp <= NOW() | WHERE KQL(\"service.name: api-gateway\") | SORT @timestamp DESC | LIMIT 20"
}
错误日志趋势与样本
POST /_query
{
"query": "FROM logs-* METADATA _id, _index | WHERE @timestamp >= NOW() - 2 hours AND @timestamp <= NOW() | WHERE KQL(\"log.level: error\") | FORK (STATS count = COUNT(*) BY bucket = BUCKET(@timestamp, 5m) | SORT bucket) (STATS total = COUNT(*)) (SORT @timestamp DESC | LIMIT 15)"
}
迭代漏斗:不断添加 NOT,直到找到有价值的信息
不要只排除一次。每一轮都针对当前最主要的噪声添加更多 NOT,然后重新运行。
第 1 轮: KQL("service.name: advertService") → 例如 55k 条日志;样本显示“Returning N ads”、“WARNING:
request...”和“received ad request”。
第 2 轮: 排除最大的噪声:
KQL("service.name: advertService AND NOT message: *Returning* AND NOT message: *WARNING*") → 重新运行,并查看新的总数
和样本。
第 3 轮: 排除下一类噪声(例如请求/缓存相关消息):
KQL("service.name: advertService AND NOT message: *Returning* AND NOT message: *WARNING* AND NOT message: *received ad request* AND NOT message: *Adding* AND NOT message: *Cache miss*") →
重新运行。
第 4 轮及以后: 针对样本中仍占主导的内容继续添加 NOT(使用 fork4/fork5 分类查看 模式)。持续操作,直到剩余的日志模式少于 20 个;此时剩下的就是需要报告的有效信息(例如“获取广告时出错”、 编码问题)。
转义:在 JSON 中用 \"...\" 包裹 KQL 字符串;对于 KQL 中的带引号短语,使用 \\\"。
指南
- 漏斗:迭代使用 NOT。 不要只运行一次宽泛查询就报告发现。针对主要噪声添加 NOT 子句,使用完整筛选条件 (保留之前的所有 NOT)重新运行,并重复操作,直到剩余的日志模式少于 20 个(使用 分类 fork4/fork5 计数)。过早停止会得到噪声,而非有效信息。
- 先看直方图: 使用趋势(fork1)查看尖峰或下降发生的时间;如有需要,在添加更多 NOT 之前缩小尖峰附近的时间范围。
- 尽量减少上下文: 样本分支中只保留摘要字段;默认 LIMIT 为 10–20,上限为 500。每个漏斗步骤都用于决定下一次调用;只有最终缩小范围后的结果才用于上下文和摘要。
- 请求正文转义:
query的值是 JSON。转义 ES|QL 字符串中的双引号:KQL 封装层使用\",KQL 表达式内部的引号(例如短语值)使用\\\"。 - 以编程方式构建查询时,
start和end使用 Elasticsearch 日期数学(例如now-1h、now-15m)。 - 根据时间范围选择分桶大小:目标是大约 20–50 个分桶(例如,1h 时间窗口使用
1m或2m)。 - 优先使用 ECS 字段名。在 Observability 索引模板中,OTel 字段已别名为 ECS;资源元数据字段的回退方案(容器、主机、集群、命名空间、pod、工作负载)请参阅 references/log-search-reference.md。
log.level: 按此字段筛选或分组有时可行,但当日志级别缺失或设置错误时,结果往往不可靠; 查找故障时优先使用消息内容或error.message。- 关键词搜索: 只搜索“error”或“fail”等词通常有缺陷(例如“no error”、“error code 0”);优先限定实体范围,并根据实际消息模式通过 NOT 逐步筛选。
参考资料
- references/log-search-reference.md — ECS/OTel 字段映射和索引模式