关于 oodle-log-metrics
oodle-log-metrics 是一项指导 Agent 将日志行转换为指标的技能,重点包括创建前验证过滤条件、控制标签基数,以及更新时保留现有规则。
主要功能
- 01创建规则前验证日志过滤条件
- 02估算标签基数
- 03定义计数器、仪表和直方图指标
- 04用正则提取标签和值
- 05更新前读取现有规则
适用场景
- 01将匹配的日志行计为指标
- 02根据日志事件创建告警
- 03测量纯文本日志中的工具延迟
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ oodle-ai/ agent-skills --skill oodle-log-metrics安装后在 Claude Code 中输入/oodle-log-metrics确认已加载。
详细介绍
Oodle 日志指标 — 规则与基数
本技能介绍如何安全地将日志流转换为指标:验证筛选条件、缩小 labels 范围,并避免创建高基数时间序列。日志指标规则也可用于统计日志行中的值或针对这些值触发告警:监控器会针对规则生成的指标触发告警。
前置条件
brew install oodle-ai/oodle/oodle
oodle configure
确认日志指标端点可用:
oodle log-metrics list -o json | jq 'length'
命令执行顺序
运行任何 oodle 命令之前:
- 检查规则的筛选条件和
labels是否已在上下文中。 - 如果没有,运行示例日志查询(例如
oodle logs query,请参阅 oodle-logs 技能,或使用日志浏览器),验证筛选条件确实能匹配日志。 - 估算基数:将每个标签的不同值数量相乘。
- 仅在验证筛选条件后运行
oodle log-metrics create -f rule.json。 - 运行
update前,务必先运行get,以获取现有规则。
快速参考
| 任务 | 命令 |
|---|---|
| 列出规则 | oodle log-metrics list -o json |
| 获取规则 | oodle log-metrics get <id> -o json |
| 创建规则 | oodle log-metrics create -f rule.json |
| 更新规则 | oodle log-metrics update <id> -f rule.json |
| 删除规则 | oodle log-metrics delete <id> --force |
常见操作
规则架构
{
"name": "http_errors_from_logs",
"filter": {
"all": [
{"field": "level", "operator": "is", "value": "error"},
{"field": "service", "operator": "is", "value": "api"}
]
},
"labels": [
{"name": "service", "valueExtractor": {"field": "service"}},
{"name": "env", "valueExtractor": {"field": "env"}}
],
"metricDefinitions": [
{"name": "http_errors", "type": "log_count"}
]
}
字段含义:
| 字段 | 含义 |
|---|---|
name | 规则的人类可读标识符。必填。 |
filter | 规则读取哪些日志行。可以是条件 {"field", "operator", "value", "jsonPath"},也可以是 {"all": [...]}(AND)、{"any": [...]}(OR)或 {"not": <filter>}。可以嵌套。运算符:is、contains、matches regex、exists。没有 !=;请使用 not。 |
labels | 添加到生成指标中的标签。每个标签包含一个 name 和静态 value,或一个 valueExtractor {"field", "jsonPath", "regex"}。 |
metricDefinitions | 要生成的指标。至少需要一个。每个指标包含一个 name 和一个 type:log_count(匹配行数)、counter(数值总和)、gauge(最后一个值)或 histogram(数值分布)。counter、gauge 和 histogram 从 field 读取数值,可选使用 jsonPath 或 regex。 |
field是顶层日志字段:只能包含字母、数字和下划线。使用jsonPath读取嵌套的 JSON 值,例如{"field": "log", "jsonPath": "user.id"}。regex使用 Rust 语法。捕获组 1 是要提取的值,因此请在所需部分外加一个分组。在 JSON 中,每个反斜杠都要转义:\d应写为"\\d"。- 如果正则表达式与某行不匹配,标签值为空,数值为
0。因此,筛选条件应仅选取包含该值的行。 - 生成的指标名称为
oodle_logs_<name>(oodle_logs_http_errors)。histogram会生成_bucket、_sum和_count系列。使用increase(...)或rate(...)查询log_count和counter,并使用sum by (...)聚合。 - 规则仅适用于创建后到达的日志。对于过去的数据,请使用
oodle logs query。 - 如示例所示,在
all列表中放入单个条件。这种写法适用于所有 CLI 和服务版本。
创建规则
# ✅ CORRECT — validate filter first, then create
# (preview with the log explorer or `oodle logs query`)
oodle log-metrics create -f rule.json
# ❌ WRONG — creating with an untested filter; the resulting metric is silently empty
oodle log-metrics create -f <(echo '{"name":"x","filter":{"all":[{"field":"levl","operator":"is","value":"eror"}]},"metricDefinitions":[{"name":"x","type":"log_count"}]}')
更新规则
# ✅ CORRECT — get → edit → update
oodle log-metrics get lm_123 -o json > rule.json
jq '.labels = [{"name":"service","valueExtractor":{"field":"service"}},{"name":"env","valueExtractor":{"field":"env"}}]' rule.json > rule.new.json
oodle log-metrics update lm_123 -f rule.new.json
# ❌ WRONG — partial payload removes existing fields (and has no `id`, so it is rejected)
oodle log-metrics update lm_123 -f <(echo '{"labels":[{"name":"service","valueExtractor":{"field":"service"}}]}')
删除规则
# ✅ CORRECT — verify and delete
oodle log-metrics get lm_123 -o json > /dev/null
oodle log-metrics delete lm_123 --force
# ❌ WRONG — speculative delete by name match
oodle log-metrics delete "$(oodle log-metrics list | grep errors | awk '{print $1}')" --force
对日志行发出告警
监控器运行的是 PromQL,而不是日志查询。要针对日志行发出告警,请创建日志指标规则,然后创建一个监控器来监控该规则生成的指标(参见 oodle-monitors 技能)。规则使用正则表达式读取原始行文本,因此适用于纯文本行,无需进行日志转换。从行中提取的标签(user、tool、tenant)会用来标识告警中的时间序列。
示例:对于以下日志行,为每个信用额度耗尽的用户分别发出告警:
2026-10-07T10:12:03Z WARN credit exhausted for user=alice plan=pro
credit-exhausted.json:
{
"name": "credit-exhausted",
"filter": {"all": [{"field": "message", "operator": "contains", "value": "credit exhausted"}]},
"labels": [
{"name": "user", "valueExtractor": {"field": "message", "regex": "user=(\\S+)"}}
],
"metricDefinitions": [{"name": "credit_exhausted", "type": "log_count"}]
}
credit-exhausted-monitor.json(op 2 表示 >):
{
"name": "Credit exhausted",
"promql_query": "sum by (user) (increase(oodle_logs_credit_exhausted[10m]))",
"conditions": {"critical": {"op": 2, "value": 0}},
"labels": {"team": "billing", "env": "prod"},
"annotations": {"summary": "User {{ $labels.user }} ran out of credit"}
}
oodle log-metrics create -f credit-exhausted.json
oodle monitors create -f credit-exhausted-monitor.json
# Later: whose credit ran out in the last 24 hours
oodle metrics query --query 'sum by (user) (increase(oodle_logs_credit_exhausted[24h])) > 0' -o json
此处每个用户一个标签是可行的,因为此事件很少发生(参见“将 labels 保持在 2–3 个低基数标签”)。
示例:提取行中的值,例如 tool Bash ok in 12345 ms 中的持续时间。每个值使用一个正则表达式,每个表达式包含一个捕获组,并使用过滤条件确保该值存在:
{
"name": "tool-latency",
"filter": {"all": [{"field": "message", "operator": "matches regex", "value": "tool \\S+ (?:ok|failed) in \\d+ ms"}]},
"labels": [
{"name": "tool", "valueExtractor": {"field": "message", "regex": "tool (\\S+) "}},
{"name": "status", "valueExtractor": {"field": "message", "regex": " (ok|failed) in "}}
],
"metricDefinitions": [
{"name": "tool_calls", "type": "log_count"},
{"name": "tool_duration_ms", "type": "histogram", "field": "message", "regex": " in (\\d+) ms"}
]
}
# p95 tool latency in ms, per tool
histogram_quantile(0.95, sum by (le, tool) (rate(oodle_logs_tool_duration_ms_bucket[5m])))
# failed calls per tool
sum by (tool) (increase(oodle_logs_tool_calls{status="failed"}[10m]))
最佳实践
创建规则前,先根据实际日志量验证 filter
过滤条件中的拼写错误(levl=eror)会导致规则悄无声息地生成零个数据点。
# ✅ CORRECT — preview matching log volume in the log explorer first;
# only then run `oodle log-metrics create`
# (or run `oodle logs query` with the same condition and confirm count > 0)
# ❌ WRONG — create the rule, then notice the dashboard panel is empty next week
oodle log-metrics create -f rule.json
将 labels 保持在 2–3 个低基数标签
每个标签都会使时间序列数量倍增。避免使用 request_id、user_id、trace_id、path(包含 ID)。只有在信用拒绝这类罕见事件中,才适合使用 user 标签。
# ✅ CORRECT — bounded labels
"labels": [{"name": "service", "valueExtractor": {"field": "service"}}, {"name": "env", "valueExtractor": {"field": "env"}}]
# ❌ WRONG — `user_id` on every request blows up cardinality (one series per user)
"labels": [{"name": "service", "valueExtractor": {"field": "service"}}, {"name": "user_id", "valueExtractor": {"field": "user_id"}}]
更新前务必先 get,以保留字段
update 会替换整个文档。只发送一个字段会将其他所有字段置空。
# ✅ CORRECT
oodle log-metrics get lm_123 -o json > rule.json
jq '.filter.all += [{"field": "status", "operator": "matches regex", "value": "^5"}]' rule.json > rule.new.json
oodle log-metrics update lm_123 -f rule.new.json
# ❌ WRONG — clobbers labels and metricDefinitions
oodle log-metrics update lm_123 -f <(echo '{"filter":{"all":[{"field":"level","operator":"is","value":"error"}]}}')
使用稳定且描述性强的指标名称
生成的名称为 oodle_logs_<name>。采用 <domain>_<measurement> 格式,便于在仪表板和删除规则中查找该指标。仅使用字母、数字和下划线。
# ✅ CORRECT
"metricDefinitions": [{"name": "http_errors", "type": "log_count"}]
# ❌ WRONG — generic name collides with other rules
"metricDefinitions": [{"name": "errors", "type": "log_count"}]
故障处理
| 错误 | 原因 | 解决方法 |
|---|---|---|
| 401 Unauthorized | API 密钥无效或缺失 | 运行 oodle configure,或设置 OODLE_API_KEY |
| 404 Not Found | 日志指标规则 ID 不存在 | 使用 oodle log-metrics list -o json 验证 |
| connection refused | OODLE_DEPLOYMENT URL 错误 | 检查 OODLE_DEPLOYMENT 环境变量 |
400 Match must have a valid Operator | 运算符不是 is、contains、matches regex 或 exists | 使用这四种运算符之一;如需表示“不等于”,请用 not 包裹条件 |
400 Field ... must contain only alphanumeric characters and underscores | field 中包含带点的字段名 | 使用顶层字段,并将路径放入 jsonPath |
400 Regex ... is not a valid rust regex | 正则表达式语法错误,或缺少 JSON 转义 | 在 JSON 中转义反斜杠(\\d);不支持环视 |
400 'logCount' metric definition has a Field, JSONPath, or Regex | log_count 包含 field、jsonPath 或 regex | 移除这些字段 |
400 Log Metrics ID in path and body don't match | 请求体中的 id 缺失,或与 <id> 不同 | 运行 get 并编辑其输出 |
| 指标存在,但没有数据点 | 过滤条件没有匹配任何日志,或规则创建后没有新日志行 | 在日志浏览器或使用 oodle logs query 重新测试过滤条件;检查字段名称是否与日志架构一致。规则不会回填数据 |
| 标签为空 / 值全为 0 | 正则表达式不匹配,或第 1 组不是数字 | 在示例行上测试正则表达式;确保过滤条件要求该值存在 |
| UI 中出现基数告警 | labels 包含高基数字段 | 编辑规则以删除该标签;旧时间序列会在常规保留期结束后过期 |
| 429 Too Many Requests | 批量创建规则 | 添加 --retries 3,并将创建速率限制在每秒 <10 次 |