mcpField
O
openobserve-api

openobserve-api

github.com/fcakyon云与 DevOps1.2k45 次安装今天更新
代码仓库

关于 openobserve-api

面向 AI Agent 的 OpenObserve REST API 使用指南,帮助通过 curl 查询和管理实例。内容强调先查阅文档或服务端源码,并涵盖仪表板结构与常见陷阱。

主要功能

  1. 01通过 REST API 查询日志、指标和链路
  2. 02管理数据流、仪表板、文件夹与告警
  3. 03使用 curl 摄取 JSON 和批量数据
  4. 04说明仪表板面板格式及并发哈希
  5. 05提供 API 检索与故障规避指南

适用场景

  1. 01分析日志并按主机聚合计数
  2. 02创建或更新仪表板及其面板
  3. 03配置告警并向数据流写入数据

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/fcakyon/claude-codex-settings --skill openobserve-api

安装后在 Claude Code 中输入/openobserve-api确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
around.md
bulk.md
delete.md
index.md
json.md
list.md
loki.md
multi.md
otlp.md
schema.md
search.md
setting.md
value.md
references/recipes/
build-dashboard.sh
search-logs.sh
update-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 = 整数)。
unitnumbers | currency | bytes | seconds | milliseconds | microseconds | nanoseconds | percent | percent-1
unit_custom当 unit=currency 时使用的 ISO 代码,例如 USD。
show_legends布尔值,仅适用于图表。
legends_positionright | bottom。
axis_border_show布尔值。
line_interpolationsmooth | 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)

语言仓库状态
Pythongithub.com/openobserve/openobserve-python-sdk活跃
Gogithub.com/openobserve/openobserve-go-clientZincObserve 时代,部分支持
Helm chartgithub.com/openobserve/openobserve-helm-chart活跃
OTel collector distrogithub.com/openobserve/openobserve-otel-collector活跃

对于大多数 Agent 任务,直接使用 curl 调用 REST API 是合适的工具——与 HTTP 请求相比,SDK 增益不大,而且功能落后于服务端。

参考资料

评论

支持 Markdown