快速开始

OpenTelemetry 导出

OpenClaw 通过官方 diagnostics-otel 插件使用 OTLP/HTTP(protobuf) 导出诊断数据。任何接受 OTLP/HTTP 的收集器或后端都无需代码更改即可工作。关于本地文件日志以及如何读取它们,请参阅 日志

它如何协同工作

  • 诊断事件 是由 Gateway 网关和内置插件为模型运行、消息流、会话、队列和 exec 发出的结构化进程内记录。
  • diagnostics-otel 插件 订阅这些事件,并通过 OTLP/HTTP 将它们作为 OpenTelemetry 指标追踪日志 导出。
  • 当提供商传输层接受自定义标头时,提供商调用 会从 OpenClaw 可信的模型调用 span 上下文接收 W3C traceparent 标头。插件发出的追踪上下文不会被传播。
  • 只有在诊断表面和插件都启用时,导出器才会附加,因此默认情况下进程内成本接近于零。

快速开始

对于打包安装,请先安装插件:

openclaw plugins install clawhub:@openclaw/diagnostics-otel
{
  plugins: {
    allow: ["diagnostics-otel"],
    entries: {
      "diagnostics-otel": { enabled: true },
    },
  },
  diagnostics: {
    enabled: true,
    otel: {
      enabled: true,
      endpoint: "http://otel-collector:4318",
      protocol: "http/protobuf",
      serviceName: "openclaw-gateway",
      traces: true,
      metrics: true,
      logs: true,
      sampleRate: 0.2,
      flushIntervalMs: 60000,
    },
  },
}

你也可以从 CLI 启用该插件:

openclaw plugins enable diagnostics-otel

导出的信号

信号 其中包含的内容
指标 token 使用量、成本、运行时长、消息流、Talk 事件、队列 lane、会话状态/恢复、exec 和内存压力的计数器与直方图。
追踪 模型使用、模型调用、harness 生命周期、工具执行、exec、webhook/消息处理、上下文组装和工具循环的 span。
日志 启用 diagnostics.otel.logs 时,通过 OTLP 导出的结构化 logging.file 记录。

可以独立切换 tracesmetricslogs。当 diagnostics.otel.enabled 为 true 时,三者默认都开启。

配置参考

{
  diagnostics: {
    enabled: true,
    otel: {
      enabled: true,
      endpoint: "http://otel-collector:4318",
      tracesEndpoint: "http://otel-collector:4318/v1/traces",
      metricsEndpoint: "http://otel-collector:4318/v1/metrics",
      logsEndpoint: "http://otel-collector:4318/v1/logs",
      protocol: "http/protobuf", // grpc is ignored
      serviceName: "openclaw-gateway",
      headers: { "x-collector-token": "..." },
      traces: true,
      metrics: true,
      logs: true,
      sampleRate: 0.2, // root-span sampler, 0.0..1.0
      flushIntervalMs: 60000, // metric export interval (min 1000ms)
      captureContent: {
        enabled: false,
        inputMessages: false,
        outputMessages: false,
        toolInputs: false,
        toolOutputs: false,
        systemPrompt: false,
      },
    },
  },
}

环境变量

变量 用途
OTEL_EXPORTER_OTLP_ENDPOINT 覆盖 diagnostics.otel.endpoint。如果该值已经包含 /v1/traces/v1/metrics/v1/logs,则会按原样使用。
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT / OTEL_EXPORTER_OTLP_METRICS_ENDPOINT / OTEL_EXPORTER_OTLP_LOGS_ENDPOINT 当匹配的 diagnostics.otel.*Endpoint 配置键未设置时使用的特定信号端点覆盖。特定信号配置优先于特定信号环境变量,后者优先于共享端点。
OTEL_SERVICE_NAME 覆盖 diagnostics.otel.serviceName
OTEL_EXPORTER_OTLP_PROTOCOL 覆盖线路协议(目前仅认可 http/protobuf)。
OTEL_SEMCONV_STABILITY_OPT_IN 设为 gen_ai_latest_experimental 可发出最新的实验性 GenAI span 属性(gen_ai.provider.name),而不是旧版 gen_ai.system。无论如何,GenAI 指标始终使用有界、低基数的语义属性。
OPENCLAW_OTEL_PRELOADED 当另一个 preload 或宿主进程已经注册全局 OpenTelemetry SDK 时设为 1。随后插件会跳过自己的 NodeSDK 生命周期,但仍会连接诊断监听器并遵循 traces/metrics/logs

隐私和内容捕获

