关于 oodle-logs
oodle-logs 是一项使用 OpenSearch Query DSL 搜索 Oodle 日志并发现索引模式的技能,指导 Agent 在查询前确认模式和字段,并指定时间范围。
主要功能
- 01发现日志索引模式和字段
- 02用 OpenSearch Query DSL 构建 NDJSON 查询
- 03按指定时间范围搜索日志
- 04按日志字段聚合结果
- 05用 jq 在客户端提取纯文本值
适用场景
- 01调查时查找匹配的日志事件
- 02按服务统计错误
- 03检查历史日志行并提取其中的值
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ oodle-ai/ agent-skills --skill oodle-logs安装后在 Claude Code 中输入/oodle-logs确认已加载。
详细介绍
Oodle 日志 — 搜索与索引模式发现
此技能教会 Agent 使用兼容 OpenSearch 的查询 DSL 搜索日志数据,并发现可用的日志索引模式。
前置条件
brew install oodle-ai/oodle/oodle
oodle configure
确认日志端点可用:
oodle logs index-patterns -o json | jq 'length'
命令执行顺序
运行任何日志查询之前:
- 运行
oodle logs index-patterns -o json,发现可用的日志索引模式。 - 为查询选择合适的索引模式
title。 - 使用发现的索引模式和 OpenSearch Query DSL 构建 NDJSON 查询文件。
- 设置
--start和--end,覆盖所需的时间范围。默认值分别为-1h和now。 - 不要猜测索引名称——务必先通过
index-patterns确认。
快速参考
| 任务 | 命令 |
|---|---|
| 列出日志索引模式 | oodle logs index-patterns -o json |
| 搜索日志 | oodle logs query -f query.ndjson --start <start> --end <end> -o json |
常见操作
列出日志索引模式
构建日志查询前,先发现可用的日志索引模式。返回的 title 字段将用作 NDJSON 查询文件中的 index 值。
# CORRECT — list all available index patterns
oodle logs index-patterns -o json
# CORRECT — extract just the pattern titles
oodle logs index-patterns -o json | jq '.[].title'
# CORRECT — use a discovered pattern in a log query
oodle logs index-patterns -o json | jq '.[].title'
# Then use the title in your NDJSON file:
# {"index": "logs-*"}
# {"query": {"match_all": {}}, "size": 10}
日志查询
使用兼容 OpenSearch 的多重搜索 API 搜索日志数据。请求正文采用 NDJSON 格式,且必须恰好包含两行 JSON:第一行用于选择索引,第二行包含使用 OpenSearch Query DSL 编写的搜索查询。通过 -f <file> 传入正文。
重要限制:
- CLI 仅支持单个搜索正文(恰好 2 行 NDJSON:一个标头 + 一个搜索)。不支持多个搜索对。
- 使用
--start和--end标志筛选时间范围。值可以是 Unix 时间戳(毫秒;例如1716825600000)、RFC 3339 格式(例如2026-01-02T00:00:00Z)、now或相对时间表达式(例如-1h、-24h、-7d)。 --start默认值为-1h,--end默认值为now。如果要搜索超过 1 小时之前的数据,必须显式传入--start,否则查询会静默返回 0 条结果。- 不要在查询正文中对
timestamp设置范围筛选条件——CLI 会根据--start/--end自动注入timestamp范围筛选条件。
# CORRECT — basic query (searches last 1 hour by default)
cat > query.ndjson <<'EOF'
{"index": "logs-*"}
{"query": {"match_all": {}}, "size": 10}
EOF
oodle logs query -f query.ndjson -o json
# CORRECT — search for specific log messages with explicit time range
cat > query.ndjson <<'EOF'
{"index": "logs-*"}
{"query": {"bool": {"must": [{"match_phrase": {"message": "error"}}]}}, "size": 50, "sort": [{"timestamp": {"order": "desc"}}]}
EOF
oodle logs query -f query.ndjson --start -6h --end now -o json
# CORRECT — search older data using relative time
cat > query.ndjson <<'EOF'
{"index": "logs-*"}
{"query": {"bool": {"must": [{"match_phrase": {"message": "OOMKilled"}}]}}, "size": 100}
EOF
oodle logs query -f query.ndjson --start -7d --end -6d -o json
# CORRECT — search older data using epoch milliseconds
cat > query.ndjson <<'EOF'
{"index": "logs-*"}
{"query": {"match_phrase": {"message": "connection refused"}}, "size": 50}
EOF
oodle logs query -f query.ndjson --start 1716825600000 --end 1716912000000 -o json
# WRONG — omitting --start when searching for data older than 1 hour
cat > query.ndjson <<'EOF'
{"index": "logs-*"}
{"query": {"match_phrase": {"message": "yesterday's deploy error"}}, "size": 50}
EOF
oodle logs query -f query.ndjson -o json
# ^^^ Returns 0 results! --start defaults to -1h, so older data is excluded.
# FIX: add --start -24h (or whatever range covers the target timeframe)
# WRONG — putting a range filter on timestamp in the query body
cat > query.ndjson <<'EOF'
{"index": "logs-*"}
{"query": {"bool": {"must": [{"range": {"timestamp": {"gte": "now-1h", "lte": "now"}}}]}}, "size": 100}
EOF
# The CLI already injects a timestamp range from --start/--end. In-query range filters conflict.
# FIX: use --start and --end flags instead.
# WRONG — passing the query inline without -f
oodle logs query --body '{"index":"logs-*"}'
# WRONG — using JSON instead of NDJSON format
oodle logs query -f query.json # file must be valid NDJSON (exactly two lines)
聚合
将 OpenSearch 聚合放在 aggs 块中。只需要聚合结果时,将 "size": 0。使用 jq '.responses[0].aggregations' 读取结果。下方的字段名称仅为示例;请通过 oodle logs index-patterns 确认。数值聚合需要使用数值字段,而非 message 中的文本。
# CORRECT — error lines per service (top 20)
cat > query.ndjson <<'EOF'
{"index": "logs-*"}
{"size": 0, "query": {"term": {"level": "error"}}, "aggs": {"by_service": {"terms": {"field": "service_name", "size": 20}}}}
EOF
oodle logs query -f query.ndjson --start -24h --end now -o json \
| jq -r '.responses[0].aggregations.by_service.buckets[] | "\(.key)\t\(.doc_count)"'
# CORRECT — error lines per hour
cat > query.ndjson <<'EOF'
{"index": "logs-*"}
{"size": 0, "query": {"match_phrase": {"message": "error"}}, "aggs": {"per_hour": {"date_histogram": {"field": "timestamp", "fixed_interval": "1h"}}}}
EOF
oodle logs query -f query.ndjson --start -24h --end now -o json \
| jq -r '.responses[0].aggregations.per_hour.buckets[] | "\(.key_as_string)\t\(.doc_count)"'
纯文本日志行中的值
不支持 regexp 查询、脚本和运行时字段。因此,日志查询无法筛选或分组纯文本行中仅存在于文本里的值。对于 tool Bash ok in 12345 ms 这样的行,查询无法筛选出“超过 10000 ms”的记录,也无法按工具名称分组。不要告诉用户 Oodle 无法做到。请提供以下方案之一:
- 日志指标规则(oodle-log-metrics 技能):使用正则表达式提取值,并将其存入带标签的指标。适用于对新日志进行计数、延迟统计和告警。
- 历史数据:使用
match_phrase查找日志行,并在客户端使用jq提取值。
| 需求 | 使用方式 |
|---|---|
| 查找日志行、读取事件、调查历史数据 | oodle logs query |
| 对真实字段进行计数或分组,查询历史数据 | 使用 aggs 的 oodle logs query |
| 按仅存在于纯文本中的值筛选或分组 | 日志指标规则(新数据),或在客户端使用 jq(历史数据) |
| 对日志行告警(计数、速率、延迟、按用户) | 日志指标规则 + 监控器 |
| 在仪表板上查看数周趋势 | 日志指标规则 |
示例:过去 24 小时内哪些用户的额度用尽了?
首先检查是否已有日志指标规则统计这些行:oodle log-metrics list -o json。如果有,请读取其指标,例如 oodle metrics query --query 'sum by (user) (increase(oodle_logs_credit_exhausted[24h])) > 0' -o json。规则只会统计创建后到达的行,因此查询较早的数据时,请使用下面的日志。
cat > credit.ndjson <<'EOF'
{"index": "logs-*"}
{"query": {"match_phrase": {"message": "credit exhausted"}}, "size": 1000, "sort": [{"timestamp": {"order": "desc"}}]}
EOF
# If the user is a real field (for example user_id): use a terms aggregation on it.
# If the user is only inside the text ("... for user=alice ..."): extract it on the client.
oodle logs query -f credit.ndjson --start -24h --end now -o json \
| jq -r '.responses[0].hits.hits[]._source.message | capture("user=(?<user>[^ ]+)").user' \
| sort | uniq -c | sort -rn
如果查询返回 size 行,实际结果可能不止这些:拆分时间范围(例如,查询四次,每次 6 小时),然后合并结果。若要从现在起按用户触发告警,请创建一个使用正则表达式 user=(\S+) 提取 user 的日志指标规则,并设置监控项 sum by (user) (increase(oodle_logs_credit_exhausted[10m]))。规则 JSON 见 oodle-log-metrics 技能。
最佳实践
查询前先发现索引模式
始终运行 oodle logs index-patterns 以查找正确的索引名称。猜测索引名称可能会返回空结果或错误。
# CORRECT — discover first, then query
oodle logs index-patterns -o json | jq '.[].title'
# Use the discovered title in your NDJSON file
# WRONG — guessing index names
cat > query.ndjson <<'EOF'
{"index": "my-logs"}
{"query": {"match_all": {}}, "size": 10}
EOF
oodle logs query -f query.ndjson -o json
进行时间范围筛选时,始终使用 --start 和 --end
CLI 会根据 --start/--end 自动注入 timestamp 范围筛选器。不要在查询正文中自行添加 timestamp 范围筛选器,否则会与 CLI 注入的筛选器冲突。
# CORRECT — use CLI flags for time range
oodle logs query -f query.ndjson --start -24h --end now -o json
# CORRECT — use epoch milliseconds for precise ranges
oodle logs query -f query.ndjson --start 1716825600000 --end 1716912000000 -o json
# WRONG — relying on the default 1-hour window for older data
oodle logs query -f query.ndjson -o json
# ^^^ Only searches the last 1 hour. If the data you need is older, you get 0 results.
# WRONG — putting range filters on timestamp in the query body
{"query": {"bool": {"must": [{"range": {"timestamp": {"gte": "now-6h"}}}]}}}
# FIX: remove the range from the query body and use --start -6h instead.
使用 -o json 获取日志查询结果
日志查询响应包含嵌套 JSON 结构。始终使用 -o json,并通过管道将结果传给 jq 进行提取。
# CORRECT — extract log messages from the response
oodle logs query -f query.ndjson -o json | jq '.responses[].hits.hits[]._source.message'
# WRONG — using table output for complex nested log data
oodle logs query -f query.ndjson -o table
使用索引模式字段构建精准查询
每种索引模式都包含一个 fields 数组,其中列出了字段名称和类型。请用这些字段构建有针对性的查询,而不是使用 match_all。
# CORRECT — check available fields first
oodle logs index-patterns -o json | jq '.[0].fields[].name'
# Then build a query using confirmed field names
故障处理
| 错误 | 原因 | 修复方法 |
|---|---|---|
| 401 Unauthorized | API 密钥无效或缺失 | 运行 oodle configure 或设置 OODLE_API_KEY |
| 400 Bad Request | NDJSON 正文或 Query DSL 无效 | 验证 NDJSON 文件是否恰好有两行;检查 OpenSearch Query DSL 语法 |
| 结果为空(非错误) | 索引或时间范围内没有匹配的日志 | 扩大时间范围,或使用 oodle logs index-patterns 检查索引名称 |
| 较早数据的结果为 0 | --start 默认为 -1h;此时间窗口以外的数据会被排除 | 对较早的数据传入显式的 --start,例如 --start -24h 或 --start <epoch_ms> |
| connection refused | OODLE_DEPLOYMENT URL 错误 | 检查 OODLE_DEPLOYMENT 环境变量,并确保末尾没有斜杠 |
| 429 Too Many Requests | 请求频率受限 | 添加 --retries 3,延长重试间隔,并降低查询频率 |
regexp、script 或运行时字段出现 400 错误 | 不受支持 | 使用 match_phrase 或日志指标规则(参见“纯文本日志行中的值”) |
required flag(s) "file" not set | 日志查询缺少必需标志 | 添加 -f 标志并指定 NDJSON 文件的路径 |