Nightingale Skill Gateway 数据查询 API 完全指南:指标与日志的只读 POST 查询协议
【免费下载链接】nightingaleNightingale is to monitoring and alerting what Grafana is to visualization.项目地址: https://gitcode.com/GitHub_Trending/ni/nightingale
数据查询是 Nightingale 技能网关(Skill Gateway)中唯一被允许使用POST方法的一组接口:技能(Skill)通过它们读取数据源中的真实时序数据与日志数据,而不是平台自身的告警、规则等配置对象。本文以 api/data-query.md 为核心骨架,结合网关策略源码(policy.go、gateway.go)与中心端查询路由实现(router_proxy.go、router_query.go),完整讲解这组 POST 白名单端点的请求体、响应结构、调用顺序与安全边界。读完本文,你将能编写出经网关代理、受 RBAC 约束、可正确解析结果的指标/日志查询脚本。
一、定位与总体规则:为什么数据查询是"唯一的 POST 例外"
在 Nightingale 的技能网关安全模型中,绝大多数对平台自身数据的读取走 GET(黑名单模型,默认放行、按前缀拒绝敏感路径),而写操作一律被拒绝。唯一的例外是一组只读的数据查询端点——它们之所以被允许 POST,并不是因为它们会修改状态,而是因为它们携带的查询体是结构化的 JSON,体积与结构都远超 query 参数的承载能力。
这一点在 policy.go 中有明确注释与实现:
// postAllowN9eAPIPaths is the allowlist of read-only DATA-query endpoints the // gateway may forward with POST. They are POST only because they carry a JSON // query body that is too large/structured for query params — not because they // mutate state: each runs the query under the caller's RBAC (the handler's // CheckDsPerm) and returns time-series / log DATA, never config or secrets. var postAllowN9eAPIPaths = map[string]bool{ "/ds-query": true, // unified datasource query (metrics/logs by cate) "/query-range-batch": true, // Prometheus range query (batch) "/query-instant-batch": true, // Prometheus instant query (batch) "/logs-query": true, // log query (v2) "/log-query": true, // log query "/log-query-batch": true, // log query (batch) }1.1 网关调用约定(对>var getAllowExceptions = map[string]bool{ "/datasource/brief": true, }
/datasource/brief是唯一可达的数据源路径,且服务端会调用RedactSecrets对返回内容做密钥脱敏——settings/auth/http字段中的地址、密码、token 全部被剔除。
2.2 端点与响应字段
| Path | 用途 | dat形状 |
|---|---|---|
/datasource/brief | 列出你能查询的数据源(密钥脱敏)。网关可达的唯一数据源路径(/datasource*配置均被拦截) | Pattern B(裸数组) |
无 query 参数。dat是数据源对象数组,每个对象的json字段含义如下:
| 字段 | 类型 | 含义 |
|---|---|---|
id | int64 | 数据源 id——传给查询端点作为datasource_id |
name | string | 显示名称 |
identifier | string | 唯一标识 |
description | string | 描述 |
plugin_id | int64 | 插件 id |
plugin_type | string | 插件类型——这就是/ds-query的cate(如prometheus、mysql、elasticsearch、loki、tdengine、ck) |
plugin_type_name | string | 人类可读插件名(如Prometheus Like) |
category | string | 大类:timeseries、logging等——用于区分指标源与日志源 |
cluster_name | string | 集群名(遗留字段) |
status | string | enabled/disabled |
is_default | bool | 是否为默认数据源 |
settings | object | 已脱敏——仅保留非敏感 UI 键,不要期望有地址/凭据 |
created_at/updated_at | int64 | Unix 秒 |
created_by/updated_by | string | 作者 |
2.3 示例
请求:
{"method":"GET","path":"/api/n9e/datasource/brief"}响应(已裁剪):
{"ok":true,"status":200,"data":{"dat":[ {"id":1,"name":"prod-prometheus","plugin_type":"prometheus","category":"timeseries","status":"enabled","is_default":true}, {"id":5,"name":"app-loki","plugin_type":"loki","category":"logging","status":"enabled"} ],"err":""}}拿到id(作为datasource_id)与plugin_type(作为cate)之后,就可以继续下面的查询端点了。
三、POST 白名单端点总览
| Path | 用途 | Body | dat结果 |
|---|---|---|---|
/query-range-batch | Prometheus区间查询(一段时间内的序列) | BatchQueryForm | 与queries对齐的数组;每项是一个 Prometheusmatrix |
/query-instant-batch | Prometheus瞬时查询(一个时间点) | BatchInstantForm | 与queries对齐的数组;每项是一个 Prometheusvector |
/ds-query | 按cate对任意数据源(指标或日志)的统一查询 | QueryParam | 数据源相关 |
/logs-query·/log-query·/log-query-batch | 日志查询 | QueryParam | {"total":N,"list":[...]} |
这些路由在 center/router/router.go 中注册(如pages.POST("/query-range-batch", rt.promBatchQueryRange)),并且支持仪表盘限时分享 token 的放行链路(boardTokenDetect()+skipIfBoardToken(...))。
四、Prometheus 指标查询:最简单的路径
如果目标数据源是 Prometheus 类(plugin_type == "prometheus",如 Prometheus / VictoriaMetrics),优先使用区间/瞬时批量查询端点,它们只接受 PromQL 字符串 + 时间窗,形状固定、无歧义。
4.1 区间查询:/query-range-batch
请求体(BatchQueryForm):
{ "datasource_id": 1, "queries": [ {"query": "<PromQL>", "start": 1751330400, "end": 1751334000, "step": 60} ] }字段说明:
datasource_id(int,必填)——来自/datasource/brief。queries[](必填)——每项含:query(PromQL 字符串)、start/end(Unix 秒)、step(秒级分辨率)。全部必填。
底层实现见 router_proxy.go:服务端将start/end经time.Unix转为时间、step转为time.Duration,逐条调用 Prometheus 客户端的QueryRange,结果按查询顺序追加到数组返回。
dat是与queries一一对应的数组,每项是一个 Prometheusmatrix(序列列表),每个序列含metric标签与values(时间戳-值对):
{"ok":true,"status":200,"data":{"dat":[ [ {"metric":{"__name__":"cpu","ident":"host-01"}, "values":[[1751330400,"6.3"],[1751330460,"7.1"]]} ] ],"err":""}}step 取值建议(来自 query-datasource/datasources/prometheus.md,可直接套用):
| 时间范围 | step |
|---|---|
| 1 小时 | 15s |
| 6 小时 | 60s |
| 24 小时 | 300s |
| 7 天 | 1800s |
4.2 瞬时查询:/query-instant-batch
请求体(BatchInstantForm)——注意每项用的是time,而非start/end/step:
{"datasource_id": 1, "queries": [{"query": "<PromQL>", "time": 1751334000}]}字段:datasource_id(int,必填)、queries[](必填)每项为query(PromQL 字符串)与time(Unix 秒,必填)。服务端实现见 router_proxy.go(PromBatchQueryInstant调用cli.Query于time.Unix(item.Time, 0)时刻)。
dat是与queries一一对应的数组,每项是一个 Prometheusvector:
[{"metric":{...}, "value":[1751334000,"7.1"]}]4.3 常用 PromQL 速查(指标查询场景)
| 需求 | PromQL |
|---|---|
| CPU 使用率 | cpu_usage_active |
| 内存使用率 | mem_used_percent |
| 磁盘使用率 | disk_used_percent{path="/"} |
| 入站流量速率 | rate(net_bytes_recv[5m]) |
| 1 分钟负载 | system_load1 |
| HTTP 请求速率 | rate(http_requests_total[5m]) |
| 某实例全部指标 | {ident="web-01"} |
| 按实例聚合平均 | avg by (ident)(cpu_usage_active) |
| CPU Top 10 | topk(10, cpu_usage_active) |
五、任意数据源统一查询:/ds-query
当目标数据源不是Prometheus 类(MySQL、Elasticsearch、Loki、TDengine、ClickHouse 等),或需要统一入口时,使用/ds-query。它按cate分发到对应数据源插件。
请求体(QueryParam):
{"cate": "<plugin_type>", "datasource_id": <id>, "query": [ <one-or-more query objects> ]}cate——数据源在/datasource/brief中返回的plugin_type(prometheus、mysql、elasticsearch、loki、tdengine、ck、…)。query[]——查询对象数组;其形状取决于cate,与该数据源的告警规则查询配置一致(每种类型已有文档)。读取方式为:read_file(base="create-alert-rule", path="datasources/<cate>.md"),例如 datasources/prometheus.md、datasources/mysql.md、datasources/elasticsearch.md、datasources/loki.md(同目录下还有clickhouse.md、doris.md、host.md、pgsql.md、tdengine.md、victorialogs.md)。
dat的结果是数据源相关的:Prometheus 类数据源返回指标值;SQL 类数据源(mysql/pgsql/ck/tdengine)返回行;ES/Loki 返回日志或聚合结果。
5.1 源码视角:QueryDataConcurrently
统一查询的底层实现在 router_query.go,可以确认几个重要行为:
- 权限门:每条 query 在执行前都调用
CheckDsPerm(ctx, f.DatasourceId, f.Cate, q)校验当前用户对该数据源的查询权限,未通过直接返回forbidden——这正是文档中"read-only and under your RBAC"的落地。 - 数据源解析:通过
dscache.DsCache.Get(f.Cate, f.DatasourceId)取到数据源插件,再调用plug.QueryData。 - 并发与排序:多条 query 通过 goroutine 并发执行(
sync.WaitGroup+ mutex 收集结果);返回多条结果时按.Metric字典序排序,确保仪表盘中相同图例曲线颜色一致。 - 同一入口还支持仪表盘分享 token 场景(
boardTokenQueryContext),命中后跳过基于登录用户的权限校验并置位强只读。
六、日志查询:/logs-query(及/log-query、/log-query-batch)
请求体同样是QueryParam(cate+datasource_id+query[]的形状不变;query 的具体形状按create-alert-rule/datasources/<cate>.md构造)。dat的返回结构为:
{"total": N, "list": [ ...records... ]}即"总数 + 记录列表"模式。底层实现在 router_query.go(QueryLogBatch/QueryLogConcurrently):逐条logQueryAccess审计,逐条CheckDsPerm校验,再并发调用数据源插件的QueryLog。
七、推荐工作流
综合上述端点,一个完整的"从发现数据源到拿到数据"的调用流程如下:
GET /datasource/brief→ 按category/plugin_type挑选数据源 → 记录其id+plugin_type。- 若是 Prometheus 类指标源 →
POST /query-range-batch(或/query-instant-batch),携带datasource_id+ PromQL 即可。 - 若是其他类型数据源,或日志 →
POST /ds-query(或/logs-query),携带cate=plugin_type,并按照create-alert-rule/datasources/<cate>.md构造 query 对象形状。
八、安全边界与注意事项
- 只读且受你的 RBAC 约束:处理函数会校验数据源查询权限(
CheckDsPerm),只返回数据,绝不写任何状态;数据源配置、通知密钥、用户 token、SSO 配置等仍被黑名单拦截(详见 n9e-api.md 的 deny-list 一节)。 - 像所有网关调用一样校验响应:先确认
ok为 true、data是 dict、data["err"]为空,再取data["dat"]。这同时也是发现"路径写错"的手段——未知路径不会返回 404,而是静默返回 SPA 的 HTML 字符串(data直接是 HTML)。可参考 n9e-api.md 中的 Python 校验片段:
if not resp.get("ok") or not isinstance(resp.get("data"), dict): raise RuntimeError(f"gateway call failed (wrong path? denied?): {str(resp)[:200]}") env = resp["data"] if env.get("err"): raise RuntimeError(f"n9e api error: {env['err']}") dat = env["dat"]- 不要猜 query 形状:如果不知道某个数据源的查询形状,且
create-alert-rule/datasources/<cate>.md描述不足,向用户询问,而不是猜测请求体。同样,不要按类比猜测 API 路径——所有路径必须以文档为准。 - 网关侧的硬边界:请求体超过 256 KiB 会被拒绝(
body_too_large);路径含%(百分号编码)会被拒绝(防黑名单绕过,gateway.go);每次网关调用都有令牌桶限速,超限返回rate limit exceeded for this skill execution。网关测试覆盖见 gateway_test.go。
小结
Nightingale 的数据查询 POST 白名单是技能网关"只读安全模型"中的一个精心设计的例外:它以 JSON body 承载结构化查询、以datasource_id+cate定位数据源、以CheckDsPerm维持 RBAC 边界,最终把真实指标与日志数据安全地交付给技能脚本。掌握/query-range-batch、/query-instant-batch、/ds-query、/logs-query这组端点及"先 brief 发现、再按 cate 查形状"的工作流,你就能编写出可靠、可审计的数据查询技能脚本。
【免费下载链接】nightingaleNightingale is to monitoring and alerting what Grafana is to visualization.项目地址: https://gitcode.com/GitHub_Trending/ni/nightingale
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考