Corsair Grafana 插件接入指南:用 API Key 打通 Grafana 观测数据、Loki 日志与 Mimir 集群状态
【免费下载链接】corsairConnect your users to their apps项目地址: https://gitcode.com/GitHub_Trending/corsa/corsair
本指南以 packages/grafana/README.md 为骨架,完整讲解@corsair-dev/grafana这一 Corsair 官方 Grafana 插件的安装、认证、11 个内置端点、权限配置、错误重试与数据持久化机制,并深入源码验证每个端点对应的真实 Grafana API 路径。读完你将能在自己的 Corsair 应用中一键接入 Grafana,让 AI Agent 安全地查询公共仪表盘、写入 Loki 日志、检查 Mimir 哈希环状态与 Enterprise 许可证状态。
安装与依赖
@corsair-dev/grafana是 Corsair 生态中的 Grafana 连接插件,通过 pnpm 安装:
pnpm add @corsair-dev/grafana从 packages/grafana/package.json 可以看到,它声明了两个 peerDependencies:
corsair >= 0.1.0:Corsair 核心运行时,提供插件上下文、认证、请求、日志等基础设施;zod ^4.1.13:用于端点输入/输出 schema 的运行时校验。
插件使用 TypeScript 编写并以 ESM 形式发布("type": "module"),包入口为dist/index.js,同时通过exports["."]["dev-source"]暴露源码路径方便开发调试。安装完成后,用corsair核心包中注册插件的方式将其挂载到你的 Corsair 应用中即可。
快速接入:注册 Grafana 插件
在 packages/grafana/index.ts 中,插件以工厂函数grafana(options)的形式导出。最小接入方式如下:
import { grafana } from '@corsair-dev/grafana'; export const grafanaPlugin = grafana({ authType: 'api_key', key: process.env.GRAFANA_SERVICE_ACCOUNT_TOKEN, grafanaUrl: 'https://example.grafana.net', });GrafanaPluginOptions支持的完整配置项如下(类型定义见 packages/grafana/index.ts):
| 配置项 | 类型 | 说明 |
|---|---|---|
authType | 'api_key' | 认证方式,当前固定为api_key,未传时默认为api_key |
key | string | Grafana Service Account Token,作为 Bearer Token 使用;也可不传,改由运行时通过corsair auth动态获取 |
grafanaUrl | string | Grafana 实例基础 URL,例如https://example.grafana.net |
hooks | 插件钩子 | 透传给 Corsair 的插件生命周期钩子 |
webhookHooks | 插件钩子 | Grafana 插件未定义 webhook,保留字段 |
errorHandlers | CorsairErrorHandler | 自定义错误处理器,会与插件内置错误处理器合并 |
permissions | 权限配置 | 控制 AI Agent 可调用的端点,使用端点树的点分路径,路径非法会触发类型错误 |
在插件内部,grafana()工厂把端点组织为嵌套结构(logs、health、status、ring、storeGateway、saml、dashboards、jwks),并一次性注册了 endpointMeta(风险等级与描述)、endpointSchemas(zod 输入输出校验)与错误处理器。
插件 ID 与认证配置
插件 ID 固定为'grafana'。值得注意的是一段自定义认证配置(packages/grafana/index.ts):
export const grafanaAuthConfig = { api_key: { account: ['grafana_url', 'org_id'] as const, }, } as const satisfies PluginAuthConfig;这意味着通过corsair auth为租户录入凭证时,除了 API Key 本身,还可以附带grafana_url与org_id两个账户级字段。grafana_url会在每个端点的实现中被读取(ctx.keys.get_grafana_url()),从而让实例 URL 随租户凭证存储,而不是硬编码在插件配置里——这是多租户场景下避免写死 URL 的关键设计。org_id同样被预留为账户字段,便于区分不同 Grafana 组织。
认证机制:API Key 与 Service Account Token
README 明确指出本插件认证方式为API key,并说明"Corsair prompts your tenant for credentials on first use",即租户首次使用时 Corsair 会提示录入凭证。
从源码keyBuilder(packages/grafana/index.ts)可以看到凭证解析的完整逻辑:
- 若请求来源为
webhook,直接返回空字符串(Grafana 插件没有 webhook); - 若请求来源为
endpoint且插件配置了options.key,优先使用配置中的 Service Account Token; - 否则从
ctx.keys.get_api_key()读取租户在corsair auth中录入的 API Key; - 读取失败时抛出
AuthMissingError('grafana', 'api_key'),提示用户补充凭证。
传输安全:强制 HTTPS
底层客户端 packages/grafana/client.ts 在发起任何请求前都会执行assertHttpsGrafanaBaseUrl:剥离 URL 尾部的斜杠、校验 URL 合法性,并且只允许https:协议,否则直接抛出GrafanaAPIError,拒绝将 Bearer Token 发送到非 HTTPS 地址。这是针对令牌泄露风险的一道硬性防线。
两种请求通道
客户端提供两个底层函数:
makeGrafanaRequest:发送 JSON 请求(GET/POST/PUT/DELETE/PATCH),自动附加Authorization: Bearer <token>与Content-Type: application/json,用于 health、status 等标准 REST 接口;makeGrafanaRawRequest:发送原始请求并返回{ content, content_type, status_code },用于返回 HTML 页面或表单编码体的端点(如 Mimir ring 状态页、SAML ACS、Loki OTLP 写入),POST 时支持自定义contentType。
此外,GRAFANA_RATE_LIMIT_CONFIG(packages/grafana/client.ts)内置了速率限制处理:启用重试、最多 3 次、初始退避 1s、指数退避倍数 2,并读取Retry-After响应头。
端点总览
插件共暴露11 个端点,覆盖 Grafana 的观测、鉴权与 Mimir 集群运维能力。以下是 README 中的端点速查表:
| 操作 | Operation ID | 风险 | 描述 |
|---|---|---|---|
dashboards.queryPublic | grafana.api.dashboards.queryPublic | read | 查询公共 Grafana 仪表盘上的面板 |
health.get | grafana.api.health.get | read | 检查 Grafana 服务器健康状态与数据库连通性 |
jwks.retrieve | grafana.api.jwks.retrieve | read | 获取用于令牌验证的 JWKS 公钥 |
logs.createOtlp | grafana.api.logs.createOtlp | write | 通过 OTLP v1 向 Grafana Loki 发送日志 |
ring.getDistributorHaTracker | grafana.api.ring.getDistributorHaTracker | read | 获取 distributor HA tracker ring 状态 |
ring.getIndexGateway | grafana.api.ring.getIndexGateway | read | 获取 index gateway 哈希环状态 |
ring.getOverridesExporter | grafana.api.ring.getOverridesExporter | read | 获取 overrides-exporter 哈希环状态 |
ring.getRuler | grafana.api.ring.getRuler | read | 获取 Grafana Mimir 的 ruler ring 状态 |
saml.postAcs | grafana.api.saml.postAcs | write | 处理 SAML Assertion Consumer Service 认证响应 |
status.get | grafana.api.status.get | read | 检查 Grafana Enterprise 许可证可用性 |
storeGateway.getTenants | grafana.api.storeGateway.getTenants | read | 列出 store-gateway 存储中拥有块的租户 |
其中read风险端点 9 个、write风险端点 2 个(logs.createOtlp与saml.postAcs)。风险等级在grafanaEndpointMeta中集中声明(packages/grafana/index.ts),Corsair 权限系统会据此约束 Agent 行为。所有端点的输入/输出 schema 定义在 packages/grafana/endpoints/types.ts,并在 packages/grafana/endpoints/index.ts 中聚合导出。
下面按功能域深入每个端点的实现细节与真实 API 路径。
dashboards.queryPublic:查询公共仪表盘面板
对应 Grafana 公共仪表盘 API,实现位于 packages/grafana/endpoints/dashboards.ts。
输入参数(来自DashboardsQueryPublicInputSchema):
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
access_token | string | 是 | 公共仪表盘的访问令牌 |
panel_id | number | 是 | 要查询的面板 ID |
from/to | string | 是 | 时间范围 |
intervalMs | number | 否 | 采样间隔(毫秒) |
maxDataPoints | number | 否 | 最大数据点数 |
base_url_override | string | 否 | 覆盖默认 Grafana 实例 URL |
实现通过makeGrafanaRawRequest以 POST 方式请求/api/public/dashboards/{access_token}/panels/{panel_id}/query(packages/grafana/endpoints/dashboards.ts),并尝试将响应解析为 JSON,取results字段;若返回的是 HTML 错误页则回退为把原始内容放入message字段。查询成功后,结果会以${access_token}-${panel_id}为唯一键 upsert 到数据库的dashboardQueries表,并记录grafana.dashboards.queryPublic事件日志。
health.get 与 status.get:健康与许可证检查
两个端点都调用 JSON 通道,实现在 packages/grafana/endpoints/health.ts:
health.get请求/api/health,返回{ version, commit, database, enterpriseCommit }等字段,用于判断 Grafana 服务器与底层数据库是否健康;status.get请求/api/licensing/check,通过raw.hasLicense === true或licenseExpiry > Date.now() / 1000计算license_available布尔值,用于判断 Enterprise 许可证是否有效。
两者的结果都会写入healthStatus表(status.get在已有记录上合并licenseAvailable字段),便于后续审计与状态追溯。
logs.createOtlp:向 Loki 写入 OTLP 日志
这是本插件唯一的日志写入端点,实现于 packages/grafana/endpoints/logs.ts。它通过原始请求通道 POST/otlp/v1/logs,请求体为 OTLP 的resourceLogs结构(ResourceLog → ScopeLog → LogRecord,每层 schema 见 packages/grafana/endpoints/types.ts)。
成功(2xx)后,插件会将日志记录扁平化持久化:把 resource attributes 展平为字符串键值对、提取scope.name与scope.version、以body.stringValue优先存储日志正文(否则 JSON 序列化整个 body),并用crypto.randomUUID()生成唯一 ID 写入logs表。
ring.* 与 storeGateway.getTenants:Mimir 集群状态
四个ring.*端点与storeGateway.getTenants面向 Grafana Mimir 的哈希环运维场景,实现见 packages/grafana/endpoints/ring.ts,全部通过原始请求通道返回 HTML 页面:
| 端点 | 请求路径 | 数据落库 key |
|---|---|---|
ring.getDistributorHaTracker | /distributor/ha-tracker | distributor-ha-tracker |
ring.getIndexGateway | /index-gateway/ring | index-gateway |
ring.getOverridesExporter | /overrides-exporter/ring | overrides-exporter |
ring.getRuler | /ruler/ring | ruler |
storeGateway.getTenants | /store-gateway/tenants | store-gateway-tenants |
响应中html_content或content连同content_type、status_code一起写入ringStatus表。这类端点让 Agent 能快速巡检 Mimir 各组件哈希环的健康状态、租户块分布等信息,是监控告警类 Agent 的高价值能力。
saml.postAcs:处理 SAML 认证响应
write风险端点,实现于 packages/grafana/endpoints/auth.ts。输入为saml_response(必填)与relay_state(可选)。实现将参数编码为application/x-www-form-urlencoded表单,POST 到/login/saml/acs(packages/grafana/endpoints/auth.ts)。
返回结果会判断是否 302 重定向,并尝试从返回内容中正则提取<meta ... url=...>形式的跳转地址作为location。每次处理都会以saml-${saml_response 前 16 位}为稳定 session ID 写入samlSessions表。
jwks.retrieve:获取令牌验证公钥
同样实现于 packages/grafana/endpoints/auth.ts,该端点按顺序探测三个已知 JWKS 路径,直到拿到 200 响应为止:
/api/signing-keys/jwks/.well-known/jwks.json/api/jwks
拿到 JSON 后解析出keys数组;JWKS 中每个 key 的Key(密钥材料)、Use、Algorithm、Certificates等字段会展开存入jwksKeys表。这为验证 Grafana 签发的令牌提供了公钥获取通道。
权限配置:限制 Agent 可调用的端点
插件通过permissions选项控制 AI Agent 的调用边界,类型定义明确指出"Overrides use dot-notation paths from the Grafana endpoint tree — invalid paths are type errors"。由于权限路径是类型化的,写错端点路径会在编译期直接报错,而不是留到运行时。示例:
grafana({ key: process.env.GRAFANA_SERVICE_ACCOUNT_TOKEN, permissions: { // 只允许读取健康状态,禁止写入 'health.get': true, 'logs.createOtlp': false, }, });结合 endpointMeta 中的风险等级(packages/grafana/index.ts),可以实现"默认只读、按需开放写操作"的精细化授权策略,降低 Agent 误操作风险。
错误处理与重试策略
插件内置了一套完整的错误处理器,定义在 packages/grafana/error-handlers.ts,覆盖六类场景:
| 错误类型 | 匹配条件 | 处理策略 |
|---|---|---|
RATE_LIMIT_ERROR | HTTP 429 或消息含 rate_limited/ratelimited/429 | 最多重试 5 次,优先采用Retry-After头 |
AUTH_ERROR | HTTP 401 或 unauthorized/invalid token/token expired 等 | 不重试,提示检查 Service Account Token |
PERMISSION_ERROR | HTTP 403 或 forbidden/permission denied 等 | 不重试 |
NOT_FOUND_ERROR | HTTP 404 或 not found | 不重试 |
NETWORK_ERROR | 消息含 network/connection/ECONNREFUSED/ETIMEDOUT 等 | 最多重试 3 次 |
DEFAULT | 兜底匹配一切 | 不重试,记录 unhandled error |
开发者可以通过grafana({ errorHandlers })传入自定义处理器,插件会在构造时用{ ...errorHandlers, ...options.errorHandlers }合并(packages/grafana/index.ts),自定义项覆盖同名内置项。
数据持久化:插件内置的数据库表
插件的 schema 定义在 packages/grafana/schema/database.ts,包含 6 张表对应的 zod schema:
| 表 | 用途 | 主要字段 |
|---|---|---|
healthStatus | 健康与许可证状态 | version、commit、database、licenseAvailable、checkedAt |
logs | Loki 摄入的日志记录 | timeUnixNano、severityText、body、traceId、spanId、resource、scope |
dashboardQueries | 公共仪表盘查询历史 | accessToken、panelId、from、to、results、queriedAt |
ringStatus | Mimir 哈希环状态快照 | content、contentType、statusCode、fetchedAt |
jwksKeys | JWKS 公钥记录 | use、algorithm、certificates、keyMaterial |
samlSessions | SAML ACS 处理记录 | statusCode、location、message、successful |
所有表均以.loose()声明,允许保存 Grafana 返回的未知扩展字段。端点在成功时通过ctx.db.xxx.upsertByEntityId写入,失败时仅返回错误而不会污染历史数据——这种"成功才落库"的约定保证了数据库里只保留有效观测记录。
测试与验证
插件提供了基于 Jest 的测试,见 packages/grafana/api.test.ts 与 packages/grafana/client.test.ts。api.test.ts是针对真实 Grafana 实例的集成测试,依赖以下环境变量:
GRAFANA_BEARER_TOKEN:Service Account Token;GRAFANA_URL:Grafana 实例地址;GRAFANA_PUBLIC_DASHBOARD_ACCESS_TOKEN/GRAFANA_PUBLIC_DASHBOARD_PANEL_ID:公共仪表盘测试令牌与面板 ID(可选)。
测试逐一调用 11 个端点并用GrafanaEndpointOutputSchemas.*校验返回结构与声明类型一致,client.test.ts则覆盖客户端请求构造与 HTTPS 校验逻辑。本地可通过pnpm test运行(见 packages/grafana/package.json)。
Webhooks 与许可证
README 明确说明 Grafana 插件无 webhook 触发("No webhooks")。源码中grafanaWebhooksNested为空对象、pluginWebhookMatcher恒返回false(packages/grafana/index.ts),keyBuilder对 webhook 来源直接返回空字符串。因此本插件仅需处理出站 API 调用,无需配置入站 webhook 接收地址。
插件遵循Apache-2.0开源许可发布,配置元数据(displayName、description)见 packages/grafana/plugin-docs.yaml,可用于在 Corsair Hub 中展示。
总结
@corsair-dev/grafana以极简的接入方式(一条pnpm add+ 一个grafana()工厂调用)为 Corsair 应用补全了 Grafana 观测能力:读取健康与许可证状态、查询公共仪表盘、向 Loki 写入 OTLP 日志、巡检 Mimir 哈希环、处理 SAML 与 JWKS 令牌流程。其强制的 HTTPS 传输、类型化权限路径、内置错误重试与"成功才落库"的持久化约定,使它适合作为多租户 AI Agent 的 Grafana 数据网关,相关实现均可继续在 packages/grafana 目录下深入阅读。
【免费下载链接】corsairConnect your users to their apps项目地址: https://gitcode.com/GitHub_Trending/corsa/corsair
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考