OpenClaw 诊断指标接入 Prometheus:diagnostics-prometheus 插件安装、指标与抓取实战指南
导读
diagnostics-prometheus 是 OpenClaw 官方提供的 Prometheus 诊断导出插件,负责把 OpenClaw Gateway 的运行时指标(Agent 运行、模型调用、工具执行、消息调度、队列、会话恢复、内存等)以标准 Prometheus 文本格式暴露出来,供 Prometheus、Grafana、VictoriaMetrics 等抓取器采集。它采用拉取(Pull)模型,无需额外部署 OpenTelemetry Collector,且完全复用 Gateway 既有鉴权面。阅读完本文,你将掌握该插件的安装启用、配置方式、受保护抓取端点的调用方法、全部指标与标签语义,以及底层事件管道与指标渲染的实现原理,可直接用于搭建 OpenClaw 的可观测性与告警体系。
主题载体:本文以插件自述文件 extensions/diagnostics-prometheus/README.md 为核心骨架,完整展开其引用的官方文档 docs/gateway/prometheus.md(README 中声明"完整配置面、指标名与抓取示例均在文档中"),并结合插件源码、清单文件与测试用例进行纵深验证。
一、插件是什么:能力边界与数据链路
该插件的定位由自述文件第一段概括:把 Gateway 运行时诊断事件转换成 Prometheus 文本格式指标,支持 Prometheus、Grafana、VictoriaMetrics 及一切兼容抓取器。它只导出指标,不负责 Trace 与日志;需要链路与日志时请使用 OpenTelemetry 导出方案。
从源码清单 openclaw.plugin.json 可确认三个关键事实:
- 插件 id 为
diagnostics-prometheus,activation.onStartup = true,即 Gateway 启动即激活; - 其
configSchema目前为空对象(additionalProperties: false),说明该插件本身没有可配置参数,开箱即用,全部开关都在宿主配置(plugins.entries、diagnostics.enabled)中。
数据链路(依据 index.ts 与 src/service.ts)可以概括为三步:
- 订阅:插件注册为诊断运行时服务,在
service.start()时通过ctx.internalDiagnostics.onEvent(...)订阅诊断事件流(src/service.ts); - 归一化写入:每个诊断事件经
recordDiagnosticEvent()分派到内存中的指标存储(counter / gauge / histogram); - 暴露端点:插件在启动时注册 HTTP 路由,把内存快照渲染为 Prometheus 文本格式(index.ts)。
需要注意:指标只保留在内存,Gateway 重启后计数器归零、gauge 从下一次上报值重新开始。这也是官方文档提醒要用 rate() / increase() 这类能容忍重置的 PromQL 函数的原因。
二、安装与启用(含最低宿主版本要求)
2.1 安装插件
README 给出了最直接的安装命令(npm 包形式):
openclaw plugins install @openclaw/diagnostics-prometheus
官方文档 docs/gateway/prometheus.md 的快速开始则推荐从 ClawHub 安装:
openclaw plugins install clawhub:@openclaw/diagnostics-prometheus
两条命令等价,差别只是包来源。从 package.json 可以看到该插件的发布策略:同时发布到 ClawHub 与 npm(publishToClawHub: true、publishToNpm: true),默认安装源为 npm(defaultChoice: "npm")。安装/更新后必须重启 Gateway,因为 HTTP 路由是在插件启动阶段注册的(index.ts 中 register 即注册路由)。
版本约束:README 与 package.json 一致声明——最低 OpenClaw 宿主版本为 2026.4.25(minHostVersion: ">=2026.4.25"),同时要求插件 API 不低于 2026.8.1(pluginApi: ">=2026.8.1")。在旧版本宿主上安装会无法加载。
2.2 启用插件与诊断总开关
启用方式二选一:
方式 A:修改配置文件(配置位于 plugins.entries.diagnostics-prometheus):
{
plugins: {
allow: ["diagnostics-prometheus"],
entries: {
"diagnostics-prometheus": { enabled: true },
},
},
diagnostics: {
enabled: true,
},
}
方式 B:CLI 快捷启用:
openclaw plugins enable diagnostics-prometheus
关于 diagnostics.enabled 有一个必须理解的细节(官方文档 docs/gateway/prometheus.md 以 Note 强调):
- 它默认就是
true,只有在资源极度受限的环境才显式设为false; - 如果设为
false,插件仍会注册 HTTP 路由,但不会有任何诊断事件流入导出器,抓取结果将是空响应体。这是排障时第一优先检查项。
2.3 验证启用状态
启用后可用如下命令确认插件已加载:
openclaw plugins list --enabled
三、抓取受保护指标端点
3.1 端点与响应格式
指标暴露在 Gateway 的:
GET /api/diagnostics/prometheus
响应 Content-Type 为 text/plain; version=0.0.4; charset=utf-8,即标准 Prometheus 文本格式 0.0.4。端点处理器逻辑(src/service.ts)细节如下:
- 仅接受
GET与HEAD,其余方法返回405 Method Not Allowed,并带Allow: GET, HEAD响应头; HEAD请求返回与GET完全一致的Content-Length(字节精确),但响应体为空——这个行为由 src/service.test.ts 中 "sends byte-accurate representation metadata on HEAD" 用例覆盖验证;- 响应带
Cache-Control: no-store,禁止抓取器与中间层缓存。
3.2 鉴权要求(重要安全约束)
该路由不是公开的无鉴权 /metrics 端点。 依据 index.ts,路由声明了 auth: "gateway" 且 gatewayRuntimeScopeSurface: "trusted-operator"——即 Gateway 鉴权、operator 作用域、可信运维面。官方文档给出直白警告:不要把它暴露为公开的 /metrics,抓取时应走与其他 operator API 相同的鉴权路径。
手动验证抓取(使用 operator 客户端同款令牌):
curl -H "Authorization: Bearer $OPENCLAW_GATEWAY_TOKEN" \
http://127.0.0.1:18789/api/diagnostics/prometheus
3.3 配置 Prometheus 抓取任务
将 Gateway 令牌写入文件(供 Prometheus 读取),例如 /etc/prometheus/openclaw-gateway-token,然后在 prometheus.yml 中声明抓取任务:
# prometheus.yml
scrape_configs:
- job_name: openclaw
scrape_interval: 30s
metrics_path: /api/diagnostics/prometheus
authorization:
credentials_file: /etc/prometheus/openclaw-gateway-token
static_configs:
- targets: ["openclaw-gateway:18789"]
若再叠加 Grafana 面板或 VictoriaMetrics 作为远端存储,只需把该抓取任务挂到对应采集链即可,指标面完全兼容。
四、导出的指标全表
以下全部指标、类型与标签清单来自官方文档 docs/gateway/prometheus.md 的"Metrics exported"章节,与 src/service.ts 中的实际写入逻辑一一对应,可直接作为 dashboard 与告警规则的设计依据:
| Metric | 类型 | 标签 |
|---|---|---|
openclaw_run_completed_total |
counter | channel、model、outcome、provider、trigger |
openclaw_run_duration_seconds |
histogram | channel、model、outcome、provider、trigger |
openclaw_model_call_total |
counter | api、error_category、model、observation_unit、outcome、provider、transport |
openclaw_model_call_duration_seconds |
histogram | api、error_category、model、observation_unit、outcome、provider、transport |
openclaw_model_failover_total |
counter | from_model、from_provider、lane、reason、suspended、to_model、to_provider |
openclaw_model_tokens_total |
counter | agent、channel、model、provider、token_type |
openclaw_gen_ai_client_token_usage |
histogram | model、provider、token_type |
openclaw_model_cost_usd_total |
counter | agent、channel、model、provider |
openclaw_model_usage_duration_seconds |
histogram | agent、channel、model、provider |
openclaw_skill_used_total |
counter | activation、agent、skill、source |
openclaw_tool_execution_total |
counter | error_category、outcome、params_kind、tool、tool_owner、tool_source |
openclaw_tool_execution_duration_seconds |
histogram | error_category、outcome、params_kind、tool、tool_owner、tool_source |
openclaw_tool_execution_blocked_total |
counter | denied_reason、params_kind、tool、tool_owner、tool_source |
openclaw_harness_run_total |
counter | channel、error_category、harness、model、outcome、phase、plugin、provider |
openclaw_harness_run_duration_seconds |
histogram | channel、error_category、harness、model、outcome、phase、plugin、provider |
openclaw_webhook_received_total |
counter | channel、webhook |
openclaw_webhook_error_total |
counter | channel、webhook |
openclaw_webhook_duration_seconds |
histogram | channel、webhook |
openclaw_message_received_total |
counter | channel、source |
openclaw_message_dispatch_started_total |
counter | channel、source |
openclaw_message_dispatch_completed_total |
counter | channel、outcome、reason、source |
openclaw_message_dispatch_duration_seconds |
histogram | channel、outcome、reason、source |
openclaw_message_processed_total |
counter | channel、outcome、reason |
openclaw_message_processed_duration_seconds |
histogram | channel、outcome、reason |
openclaw_message_delivery_started_total |
counter | channel、delivery_kind |
openclaw_message_delivery_total |
counter | channel、delivery_kind、error_category、outcome |
openclaw_message_delivery_duration_seconds |
histogram | channel、delivery_kind、error_category、outcome |
openclaw_talk_event_total |
counter | brain、event_type、mode、provider、transport |
openclaw_talk_event_duration_seconds |
histogram | brain、event_type、mode、provider、transport |
openclaw_talk_audio_bytes |
histogram | brain、event_type、mode、provider、transport |
openclaw_queue_lane_size |
gauge | lane |
openclaw_queue_lane_wait_seconds |
histogram | lane |
openclaw_session_state_total |
counter | reason、state |
openclaw_session_queue_depth |
gauge | state |
openclaw_session_turn_created_total |
counter | agent、channel、trigger |
openclaw_session_stuck_total |
counter | reason、state |
openclaw_session_stuck_age_seconds |
histogram | reason、state |
openclaw_session_recovery_total |
counter | action、active_work_kind、state、status |
openclaw_session_recovery_age_seconds |
histogram | action、active_work_kind、state、status |
openclaw_liveness_warning_total |
counter | reason |
openclaw_liveness_sessions |
gauge | state |
openclaw_liveness_event_loop_delay_p99_seconds |
histogram | reason |
openclaw_liveness_event_loop_delay_max_seconds |
histogram | reason |
openclaw_liveness_event_loop_utilization_ratio |
histogram | reason |
openclaw_liveness_cpu_core_ratio |
histogram | reason |
openclaw_payload_large_total |
counter | action、channel、plugin、reason、surface |
openclaw_payload_large_bytes |
histogram | action、channel、plugin、reason、surface |
openclaw_memory_bytes |
gauge | kind |
openclaw_memory_rss_bytes |
histogram | 无 |
openclaw_memory_pressure_total |
counter | level、reason |
openclaw_telemetry_exporter_total |
counter | exporter、reason、signal、status |
openclaw_prometheus_series_dropped_total |
counter | 无 |
openclaw_diagnostic_async_queue_dropped_total |
counter | drop_class |
openclaw_diagnostic_async_queue_length |
gauge | 无 |
4.1 观察单位:request 与 turn 的区分
模型调用类指标中,observation_unit 标签取值有两种(src/service.ts):
"request":衡量一次可观测的 provider 请求;"turn":衡量一次合成的 Claude Code 或 Codex CLI Agent 回合(synthetic turn),一次 turn 内部可能隐藏多次 provider 请求。
对比延迟时必须保持 observation_unit 维度独立,不要混算。这一语义在 src/service.test.ts 的 "separates request and turn model-call metrics by observation unit" 用例中被显式验证(同一 provider 的 request 与 turn 会渲染成两条带不同 observation_unit 的序列)。
五、标签策略:低基数、有界与敏感信息防护
这是本插件安全设计的核心,直接决定指标能否长期稳定接入生产 Prometheus。
5.1 有界、低基数标签
导出器绝不输出原始诊断标识符,例如 runId、sessionKey、sessionId、callId、toolCallId、消息 ID、chat ID、provider request ID 等。所有标签值都会经过归一化处理,并必须匹配 OpenClaw 的低基数字符策略:
- 不满足策略的值会被替换为
unknown、other或none(随指标语境选择); - 形如 scoped agent session key 的标签值(如
Agent:qa:otel-trace-smoke)也会被替换为unknown。
从 src/service.ts 可以看见实现层面的配套动作:标签按键名排序后参与序列键计算、标签值做 \、换行、引号转义、非法数值渲染为 0,从格式上杜绝指标注入与高基数污染。测试 "drops session-shaped agent labels"、"bounds messaging labels without exporting raw chat identifiers" 等用例对此逐项断言。
5.2 时间序列上限与溢出核算
导出器在内存中把保留的序列数量上限设为 2048(counter、gauge、histogram 合计),常量定义见 src/service.ts。当新序列突破上限时:
- 该序列被丢弃,且每次丢弃令
openclaw_prometheus_series_dropped_total自增 1; - 上限永远不会被自动放宽;如果该计数器持续爬升,说明上游某个属性在泄漏高基数值,正确的处理是修复源头而不是关掉上限。
对应实现见 src/service.ts 的 canCreateSeries,以及被测试 "caps metric series growth and reports dropped series"(灌入 2100 条不同 model 序列)验证。
5.3 什么永远不出现在 Prometheus 输出中
官方文档明确列出以下内容绝不进入指标输出:
- 提示词(prompt)文本、回复文本、工具输入、工具输出、系统提示词;
- Talk 转录文本、音频载荷、call id、room id、handoff token、turn id、原始 session id;
- 原始 provider 请求 ID(指标上永不出现;仅在 span 场景允许有界哈希);
- session key 与 session ID;
- hostname、文件路径、密钥值。
实现上,所有标签都经 normalizeDiagnosticValue() 归一,错误消息在落指标前还会走 redactSensitiveText() 敏感信息擦除 + UTF-16 安全截断(上限 500 字符),见 src/service.ts。测试 "redacts and bounds label values" 验证了像 Bearer sk-secret-token-value 这类值最终只会变成 error_category="other",原始密钥绝不出现。
5.4 事件可信度过滤
诊断事件仅在元数据 trusted 为真,或属于 dispatcher 内部标注的"内部诊断事件"时才会被记录(shouldRecordDiagnosticEvent,见 src/service.ts)。这意味着:
- 不可信的第三方插件伪造的
webhook.received、payload.large、session.stuck等稳定性信号会被直接丢弃,防止插件污染 Gateway 运维指标; - 测试 "drops untrusted plugin-emitted diagnostic events" 与 "drops untrusted plugin-emitted diagnostic events that spoof gateway stability signals" 覆盖了这条安全边界。
六、PromQL 实战配方
官方文档 docs/gateway/prometheus.md 提供的 PromQL 配方可以直接套用于 Grafana 面板与告警规则:
# Tokens per minute, split by provider
sum by (provider) (rate(openclaw_model_tokens_total[1m]))
# Spend (USD) over the last hour, by model
sum by (model) (increase(openclaw_model_cost_usd_total[1h]))
# 95th percentile model run duration
histogram_quantile(
0.95,
sum by (le, provider, model)
(rate(openclaw_run_duration_seconds_bucket[5m]))
)
# Queue wait time SLO (95p under 2s)
histogram_quantile(
0.95,
sum by (le, lane) (rate(openclaw_queue_lane_wait_seconds_bucket[5m]))
) < 2
# Skill usage, split by bounded source
sum by (skill, source) (increase(openclaw_skill_used_total[24h]))
# Dropped Prometheus series (cardinality alarm)
increase(openclaw_prometheus_series_dropped_total[15m]) > 0
文档同时给出一个重要建议:跨 provider 的成本/用量面板优先使用 openclaw_gen_ai_client_token_usage——该指标遵循 OpenTelemetry GenAI 语义约定(其来源为 model.usage 事件的 input/output token 直方图,见 src/service.ts),与来自非 OpenClaw GenAI 服务的指标口径一致,便于统一大盘。
补充说明几个与 PromQL 配方的底层适配点:
- 时长类 histogram 的桶:秒级桶序列为
0.005 ~ 600共 16 档,令牌桶为1 ~ 1048576共 11 档,字节桶覆盖1024 ~ 17179869184共 13 档,比率桶覆盖0.01 ~ 16共 11 档(常量见 src/service.ts),毫秒值统一除以 1000 转成秒(seconds()辅助函数); - counter 计数的聚合方式:内存中同一指标名+同一标签集合的样本直接累加(
existing.value += amount),例如多次model.usage事件会聚合为单条 token 序列,测试 "aggregates plugin usage without adding a plugin label" 验证了这一点。
七、Prometheus 与 OpenTelemetry 导出如何选择
OpenClaw 的两种指标出口互相独立,可跑任意一种、两种都跑、或都不跑(docs/gateway/prometheus.md)。
选择 diagnostics-prometheus 的典型场景:
- 需要**拉取(Pull)**模型:Prometheus 定期来抓
/api/diagnostics/prometheus; - 不希望为指标单独部署外部 Collector;
- 鉴权上复用 Gateway 现有 auth 即可;
- 只需要 metrics,不需要 traces/logs;
- 团队已经标准化在 Prometheus + Grafana 技术栈。
选择 diagnostics-otel 的典型场景:
- 需要**推送(Push)**模型:OpenClaw 通过 OTLP/HTTP 主动上报到 Collector 或 OTLP 兼容后端;
- 需要 metrics + traces + logs 全信号;
- 需要同时落到 Prometheus 时,可通过 OpenTelemetry Collector 的
prometheus/prometheusremotewriteexporter 桥接。
完整目录见 OpenTelemetry 导出文档。
八、源码级原理:从事件到文本的完整管道
8.1 启动即注册:路由与订阅
extensions/diagnostics-prometheus/index.ts 是唯一入口,加载时立即构建导出器 createDiagnosticsPrometheusExporter(),并在 register(api) 中做两件事:
api.registerService(exporter.service):注册生命周期服务;api.registerHttpRoute({ path: "/api/diagnostics/prometheus", auth: "gateway", match: "exact", gatewayRuntimeScopeSurface: "trusted-operator", handler: exporter.handler }):注册精确匹配、受 Gateway 鉴权保护的路由。
在 src/service.ts 的 service.start() 中,插件通过 ctx.internalDiagnostics.onEvent(...) 订阅诊断事件,并在成功订阅后:
- 上报 exporter 健康状态(
signal: "metrics"、transport: "prometheus-scrape"、status: "started"); - 通过内部诊断桥发出
telemetry.exporter生命周期事件(该事件最终体现为openclaw_telemetry_exporter_total指标)。
stop() 时对称地反订阅、上报 dropped 状态并清空指标存储(src/service.ts)。测试 src/service.test.ts 中 "subscribes to internal diagnostics and renders scrape text" 完整断言了从订阅、上报 health、渲染到 stop 清理的整条生命周期。
8.2 内存指标存储:counter/gauge/histogram
createPrometheusMetricStore()(src/service.ts)用三张 Map 分别保存 counter、gauge、histogram,每张以"指标名 + 排序后的标签 JSON"作为序列键(metricKey())。写入时遵循数值卫生规则:
- counter 只接受
> 0且有限的增量; - gauge 忽略
undefined或非有限值; - histogram 忽略负值/非有限值,并按桶累加计数、
sum、count; - 新序列受 2048 上限约束(见 5.2)。
8.3 事件分派与标签归一化
recordDiagnosticEvent() 是一个覆盖二十余种事件类型的大分派(src/service.ts),每种事件类型对应一组专门的标签构建函数(如 runLabels、modelCallLabels、toolExecutionLabels、sessionRecoveryLabels、payloadLargeLabels、talkLabels 等),统一调用 normalizeDiagnosticValue / normalizeDiagnosticLane 进行低基数归一。事件类型覆盖 Agent 运行(run.completed)、模型调用(model.call.completed/error、model.failover、model.usage)、工具执行(tool.execution.*、含被策略/沙箱拦截的 blocked)、技能使用(skill.used)、harness 运行、webhook 入站、消息收/派发/投递全链路、队列长度与等待、会话状态/卡死/恢复、liveness 告警、内存采样与压力、超大载荷、telemetry exporter 生命周期与异步诊断队列丢弃汇总等,与第四章指标全表一一对应。
8.4 渲染输出
renderPrometheusMetrics()(src/service.ts)按指标名排序后:
- 每个指标名只输出一次
# HELP与# TYPE头(用emitted集合去重); - counter/gauge 输出单行样本;
- histogram 输出
<name>_bucket{le="桶上限"}、le="+Inf"、_sum、_count完整四件套; - 行尾统一追加空行作为文本格式的结尾。
九、故障排查手册
以下排查路径出自官方文档 docs/gateway/prometheus.md 的 Troubleshooting 章节:
空响应体
- 检查配置中
diagnostics.enabled是否被显式设为false(默认true); - 用
openclaw plugins list --enabled确认插件已启用且已加载; - 生成一些流量再抓取——counter 与 histogram 只有在至少发生一次事件后才会输出对应序列,全新实例的端点内容为空是正常现象。
401 / unauthorized
端点要求 Gateway operator 作用域(auth: "gateway" 配合 gatewayRuntimeScopeSurface: "trusted-operator")。使用与其他 Gateway operator 路由相同的 token 或口令;不存在公开的无鉴权模式。
openclaw_prometheus_series_dropped_total 持续上涨
上游新增了某个超过 2048 序列上限的高基数属性。检查近期指标中异常高基数的标签并在源头修复。导出器刻意选择丢弃新序列而非静默改写标签,所以这个计数器是可靠的"高基数泄漏"告警信号。
重启后 Prometheus 显示序列过期
插件状态只保存在内存。Gateway 重启后 counter 归零、gauge 从下次上报值重新开始。请使用 PromQL rate() 与 increase() 干净地处理重置。
十、延伸阅读
- 插件自述文件 —— 安装与基本信息
- 插件入口与路由注册
- 指标存储、事件分派与渲染实现
- 行为与安全边界测试(覆盖标签脱敏、基数上限、可信事件过滤、HTTP 语义等)
- 插件清单与配置面
- 插件包与发布约束
- Prometheus 指标官方文档
- 诊断导出总览 —— 本地诊断 zip(支持包)
- 健康与就绪探针 ——
/healthz、/readyz - OpenTelemetry 导出 —— traces/metrics/logs 的 OTLP push 方案
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0629
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python07
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00