默认情况下,原始模型/工具内容不会被导出。Span 携带有界标识符(渠道、提供商、模型、错误类别、仅哈希请求 ID),并且绝不包含 prompt 文本、响应文本、工具输入、工具输出或会话键。 Talk 指标仅导出有界事件元数据,例如模式、传输协议、提供商和事件类型。它们不包含 transcript、音频载荷、会话 ID、turn ID、调用 ID、房间 ID 或 handoff token。

出站模型请求可能包含 W3C traceparent 标头。该标头仅从活动模型调用的 OpenClaw 自有诊断追踪上下文生成。现有调用方提供的 traceparent 标头会被替换,因此插件或自定义提供商选项无法伪造跨服务追踪祖先关系。

仅当你的收集器和保留策略已获准保存 prompt、响应、工具或 system-prompt 文本时,才将 diagnostics.otel.captureContent.* 设为 true。每个子键都需要独立选择启用:

  • inputMessages - 用户 prompt 内容。
  • outputMessages - 模型响应内容。
  • toolInputs - 工具参数载荷。
  • toolOutputs - 工具结果载荷。
  • systemPrompt - 组装后的 system/developer prompt。

启用任何子键时,模型和工具 span 只会针对该类别获得有界、已脱敏的 openclaw.content.* 属性。

采样和刷新

  • 追踪: diagnostics.otel.sampleRate(仅根 span,0.0 丢弃全部,1.0 保留全部)。
  • 指标: diagnostics.otel.flushIntervalMs(最小 1000)。
  • 日志: OTLP 日志遵循 logging.level(文件日志级别)。它们使用诊断日志记录脱敏路径,而不是控制台格式化。高流量安装应优先使用 OTLP 收集器采样/过滤,而不是本地采样。
  • 文件日志关联: 当日志调用携带有效诊断追踪上下文时,JSONL 文件日志会包含顶层 traceIdspanIdparentSpanIdtraceFlags,这让日志处理器可以将本地日志行与导出的 span 关联起来。
  • 请求关联: Gateway 网关 HTTP 请求和 WebSocket 帧会创建内部请求追踪作用域。该作用域内的日志和诊断事件默认继承请求追踪,而智能体运行和模型调用 span 会作为子级创建,因此提供商 traceparent 标头会保持在同一个追踪上。

导出的指标

