关于 openobserve-api
面向 AI Agent 的 OpenObserve REST API 使用指南,帮助通过 curl 查询和管理实例。内容强调先查阅文档或服务端源码,并涵盖仪表板结构与常见陷阱。
主要功能
- 01通过 REST API 查询日志、指标和链路
- 02管理数据流、仪表板、文件夹与告警
- 03使用 curl 摄取 JSON 和批量数据
- 04说明仪表板面板格式及并发哈希
- 05提供 API 检索与故障规避指南
适用场景
- 01分析日志并按主机聚合计数
- 02创建或更新仪表板及其面板
- 03配置告警并向数据流写入数据
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ fcakyon/ claude-codex-settings --skill openobserve-api安装后在 Claude Code 中输入/openobserve-api确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/around.mdbulk.mddelete.mdindex.mdjson.mdlist.mdloki.mdmulti.mdotlp.mdschema.mdsearch.mdsetting.mdvalue.mdreferences/recipes/build-dashboard.shsearch-logs.shupdate-panel.sh详细介绍
OpenObserve REST API 技能
供 AI Agent 以编程方式使用 OpenObserve。通过 curl 和已记录的 REST API,与任何 OpenObserve 实例(云端或可自托管)交互。无需 CLI——OpenObserve 没有一方 CLI。
优先检索
你掌握的 OpenObserve API 格式可能已经过时。优先检索,而非依赖预训练知识:
| 来源 | 检索方式 | 用途 |
|---|---|---|
| 文档仓库 | gh api repos/openobserve/openobserve-docs/contents/docs/reference/api/{path}.md -q .content | base64 -d | 权威的请求/响应示例 |
| 服务端源码 | gh api repos/openobserve/openobserve/contents/src/handler/http/request/dashboards/mod.rs -q .content | base64 -d | 端点路径、查询参数、状态码 |
| 面板架构 | gh api repos/openobserve/openobserve/contents/src/config/src/meta/dashboards/v8/mod.rs -q .content | base64 -d | 精确的面板 JSON 结构(Rust 结构体) |
文档与服务端源码不一致时,以服务端源码为准——处理程序的更新速度比文档快。
1. 身份验证
使用电子邮件和密码进行 HTTPS 基本身份验证。没有令牌端点。
# Method 1: curl -u shorthand
curl -u "[email protected]:PASSWORD" "https://eu1.openobserve.ai/api/<org>/streams"
# Method 2: explicit header
TOKEN=$(printf '%s' "[email protected]:PASSWORD" | base64)
curl -H "Authorization: Basic $TOKEN" "https://eu1.openobserve.ai/api/<org>/streams"
以下端点假定 BASE=https://<host>/api/<org> 且 AUTH="-u [email protected]:PASSWORD"。
2. 搜索/查询 — POST $BASE/_search
可选查询字符串 ?type=logs|metrics|traces(默认值为 logs)。
curl $AUTH -H 'Content-Type: application/json' \
"$BASE/_search?type=logs" \
-d '{
"query": {
"sql": "SELECT host_name, COUNT(*) AS n FROM \"my_stream\" GROUP BY host_name ORDER BY n DESC",
"start_time": 1777000000000000,
"end_time": 1777999999000000,
"from": 0,
"size": 100
},
"search_type": "ui"
}'
- 时间戳单位为微秒(Unix 纪元时间 × 1_000_000)。务必设置
start_time/end_time——若未设置,则会扫描所有数据。 search_type∈ui | dashboards | reports | alerts— 会影响速率限制和审计日志。- 分页:
from(偏移量)+size(限制,单次请求最多约 10000 条)。 - 响应:
{ took, hits[], total, from, size, scan_size }。 - SQL 方言:DataFusion / Arrow SQL。标识符用双引号括起(
"stream_name"),字符串用单引号括起('value')。 - 按时间桶分组:
SELECT histogram(_timestamp, '5 minute') AS ts, COUNT(*) FROM "stream" GROUP BY ts ORDER BY ts。 - 词项聚合:
SELECT k8s_namespace, COUNT(*) FROM "stream" GROUP BY k8s_namespace。 - 全文搜索:
match_all('text')、str_match(field, 'text')。默认全文搜索字段:log, message, msg, content, data, json。 - 对指标使用 PromQL:
POST $BASE/prometheus/api/v1/query_range。 - Trace 上下文窗口:
GET $BASE/{stream}/_around?key=<ts_us>&size=N。
3. 流 — GET $BASE/streams
# List
curl $AUTH "$BASE/streams?fetchSchema=false&type=logs"
# Schema
curl $AUTH "$BASE/streams/<stream>/schema?type=logs"
# Update settings
curl $AUTH -X PUT -H 'Content-Type: application/json' "$BASE/streams/<stream>/settings" -d '{"partition_keys":["host_name"]}'
# Delete
curl $AUTH -X DELETE "$BASE/streams/<stream>?type=logs"
字段类型:Utf8 | Int64 | Float64 | Timestamp | Boolean。时间戳字段始终为 _timestamp(单位为微秒)。
4. 仪表板 — GET|POST|PUT|DELETE $BASE/dashboards
所有请求均接受 ?folder=<folder_id>(默认为 default)。
# List
curl $AUTH "$BASE/dashboards?folder=default"
# Get one (returns versioned wrapper {v1..v8, version, hash, updatedAt})
curl $AUTH "$BASE/dashboards/<dashboard_id>?folder=default"
# Create — body is the UNWRAPPED inner v8 object
curl $AUTH -X POST -H 'Content-Type: application/json' \
"$BASE/dashboards?folder=default" \
-d @dashboard-v8.json
# Update — REQUIRES the current hash for optimistic concurrency
HASH=$(curl -s $AUTH "$BASE/dashboards/<id>?folder=default" | jq -r .hash)
curl $AUTH -X PUT -H 'Content-Type: application/json' \
"$BASE/dashboards/<id>?folder=default&hash=$HASH" \
-d @updated-dashboard.json
# Delete
curl $AUTH -X DELETE "$BASE/dashboards/<id>?folder=default"
# Move between folders
curl $AUTH -X PUT -H 'Content-Type: application/json' \
"$BASE/folders/dashboards/<id>" \
-d '{"from":"default","to":"<target_folder_id>"}'
重要:PUT/POST 请求正文必须是去除包装层的内部 v8 对象,而不是完整的 {v1..v8, version, hash} 包装对象。服务端会返回包装对象,但要求你仅发送内部对象。
# Read, mutate, write — the correct pattern
RAW=$(curl -s $AUTH "$BASE/dashboards/<id>?folder=default")
HASH=$(echo "$RAW" | jq -r .hash)
echo "$RAW" | jq '.v8 | .title = "New Title"' \
| curl -s $AUTH -X PUT -H 'Content-Type: application/json' \
"$BASE/dashboards/<id>?folder=default&hash=$HASH" -d @-
409 Conflict 响应表示哈希已过期——重新获取后重试。
单个面板操作(仅限 v8——返回新哈希)
# Add panel
curl $AUTH -X POST -H 'Content-Type: application/json' \
"$BASE/dashboards/<id>/panels?folder=default&hash=$HASH" \
-d '{"panel": {...}, "tabId": "default"}'
# Update panel
curl $AUTH -X PUT -H 'Content-Type: application/json' \
"$BASE/dashboards/<id>/panels/<panel_id>?folder=default&hash=$HASH" \
-d '{...panel...}'
# Delete panel
curl $AUTH -X DELETE \
"$BASE/dashboards/<id>/panels/<panel_id>?folder=default&hash=$HASH&tabId=default"
5. 面板 JSON(v8)
仪表板树:dashboard.tabs[].panels[]。每个面板:
{
"id": "panel-1",
"type": "table",
"title": "Per-host stats",
"description": "",
"queryType": "sql",
"queries": [
{
"query": "SELECT host_name, COUNT(*) AS n FROM \"my_stream\" GROUP BY host_name ORDER BY n DESC",
"vrlFunctionQuery": "",
"customQuery": true,
"fields": {
"stream": "my_stream",
"stream_type": "logs",
"x": [
{ "label": "Host", "alias": "host_name", "column": "host_name", "color": null, "aggregationFunction": null }
],
"y": [
{
"label": "Count",
"alias": "n",
"column": "n",
"color": null,
"aggregationFunction": null,
"treatAsNonTimeseries": true
}
],
"z": [],
"breakdown": [],
"filter": { "filterType": "group", "logicalOperator": "AND", "conditions": [] }
},
"config": { "promql_legend": "", "layer_type": "scatter", "weight_fixed": 1, "limit": 0, "min": 0, "max": 100 }
}
],
"config": {
"show_legends": true,
"decimals": 2,
"unit": "currency",
"unit_custom": "USD"
},
"layout": { "x": 0, "y": 0, "w": 48, "h": 14, "i": 1 }
}
面板 type 值
metric(单个大数字) · table · bar · h-bar · stacked · h-stacked · line · area · area-stacked · scatter · pie · donut · heatmap · gauge · geomap · maps · sankey · html · markdown。
布局网格
网格宽 96 列(经检查 2026 年 4 月 OpenObserve Cloud 返回的面板布局确认)。较早的文档提到 192 或 48——如有疑问,请从同一组织中获取现有仪表板,并复制其中看到的 w 值。高度以无单位的行数表示(h: 7 = 小型指标面板;h: 14 = 标准表格)。
实用的 config 键
| 键 | 效果 |
|---|---|
decimals | 所有数值列的小数位数(0 = 整数)。 |
unit | numbers | currency | bytes | seconds | milliseconds | microseconds | nanoseconds | percent | percent-1 |
unit_custom | 当 unit=currency 时使用的 ISO 代码,例如 USD。 |
show_legends | 布尔值,仅适用于图表。 |
legends_position | right | bottom。 |
axis_border_show | 布尔值。 |
line_interpolation | smooth | linear | step-start | step-end。 |
connect_nulls | 布尔值——仅适用于折线图/面积图。 |
top_results | 限制折线图/柱状图的系列数量(例如 10)。 |
mark_line | [{name, type:'avg'|'max'|'min', value}]——水平参考线。 |
6. 关键注意事项
6a. customQuery: true 时重新聚合——这是最常见的错误。
如果手写的 SQL 已包含 COUNT(*)、SUM(...)、AVG(...) 等,每个 fields.y(和 fields.x)条目都必须设置 aggregationFunction: null。默认值 'sum' 会导致 OpenObserve 在客户端再次聚合已经聚合过的列,从而产生重复行和大幅虚高的数值。
// WRONG — produces duplicate rows
"y": [{"column":"messages", "aggregationFunction":"count"}]
// RIGHT — SQL already did the aggregation
"y": [{"column":"messages", "aggregationFunction":null, "treatAsNonTimeseries":true}]
6b. Table 面板中的多个 fields.y——每个 Y 条目都可能呈现为单独的系列/行。对于应当每组显示一行的 Table,只在 fields.y 中放入一个条目(任意一列);渲染器随后会将所有 SQL 列显示为表格列。
6c. customQuery: true 的 Metric 面板——必须将结果列明确映射到 fields.y:
"y": [{"label":"Value", "alias":"value", "column":"value", "aggregationFunction":"sum", "treatAsNonTimeseries":false}]
Metric 面板需要知道要显示哪个列中的数值。
6d. ROUND + 通配符时间戳展开——OpenObserve 的规划器有时会在将 SUM(col) 包装在 ROUND(...) 中的查询里自动注入 _timestamp,导致出现 Column "_timestamp" must appear in the GROUP BY clause 错误。解决方法:去掉 ROUND(),改用面板的 decimals 配置,或预先转换类型:CAST(SUM(...) AS DOUBLE)。
6e. PUT 时基于哈希的并发控制——每次成功的 PUT 都会更改仪表板哈希。如果你用两个脚本连续修改仪表板,第二个脚本需要重新获取数据。每次 PUT 前都要重新执行 GET,以获取当前哈希。
6f. start_time/end_time 的单位是微秒——使用 Date.now() * 1000,而不是毫秒。单位差 1000 倍会导致没有命中,但不会报错。
7. 文件夹 / 警报 / 数据摄取
文件夹(v2 API):
curl $AUTH "$BASE/folders/dashboards" # list
curl $AUTH -X POST "$BASE/folders/dashboards" -d '{"name":"my-folder"}'
curl $AUTH "$BASE/folders/dashboards/name/<folder_name>" # lookup by name
folder_type ∈ dashboards | alerts | reports。
警报:
curl $AUTH "$BASE/{stream}/alerts" # list per-stream
curl $AUTH -X POST "$BASE/{stream}/alerts" -d '{...}'
# templates and destinations are referenced by alert definitions:
curl $AUTH "$BASE/alerts/templates"
curl $AUTH "$BASE/alerts/destinations"
数据摄取(POST 你自己的数据):
# JSON
curl $AUTH -X POST "$BASE/<stream>/_json" -d '[{"event":"foo","level":"info"}]'
# Multi-line JSON (one per line)
curl $AUTH -X POST "$BASE/<stream>/_multi" --data-binary @file.ndjson
# Elasticsearch bulk
curl $AUTH -X POST "$BASE/_bulk" --data-binary @bulk.txt
# OTLP HTTP
curl $AUTH -X POST "$BASE/v1/logs" -d @otlp-logs.json
curl $AUTH -X POST "$BASE/v1/traces" -d @otlp-traces.json
curl $AUTH -X POST "$BASE/v1/metrics" -d @otlp-metrics.json
# Loki
curl $AUTH -X POST "$BASE/loki/api/v1/push" -d @loki.json
# Prometheus remote-write (binary protobuf)
curl $AUTH -X POST "$BASE/prometheus/api/v1/write" --data-binary @write.pb
8. 常见配方
按消息数量获取排名靠前的主机(最近 24 小时):
NOW=$(($(date +%s) * 1000000))
DAY=$((NOW - 86400 * 1000000))
curl $AUTH -H 'Content-Type: application/json' \
"$BASE/_search?type=logs" \
-d "{\"query\":{\"sql\":\"SELECT host_name, COUNT(*) AS n FROM \\\"my_stream\\\" GROUP BY host_name ORDER BY n DESC\",\"start_time\":$DAY,\"end_time\":$NOW,\"size\":50}}"
向现有仪表板添加指标面板(一次性操作,感知哈希):
DASH_ID=<dashboard_id>
HASH=$(curl -s $AUTH "$BASE/dashboards/$DASH_ID?folder=default" | jq -r .hash)
curl $AUTH -X POST -H 'Content-Type: application/json' \
"$BASE/dashboards/$DASH_ID/panels?folder=default&hash=$HASH" \
-d '{
"tabId": "default",
"panel": {
"id": "p-cost",
"type": "metric",
"title": "Total cost (USD)",
"queryType": "sql",
"queries": [{
"query": "SELECT SUM(CAST(cost_usd AS DOUBLE)) AS value FROM \"my_stream\"",
"customQuery": true,
"fields": {
"stream":"my_stream", "stream_type":"logs",
"x":[], "z":[], "breakdown":[],
"y":[{"label":"Value","alias":"value","column":"value","aggregationFunction":"sum","treatAsNonTimeseries":false}],
"filter":{"filterType":"group","logicalOperator":"AND","conditions":[]}
},
"config":{}
}],
"config": {"unit":"currency","unit_custom":"USD","decimals":2},
"layout": {"x":0,"y":0,"w":32,"h":7,"i":99}
}
}'
从头构建完整仪表板:获取现有仪表板的面板 JSON 作为模板(这是了解服务端接受的确切字段结构的最稳妥方式),然后修改 tabs[0].panels 数组,并将未封装的 v8 请求体 PUT 回去。请参阅此技能附带的 references/recipes/build-dashboard.sh 脚本,其中包含可运行的示例。
9. SDK / 客户端(无官方 CLI)
| 语言 | 仓库 | 状态 |
|---|---|---|
| Python | github.com/openobserve/openobserve-python-sdk | 活跃 |
| Go | github.com/openobserve/openobserve-go-client | ZincObserve 时代,部分支持 |
| Helm chart | github.com/openobserve/openobserve-helm-chart | 活跃 |
| OTel collector distro | github.com/openobserve/openobserve-otel-collector | 活跃 |
对于大多数 Agent 任务,直接使用 curl 调用 REST API 是合适的工具——与 HTTP 请求相比,SDK 增益不大,而且功能落后于服务端。
参考资料
- API 文档(权威来源):https://github.com/openobserve/openobserve-docs(路径:`docs/reference/api/`)
- 服务端源代码:https://github.com/openobserve/openobserve(路径:`src/handler/http/request/`、`src/config/src/meta/dashboards/v8/mod.rs`)
- 云控制台:https://cloud.openobserve.ai(区域:us1、eu1、ap1)
- 此技能中的
references/目录包含从openobserve-docs选取的文档副本,可供离线查阅。