模型使用量

  • openclaw.tokens(计数器,属性:openclaw.tokenopenclaw.channelopenclaw.provideropenclaw.modelopenclaw.agent
  • openclaw.cost.usd(计数器,属性:openclaw.channelopenclaw.provideropenclaw.model
  • openclaw.run.duration_ms(直方图,属性:openclaw.channelopenclaw.provideropenclaw.model
  • openclaw.context.tokens(直方图,属性:openclaw.contextopenclaw.channelopenclaw.provideropenclaw.model
  • gen_ai.client.token.usage(直方图,GenAI 语义约定指标,属性:gen_ai.token.type = input/outputgen_ai.provider.namegen_ai.operation.namegen_ai.request.model
  • gen_ai.client.operation.duration(直方图,秒,GenAI 语义约定指标,属性:gen_ai.provider.namegen_ai.operation.namegen_ai.request.model,可选 error.type
  • openclaw.model_call.duration_ms(直方图,属性:openclaw.provideropenclaw.modelopenclaw.apiopenclaw.transport,以及分类错误上的 openclaw.errorCategoryopenclaw.failureKind
  • openclaw.model_call.request_bytes(直方图,最终模型请求载荷的 UTF-8 字节大小;不含原始载荷内容)
  • openclaw.model_call.response_bytes(直方图,流式模型响应事件的 UTF-8 字节大小;不含原始响应内容)
  • openclaw.model_call.time_to_first_byte_ms(直方图,首个流式响应事件前经过的时间)

消息流

  • openclaw.webhook.received(计数器,属性:openclaw.channelopenclaw.webhook
  • openclaw.webhook.error(计数器,属性:openclaw.channelopenclaw.webhook
  • openclaw.webhook.duration_ms(直方图,属性:openclaw.channelopenclaw.webhook
  • openclaw.message.queued(计数器,属性:openclaw.channelopenclaw.source
  • openclaw.message.processed(计数器,属性:openclaw.channelopenclaw.outcome
  • openclaw.message.duration_ms(直方图,属性:openclaw.channelopenclaw.outcome
  • openclaw.message.delivery.started(计数器,属性:openclaw.channelopenclaw.delivery.kind
  • openclaw.message.delivery.duration_ms(直方图,属性:openclaw.channelopenclaw.delivery.kindopenclaw.outcomeopenclaw.errorCategory

Talk

  • openclaw.talk.event(计数器,属性:openclaw.talk.event_typeopenclaw.talk.modeopenclaw.talk.transportopenclaw.talk.brainopenclaw.talk.provider
  • openclaw.talk.event.duration_ms(直方图,属性:与 openclaw.talk.event 相同;当 Talk 事件报告持续时间时发出)
  • openclaw.talk.audio.bytes(直方图,属性:与 openclaw.talk.event 相同;为报告字节长度的 Talk 音频帧事件发出)

队列和会话

  • openclaw.queue.lane.enqueue(计数器,属性:openclaw.lane
  • openclaw.queue.lane.dequeue(计数器,属性:openclaw.lane
  • openclaw.queue.depth(直方图,属性:openclaw.laneopenclaw.channel=heartbeat
  • openclaw.queue.wait_ms(直方图,属性:openclaw.lane
  • openclaw.session.state(计数器,属性:openclaw.stateopenclaw.reason
  • openclaw.session.stuck(计数器,属性:openclaw.state;仅在没有活跃工作的过期会话记账时发出)
  • openclaw.session.stuck_age_ms(直方图,属性:openclaw.state;仅在没有活跃工作的过期会话记账时发出)
  • openclaw.session.recovery.requested(计数器,属性:openclaw.stateopenclaw.actionopenclaw.active_work_kindopenclaw.reason
  • openclaw.session.recovery.completed(计数器,属性:openclaw.stateopenclaw.actionopenclaw.statusopenclaw.active_work_kindopenclaw.reason
  • openclaw.session.recovery.age_ms(直方图,属性:与对应的恢复计数器相同)
  • openclaw.run.attempt(计数器,属性:openclaw.attempt

会话存活性遥测

diagnostics.stuckSessionWarnMs 是会话存活性诊断的无进展时长阈值。当 OpenClaw 观察到回复、工具、状态、分块或 ACP 运行时进展时,processing 会话不会计入此阈值。输入状态保活不计为进展,因此仍然可以检测到无声的模型或 harness。

OpenClaw 会按仍可观察到的工作对会话分类:

  • session.long_running:活跃的嵌入式工作、模型调用或工具调用仍在取得进展。
  • session.stalled:存在活跃工作,但活跃运行最近未报告进展。停滞的嵌入式运行一开始保持仅观察状态,然后在 diagnostics.stuckSessionAbortMs 后仍无进展时执行中止并清空,使该 lane 后方排队的轮次可以继续。当未设置时,中止阈值默认使用更安全的扩展窗口,即至少 10 分钟且为 diagnostics.stuckSessionWarnMs 的 5 倍。
  • session.stuck:没有活跃工作的过期会话记账。这会立即释放受影响的会话 lane。

恢复会发出结构化的 session.recovery.requestedsession.recovery.completed 事件。诊断会话状态只有在发生变更型恢复结果(abortedreleased)之后,且同一 processing 代仍为当前代时,才会标记为空闲。

只有 session.stuck 会发出 openclaw.session.stuck 计数器、openclaw.session.stuck_age_ms 直方图和 openclaw.session.stuck span。重复的 session.stuck 诊断会在会话保持不变时退避,因此仪表盘应针对持续增长发出告警,而不是对每个 Heartbeat tick 告警。有关配置旋钮和默认值,请参阅配置参考

Harness 生命周期

  • openclaw.harness.duration_ms(直方图,属性:openclaw.harness.idopenclaw.harness.pluginopenclaw.outcome,错误时还有 openclaw.harness.phase

执行

  • openclaw.exec.duration_ms(直方图,属性:openclaw.exec.targetopenclaw.exec.modeopenclaw.outcomeopenclaw.failureKind

诊断内部机制(内存和工具循环)

  • openclaw.memory.heap_used_bytes(直方图,属性:openclaw.memory.kind
  • openclaw.memory.rss_bytes(直方图)
  • openclaw.memory.pressure(计数器,属性:openclaw.memory.level
  • openclaw.tool.loop.iterations(计数器,属性:openclaw.toolNameopenclaw.outcome
  • openclaw.tool.loop.duration_ms(直方图,属性:openclaw.toolNameopenclaw.outcome

导出的 span

  • openclaw.model.usage
    • openclaw.channelopenclaw.provideropenclaw.model
    • openclaw.tokens.*(input/output/cache_read/cache_write/total)
    • 默认使用 gen_ai.system,或在选择启用最新 GenAI 语义约定时使用 gen_ai.provider.name
    • gen_ai.request.modelgen_ai.operation.namegen_ai.usage.*
  • openclaw.run
    • openclaw.outcomeopenclaw.channelopenclaw.provideropenclaw.modelopenclaw.errorCategory
  • openclaw.model.call
    • 默认使用 gen_ai.system,或在选择启用最新 GenAI 语义约定时使用 gen_ai.provider.name
    • gen_ai.request.modelgen_ai.operation.nameopenclaw.provideropenclaw.modelopenclaw.apiopenclaw.transport
    • 错误时包含 openclaw.errorCategory 和可选的 openclaw.failureKind
    • openclaw.model_call.request_bytesopenclaw.model_call.response_bytesopenclaw.model_call.time_to_first_byte_ms
    • openclaw.provider.request_id_hash(基于上游提供商请求 id 的有界 SHA 哈希;不会导出原始 id)
  • openclaw.harness.run
    • openclaw.harness.idopenclaw.harness.pluginopenclaw.outcomeopenclaw.provideropenclaw.modelopenclaw.channel
    • 完成时:openclaw.harness.result_classificationopenclaw.harness.yield_detectedopenclaw.harness.items.startedopenclaw.harness.items.completedopenclaw.harness.items.active
    • 出错时:openclaw.harness.phaseopenclaw.errorCategory、可选的 openclaw.harness.cleanup_failed
  • openclaw.tool.execution
    • gen_ai.tool.nameopenclaw.toolNameopenclaw.errorCategoryopenclaw.tool.params.*
  • openclaw.exec
    • openclaw.exec.targetopenclaw.exec.modeopenclaw.outcomeopenclaw.failureKindopenclaw.exec.command_lengthopenclaw.exec.exit_codeopenclaw.exec.timed_out
  • openclaw.webhook.processed
    • openclaw.channelopenclaw.webhook
  • openclaw.webhook.error
    • openclaw.channelopenclaw.webhookopenclaw.error
  • openclaw.message.processed
    • openclaw.channelopenclaw.outcomeopenclaw.reason
  • openclaw.message.delivery
    • openclaw.channelopenclaw.delivery.kindopenclaw.outcomeopenclaw.errorCategoryopenclaw.delivery.result_count
  • openclaw.session.stuck
    • openclaw.stateopenclaw.ageMsopenclaw.queueDepth
  • openclaw.context.assembled
    • openclaw.prompt.sizeopenclaw.history.sizeopenclaw.context.tokensopenclaw.errorCategory(不包含提示、历史、响应或会话键内容)
  • openclaw.tool.loop
    • openclaw.toolNameopenclaw.outcomeopenclaw.iterationsopenclaw.errorCategory(不包含循环消息、参数或工具输出)
  • openclaw.memory.pressure
    • openclaw.memory.levelopenclaw.memory.heap_used_bytesopenclaw.memory.rss_bytes

当显式启用内容捕获时,模型和工具 span 还可以针对你选择启用的特定内容类别,包含有界且已脱敏的 openclaw.content.* 属性。

诊断事件目录

以下事件支撑上述指标和 span。插件也可以不经 OTLP 导出而直接订阅它们。

模型用量

  • model.usage - token、成本、持续时间、上下文、提供商/模型/渠道、会话 id。usage 是提供商/轮次层面的成本和遥测记账;context.used 是当前提示/上下文快照,当涉及缓存输入或工具循环调用时,它可能低于提供商的 usage.total

消息流

  • webhook.received / webhook.processed / webhook.error
  • message.queued / message.processed
  • message.delivery.started / message.delivery.completed / message.delivery.error

队列和会话

  • queue.lane.enqueue / queue.lane.dequeue
  • session.state / session.long_running / session.stalled / session.stuck
  • run.attempt / run.progress
  • diagnostic.heartbeat(聚合计数器:webhook/queue/session)

Harness 生命周期

  • harness.run.started / harness.run.completed / harness.run.error - agent harness 的每次运行生命周期。包括 harnessId、可选的 pluginId、提供商/模型/渠道和运行 id。完成时会添加 durationMsoutcome、可选的 resultClassificationyieldDetecteditemLifecycle 计数。错误时会添加 phaseprepare/start/send/resolve/cleanup)、errorCategory 和可选的 cleanupFailed

执行

  • exec.process.completed - 终端结果、持续时间、目标、模式、退出码和失败类型。不包含命令文本和工作目录。

没有 exporter 时

你可以在不运行 diagnostics-otel 的情况下,让诊断事件继续可供插件或自定义 sink 使用:

{
  diagnostics: { enabled: true },
}

如需有针对性的调试输出而不提高 logging.level,请使用诊断标志。标志不区分大小写并支持通配符(例如 telegram.**):

{
  diagnostics: { flags: ["telegram.http"] },
}

或者作为一次性环境覆盖:

OPENCLAW_DIAGNOSTICS=telegram.http,telegram.payload openclaw gateway

标志输出会写入标准日志文件(logging.file),并且仍会被 logging.redactSensitive 脱敏。完整指南:诊断标志

禁用

{
  diagnostics: { otel: { enabled: false } },
}

你也可以将 diagnostics-otelplugins.allow 中移除,或运行 openclaw plugins disable diagnostics-otel

相关内容