clawpro-diagnostics-metrics-cls 1.0.3 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +689 -16
- package/index.ts +175 -5
- package/openclaw.plugin.json +57 -3
- package/package.json +12 -10
- package/src/cls-service.ts +119 -125
- package/src/credential.ts +52 -22
- package/src/instance-metadata.ts +386 -161
- package/src/remote-write.ts +92 -8
- package/src/shared-constants.ts +8 -0
- package/src/shared-credential.ts +398 -0
- package/src/trace-config.ts +176 -0
- package/src/trace-constants.ts +61 -0
- package/src/trace-sender.ts +470 -0
- package/src/trace-service.ts +2129 -0
- package/src/trace-types.ts +141 -0
- package/src/trace-utils.ts +666 -0
- package/src/__tests__/cls-service.test.ts +0 -1544
- package/src/__tests__/crypto-utils.test.ts +0 -92
- package/src/__tests__/protobuf.test.ts +0 -255
- package/src/__tests__/remote-write.test.ts +0 -544
package/README.md
CHANGED
|
@@ -4,6 +4,7 @@ OpenClaw 诊断指标导出插件,提供以下核心能力:
|
|
|
4
4
|
|
|
5
5
|
- **Prometheus 指标采集**:通过 `/metrics` HTTP 端点暴露指标(Pull 模式),或通过 Prometheus Remote Write 协议主动推送到远端(Push 模式)
|
|
6
6
|
- **腾讯云 CLS Prometheus Remote Write**:通过腾讯云 CLS 的 Prometheus Remote Write 接口推送指标数据
|
|
7
|
+
- **全链路 Trace 追踪(v2)**:将 Agent 执行过程中的 Span 数据直接写入腾讯云日志服务(CLS),支持完整的 Trace Context 传播——所有 Span 共享同一个 traceId,通过显式的父子关系串联成一棵调用树,可以完整看到一次请求的全貌。遵循 OTel GenAI 语义约定,支持分段式 LLM Span、Step 层级、跨通道上下文链接、任务队列等高级特性
|
|
7
8
|
|
|
8
9
|
---
|
|
9
10
|
|
|
@@ -14,8 +15,10 @@ OpenClaw 诊断指标导出插件,提供以下核心能力:
|
|
|
14
15
|
- [配置说明](#配置说明)
|
|
15
16
|
- [CLS 配置](#cls-配置)
|
|
16
17
|
- [Prometheus 配置](#prometheus-配置可选)
|
|
18
|
+
- [Trace 配置](#trace-配置可选)
|
|
17
19
|
- [功能说明](#功能说明)
|
|
18
20
|
- [Prometheus 指标列表](#prometheus-指标列表)
|
|
21
|
+
- [全链路 Trace 追踪](#全链路-trace-追踪)
|
|
19
22
|
- [腾讯云实例元数据注入](#腾讯云实例元数据注入)
|
|
20
23
|
- [更新配置(UpdateParameter)](#更新配置updateparameter)
|
|
21
24
|
- [配置热更新](#配置热更新)
|
|
@@ -64,6 +67,8 @@ npx --yes clawpro-diagnostics-metrics-cls-onboard-cli install
|
|
|
64
67
|
| `--prometheusEnabled <bool>` | 是否开启 Prometheus 指标上报(`true`/`false`) | 可选 |
|
|
65
68
|
| `--pushIntervalMs <ms>` | Prometheus Remote Write 推送间隔(毫秒) | 可选 |
|
|
66
69
|
| `--externalLabels <labels>` | Prometheus 自定义标签(格式:`key1=value1,key2=value2`) | 可选 |
|
|
70
|
+
| `--traceEnabled <bool>` | 是否启用 Trace 链路追踪功能(`true`/`false`,默认 `true`) | 可选 |
|
|
71
|
+
| `--traceTopicId <id>` | Trace 数据上报使用的 CLS 日志主题 ID | 可选(可在配置文件中动态更新) |
|
|
67
72
|
|
|
68
73
|
### 安装示例
|
|
69
74
|
|
|
@@ -103,6 +108,18 @@ npx clawpro-diagnostics-metrics-cls-onboard-cli install \
|
|
|
103
108
|
--roleName "MyCustomRoleName"
|
|
104
109
|
```
|
|
105
110
|
|
|
111
|
+
同时配置 Trace 链路追踪(指定 Trace 日志主题 ID):
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
npx clawpro-diagnostics-metrics-cls-onboard-cli install \
|
|
115
|
+
--metricTopicId "xxxxxxxx-metric-topic-id" \
|
|
116
|
+
--secretId "AKIDxxxxxxxx" \
|
|
117
|
+
--secretKey "xxxxxxxxxxxxxxxx" \
|
|
118
|
+
--endpoint "ap-guangzhou.cls.tencentcs.com" \
|
|
119
|
+
--prometheusEnabled true \
|
|
120
|
+
--traceTopicId "zzzzzzzz-trace-topic-id"
|
|
121
|
+
```
|
|
122
|
+
|
|
106
123
|
安装完成后,CLI 会自动重启网关并输出确认信息:
|
|
107
124
|
|
|
108
125
|
```
|
|
@@ -114,6 +131,8 @@ npx clawpro-diagnostics-metrics-cls-onboard-cli install \
|
|
|
114
131
|
指标上报: 已开启
|
|
115
132
|
Prometheus Remote Write: https://ap-guangzhou.cls.tencentcs.com/prometheus/xxxxxxxx-metric-topic-id/api/v1/write
|
|
116
133
|
推送间隔: 15000ms
|
|
134
|
+
Trace 链路追踪: 已启用
|
|
135
|
+
Trace 日志主题 ID: zzzzzzzz-trace-topic-id
|
|
117
136
|
|
|
118
137
|
运行以下命令验证插件状态:
|
|
119
138
|
openclaw plugins list
|
|
@@ -129,6 +148,8 @@ npx clawpro-diagnostics-metrics-cls-onboard-cli install \
|
|
|
129
148
|
指标日志主题 ID: xxxxxxxx-metric-topic-id
|
|
130
149
|
指标推送: 已启用
|
|
131
150
|
指标上报: 未开启
|
|
151
|
+
Trace 链路追踪: 已启用
|
|
152
|
+
Trace 日志主题 ID: (未配置,可通过配置文件 trace.traceTopicId 字段动态注入)
|
|
132
153
|
|
|
133
154
|
运行以下命令验证插件状态:
|
|
134
155
|
openclaw plugins list
|
|
@@ -223,7 +244,7 @@ openclaw gateway restart
|
|
|
223
244
|
|
|
224
245
|
## 配置说明
|
|
225
246
|
|
|
226
|
-
所有配置均位于 `~/.openclaw/openclaw.json` 中 `plugins.entries["clawpro-diagnostics-metrics-cls"].config` 下,分为 `cls` 和 `
|
|
247
|
+
所有配置均位于 `~/.openclaw/openclaw.json` 中 `plugins.entries["clawpro-diagnostics-metrics-cls"].config` 下,分为 `cls`、`prometheus` 和 `trace` 三个子对象。
|
|
227
248
|
|
|
228
249
|
### CLS 配置
|
|
229
250
|
|
|
@@ -324,7 +345,7 @@ Prometheus 功能通过插件的 `config.prometheus` 配置项控制,与 `cls`
|
|
|
324
345
|
| `external_labels` | object | `{}` | 附加到所有指标的自定义标签 |
|
|
325
346
|
| `push_interval_ms` | number | `15000` | Remote Write 推送间隔(毫秒) |
|
|
326
347
|
|
|
327
|
-
> **说明**:当部署在腾讯云环境时,插件会自动注入实例元数据作为标签(`cvm_instance_id`、`cvm_instance_name`、`cvm_instance_intra_ip
|
|
348
|
+
> **说明**:当部署在腾讯云环境时,插件会自动注入实例元数据作为标签(`cvm_instance_id`、`cvm_instance_name`、`cvm_instance_intra_ip`)。若在 `external_labels` 中手动配置了同名标签,则以手动配置的值为准。
|
|
328
349
|
|
|
329
350
|
#### Remote Write 配置
|
|
330
351
|
|
|
@@ -371,6 +392,60 @@ Prometheus 功能通过插件的 `config.prometheus` 配置项控制,与 `cls`
|
|
|
371
392
|
|
|
372
393
|
---
|
|
373
394
|
|
|
395
|
+
### Trace 配置(可选)
|
|
396
|
+
|
|
397
|
+
Trace 功能通过插件的 `config.trace` 配置项控制,与 `cls` 和 `prometheus` 配置并列放在 `plugins.entries["clawpro-diagnostics-metrics-cls"].config` 下。
|
|
398
|
+
|
|
399
|
+
**Trace 模块复用 `cls` 配置中的 `secretId` / `secretKey` / `endpoint` 凭证**,只需在 `trace` 中独立配置 `enabled` 和 `traceTopicId`。
|
|
400
|
+
|
|
401
|
+
在 `~/.openclaw/openclaw.json` 中添加:
|
|
402
|
+
|
|
403
|
+
```json
|
|
404
|
+
{
|
|
405
|
+
"plugins": {
|
|
406
|
+
"allow": ["clawpro-diagnostics-metrics-cls"],
|
|
407
|
+
"entries": {
|
|
408
|
+
"clawpro-diagnostics-metrics-cls": {
|
|
409
|
+
"enabled": true,
|
|
410
|
+
"config": {
|
|
411
|
+
"cls": {
|
|
412
|
+
"secretId": "your-secret-id",
|
|
413
|
+
"secretKey": "your-secret-key",
|
|
414
|
+
"endpoint": "ap-guangzhou.cls.tencentcs.com"
|
|
415
|
+
},
|
|
416
|
+
"trace": {
|
|
417
|
+
"enabled": true,
|
|
418
|
+
"traceTopicId": "your-trace-topic-id",
|
|
419
|
+
"serviceName": "openclaw-agent",
|
|
420
|
+
"debug": false
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
#### Trace 基础配置
|
|
430
|
+
|
|
431
|
+
| 字段 | 类型 | 默认值 | 说明 |
|
|
432
|
+
|------|------|--------|------|
|
|
433
|
+
| `enabled` | boolean | `true` | 是否启用 Trace 链路追踪功能 |
|
|
434
|
+
| `traceTopicId` | string | — | **必填**。Trace 数据上报使用的 CLS 日志主题 ID |
|
|
435
|
+
| `serviceName` | string | `"openclaw-agent"` | 服务名称,用于标识 Trace 数据来源,会写入每个 Span 的 `service.name` 属性 |
|
|
436
|
+
| `debug` | boolean | `false` | 是否启用 trace 调试日志,开启后会输出详细的 Span 创建/关闭日志 |
|
|
437
|
+
| `batchSize` | number | `10` | Span 缓冲区大小,达到此数量后触发批量发送 |
|
|
438
|
+
| `flushIntervalMs` | number | `5000` | Span 缓冲区最大等待时间(毫秒),超时后强制发送已缓冲的 Span |
|
|
439
|
+
| `enabledHooks` | string[] | — | 可选。指定启用的 hook 列表,未指定时启用所有 hook。支持**热更新**,修改后约 10 秒内生效。例如 `["message_received", "llm_input", "agent_end"]` |
|
|
440
|
+
|
|
441
|
+
> **注意**:
|
|
442
|
+
> - Trace 功能复用 `cls` 配置中的 `secretId` / `secretKey` / `endpoint` / `token`,无需在 `trace` 中重复配置凭证
|
|
443
|
+
> - Trace 功能使用独立的 CLS 日志主题(`traceTopicId`),与 Prometheus 指标使用的日志主题(`metricTopicId`)分开,便于分别管理和查询
|
|
444
|
+
> - Trace 配置不完整时(缺少 `traceTopicId` 或 `cls` 凭证不完整),trace 功能会自动禁用,不影响 Prometheus 指标功能
|
|
445
|
+
> - **热更新恢复**:即使初始启动时 trace 配置不完整或 `enabled=false`,trace 服务仍会被创建并启动热更新轮询。后续在 `openclaw.json` 中补全配置后,约 10 秒内自动恢复 trace 功能,无需重启网关
|
|
446
|
+
|
|
447
|
+
---
|
|
448
|
+
|
|
374
449
|
## 功能说明
|
|
375
450
|
|
|
376
451
|
### Prometheus 指标列表
|
|
@@ -449,25 +524,502 @@ Histogram 类型指标在 Prometheus 中会自动展开为 `_bucket`(带 `le`
|
|
|
449
524
|
|
|
450
525
|
---
|
|
451
526
|
|
|
527
|
+
### 全链路 Trace 追踪(v2)
|
|
528
|
+
|
|
529
|
+
插件通过 hook 机制捕获 Agent 执行过程中的关键事件,生成符合 **OTel GenAI 语义约定** 的 Span 数据,并通过腾讯云 CLS SDK 实时上报。
|
|
530
|
+
|
|
531
|
+
#### 模块文件结构
|
|
532
|
+
|
|
533
|
+
Trace 模块按职责拆分为以下子模块,便于维护和测试:
|
|
534
|
+
|
|
535
|
+
| 文件 | 职责 | 行数 |
|
|
536
|
+
|------|------|------|
|
|
537
|
+
| `trace-service.ts` | **主入口**:创建 trace 服务实例、注册 hook、热更新轮询 | ~1560 |
|
|
538
|
+
| `trace-constants.ts` | 常量定义(日志前缀、超时阈值、轮询间隔等) | ~60 |
|
|
539
|
+
| `trace-types.ts` | 接口/类型定义(`TraceConfig`、`TraceContext`、`PendingToolCall` 等) | ~130 |
|
|
540
|
+
| `trace-utils.ts` | 工具函数(加密安全 ID 生成、消息格式化、inbound metadata 剥离) | ~470 |
|
|
541
|
+
| `trace-sender.ts` | `ClsSpanSender` 类(Span → CLS Trace 标准扁平化 LogItem 转换与发送) | ~460 |
|
|
542
|
+
| `trace-config.ts` | 配置解析(`resolveTraceConfig`、`readTraceHotConfigFromDisk`) | ~130 |
|
|
543
|
+
| `shared-credential.ts` | **共享凭证管理器**:trace 和指标服务共用同一份临时密钥,统一管理获取、缓存、定时刷新和事件通知 | ~290 |
|
|
544
|
+
|
|
545
|
+
> **对外 API 不变**:`trace-service.ts` 通过 re-export 保持了所有对外导出(`createTraceService`、`resolveTraceConfig`、`stripInboundMetadata`、`TraceConfig`、`LOG_PREFIX`),现有调用方无需修改导入路径。
|
|
546
|
+
|
|
547
|
+
#### 核心设计理念
|
|
548
|
+
|
|
549
|
+
- **完整的调用树**:所有 Span 共享同一个 `traceId`,通过显式的 `parentSpanId` 串联成一棵调用树
|
|
550
|
+
- **Resource 全局属性**:每个 Span 自动注入 `service.name`、`host.name`、`service.instance.id`、`telemetry.sdk.language` 等 OTel Resource 属性,以及 `cvm_instance_id`、`cvm_instance_name` 等 CVM 实例元数据(字段名格式与指标服务一致)
|
|
551
|
+
- **长生命周期 Span**:Entry Span 和 Agent Span 在请求开始时创建,在请求结束时关闭,确保完整的时间跨度
|
|
552
|
+
- **分段式 LLM Span**:通过 `before_message_write` hook 实现分段导出,每次 LLM 响应(包含 tool call)都会生成独立的 LLM Span
|
|
553
|
+
- **Step 层级**:每轮 LLM + Tool 调用构成一个 react step,形成清晰的推理循环
|
|
554
|
+
- **跨通道上下文链接**:agent 通道自动链接到最近的用户通道上下文
|
|
555
|
+
- **任务队列**:保证同一 trace 的操作按顺序执行,内置上限保护(默认 10000 条),防止异常场景下内存泄漏
|
|
556
|
+
- **Pending Assistant 缓冲**:处理 `before_message_write` 先于 `llm_input` 到达的乱序场景
|
|
557
|
+
- **热重载安全的 Hook 注册**:openclaw 框架在 gateway 热重载(SIGUSR1)时会创建新的 plugin registry,旧 registry 上注册的 hook 不会被新的 hookRunner 识别。因此每次 `register()` 调用都会重新注册 hook 到当前的 api 实例上,确保 hook 始终生效。trace 服务实例通过模块级 `cachedTracePlugin` 缓存复用,避免重复创建 Producer 和 sender
|
|
558
|
+
- **定时器不使用 unref()**:所有内部定时器(任务超时、上下文清理、热更新轮询、`agent_end` 延迟关闭等)均不调用 `unref()`,确保 Node.js 进程在定时器回调执行完毕前不会提前退出,避免 Entry/Agent/Step Span 等长生命周期 Span 因进程退出而丢失
|
|
559
|
+
- **主动 flush 缓冲区**:CLS Producer(`tencentcloud-cls-sdk-nodejs`)没有公开的 `flush()` 方法,数据通过 `send()` 进入内部缓冲区后默认每 2 秒由内部定时器批量发送。`ClsSpanSender.flush()` 通过调用 Producer 的 `batchSend()` 方法主动触发缓冲区刷新,确保 `agent_end` 关闭 Span 后数据能及时发送到 CLS,而不是等待下一个 2 秒定时器周期
|
|
560
|
+
- **关键 Hook 和 Span 诊断日志**:`message_received`、`llm_input`、`before_message_write`、`agent_end` 四个关键 hook 以及 `ensureEntrySpan`、`ensureAgentSpan`、`exportPendingLlmSpan`、`endSpanById`(Agent/Entry)、`flush` 等关键操作始终输出诊断日志(不依赖 `debug` 标志),记录 hook 触发状态、Span 创建/关闭/导出/刷新的完整链路,便于排查 trace 数据丢失问题
|
|
561
|
+
- **ACP 模式自动适配**:当 OpenClaw 运行在 ACP(Agent Control Plane)模式下时,框架不会触发 `llm_input`、`llm_output`、`before_agent_start`、`agent_end`、`before_message_write`、`before_tool_call`/`after_tool_call` 等 hook。插件通过 `acpFallbackPending` 标记自动检测 ACP 模式:在 `message_received` 中设置标记,如果后续 `llm_input` 或 `before_agent_start` 被触发则清除标记(非 ACP 模式);如果标记未被清除,`message_sent` 中的延迟定时器(2 秒无新消息后触发)会自动结束 Entry Span 并导出,确保 ACP 模式下也能上报完整的对话 trace 数据。ACP 模式下的 Entry Span 会额外携带 `openclaw.acp_mode=true` 属性以便区分
|
|
562
|
+
|
|
563
|
+
#### Span 层级结构
|
|
564
|
+
|
|
565
|
+
```
|
|
566
|
+
Entry (enter_openclaw_system) ← 长生命周期,message_received → agent_end
|
|
567
|
+
├── Agent (invoke_agent <agentId>) ← 长生命周期,llm_input → agent_end
|
|
568
|
+
│ ├── Step (react step round=1) ← 每轮 LLM+Tool 一个 Step
|
|
569
|
+
│ │ ├── LLM (chat <model>) ← 分段式导出,before_message_write 触发
|
|
570
|
+
│ │ ├── Tool (execute_tool <name>) ← before_tool_call → after_tool_call
|
|
571
|
+
│ │ └── Tool (execute_tool <name>)
|
|
572
|
+
│ ├── Step (react step round=2) ← tool batch 完成后自动开始新 Step
|
|
573
|
+
│ │ └── LLM (chat <model>) ← 最终回复(无 tool call)
|
|
574
|
+
│ ├── Compaction (openclaw.compaction) ← 上下文压缩
|
|
575
|
+
│ └── Subagent (openclaw.subagent.<key>) ← 子 Agent 调用
|
|
576
|
+
├── Session (session_start / session_end) ← 会话生命周期事件
|
|
577
|
+
└── Gateway (gateway_start) ← 网关启动事件
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
#### ACP 模式自动适配
|
|
581
|
+
|
|
582
|
+
当 OpenClaw 运行在 **ACP(Agent Control Plane)模式**下时,对话由 ACP 运行时处理,框架不会触发 `llm_input`、`llm_output`、`before_agent_start`、`agent_end`、`before_message_write`、`before_tool_call`/`after_tool_call` 等 hook。这意味着长生命周期的 Entry Span 无法通过 `agent_end` 正常关闭。
|
|
583
|
+
|
|
584
|
+
插件通过 **`acpFallbackPending` 标记 + 延迟定时器** 机制自动检测并适配 ACP 模式:
|
|
585
|
+
|
|
586
|
+
```
|
|
587
|
+
message_received → 设置 acpFallbackPending=true,创建 Entry Span
|
|
588
|
+
↓
|
|
589
|
+
llm_input / before_agent_start 被触发?
|
|
590
|
+
├── 是 → 清除标记(非 ACP 模式),由 agent_end 正常关闭 Entry Span
|
|
591
|
+
└── 否 → 标记保持(ACP 模式)
|
|
592
|
+
↓
|
|
593
|
+
message_sent → 检测到 acpFallbackPending=true
|
|
594
|
+
→ 调度延迟定时器(2 秒无新消息后触发)
|
|
595
|
+
→ 每次 message_sent 重置定时器(适配流式输出)
|
|
596
|
+
↓
|
|
597
|
+
定时器到期 → 自动结束 Entry Span 并导出
|
|
598
|
+
→ Entry Span 额外携带 openclaw.acp_mode=true 属性
|
|
599
|
+
→ 清理上下文,flush 缓冲区
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
ACP 模式下的 Span 层级结构简化为:
|
|
603
|
+
|
|
604
|
+
```
|
|
605
|
+
Entry (enter_openclaw_system) ← message_received → ACP 回退自动关闭
|
|
606
|
+
├── Session (session_start / session_end) ← 会话生命周期事件(如有)
|
|
607
|
+
└── Gateway (gateway_start) ← 网关启动事件
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
> **说明**:ACP 模式下不会生成 Agent Span、Step Span、LLM Span、Tool Span 等子 Span,因为框架不触发对应的 hook。Entry Span 仍然包含完整的用户输入(`gen_ai.input.messages`)和最终输出(`gen_ai.output.messages`),可用于基本的对话追踪和延迟分析。
|
|
611
|
+
|
|
612
|
+
#### 支持的 Hook 事件
|
|
613
|
+
|
|
614
|
+
| # | Hook 名称 | 对应 Span / 行为 | gen_ai.span.kind | 说明 |
|
|
615
|
+
|---|-----------|-----------------|------------------|------|
|
|
616
|
+
| 1 | `gateway_start` | Gateway Span | ENTRY | 网关启动事件 |
|
|
617
|
+
| 2 | `gateway_stop` | — | — | 网关停止,等待所有排队任务完成后清理资源 |
|
|
618
|
+
| 3 | `session_start` | Session Span | SESSION | 会话开始 |
|
|
619
|
+
| 4 | `session_end` | Session Span | SESSION | 会话结束,记录持续时间、消息数和输出内容,清理通道映射 |
|
|
620
|
+
| 5 | `message_received` | **Entry Span(长生命周期)** | ENTRY | 消息接收,创建 Root Span,记录用户输入 |
|
|
621
|
+
| 6 | `message_sending` | 更新 lastOutput | — | 消息发送中,捕获最终输出 |
|
|
622
|
+
| 7 | `message_sent` | 更新 lastOutput + **ACP 回退** | — | 消息发送完成,捕获最终输出;ACP 模式下调度延迟关闭 Entry Span |
|
|
623
|
+
| 8 | `before_agent_start` | **Agent Span(长生命周期)** | AGENT | 确保 Entry + Agent Span 已创建 |
|
|
624
|
+
| 9 | `llm_input` | 开始 LLM 分段追踪 + **Step Span** | STEP/LLM | LLM 调用开始,创建 Step Span,记录系统指令和输入消息 |
|
|
625
|
+
| 10 | `llm_output` | 回退 LLM Span 导出 | LLM | LLM 调用结束(仅在 before_message_write 未处理时作为回退) |
|
|
626
|
+
| 11 | `before_message_write` | **分段式 LLM Span 导出** | LLM | 核心:assistant 消息写入前触发,导出分段 LLM Span |
|
|
627
|
+
| 12 | `before_tool_call` | 记录 Pending Tool Call | — | 工具调用开始,缓存到 pendingToolCalls |
|
|
628
|
+
| 13 | `after_tool_call` | **Tool Span** | TOOL | 工具调用结束,导出 Tool Span,检查 tool batch 完成状态 |
|
|
629
|
+
| 14 | `before_compaction` | 记录开始时间 | — | 上下文压缩开始 |
|
|
630
|
+
| 15 | `after_compaction` | Compaction Span | TOOL | 上下文压缩结束,记录压缩统计 |
|
|
631
|
+
| 16 | `subagent_spawned` | 记录开始时间 | — | 子 Agent 已创建 |
|
|
632
|
+
| 17 | `subagent_ended` | Subagent Span | AGENT | 子 Agent 结束 |
|
|
633
|
+
| 18 | `agent_end` | **结束 Step + Agent + Entry Span** | — | Agent 结束,延迟 100ms 关闭(等待 lastOutput 写入),传递 agentUsageCost |
|
|
634
|
+
| 19 | `before_reset` | 在 Root Span 上添加 reset 属性 | — | 会话重置事件 |
|
|
635
|
+
|
|
636
|
+
#### Resource 级别全局属性
|
|
637
|
+
|
|
638
|
+
每个 Span 都会自动注入以下 Resource 级别的全局属性(对齐 OTel Resource 语义约定):
|
|
639
|
+
|
|
640
|
+
| 属性 | 说明 | 示例值 |
|
|
641
|
+
|------|------|--------|
|
|
642
|
+
| `service.name` | 服务名称(默认取 `serviceName` 配置或当前工作目录名) | `openclaw-agent` |
|
|
643
|
+
| `service.instance.id` | 服务实例标识(格式:`<serviceName>@<hostname>:<pid>`) | `openclaw-agent@my-host:12345` |
|
|
644
|
+
| `host.name` | 主机名 | `my-host` |
|
|
645
|
+
| `telemetry.sdk.language` | SDK 语言 | `nodejs` |
|
|
646
|
+
| `cvm_instance_id` | CVM 实例 ID(动态获取,仅在腾讯云环境下有值) | `ins-abc123` |
|
|
647
|
+
| `cvm_instance_name` | CVM 实例名称(动态获取) | `my-cvm-instance` |
|
|
648
|
+
| `cvm_instance_intra_ip` | CVM 内网 IP(动态获取) | `10.0.0.1` |
|
|
649
|
+
| `cvm_instance_internet_ip` | CVM 公网 IP(动态获取) | `1.2.3.4` |
|
|
650
|
+
| `cvm_instance_region` | CVM 所在地域(动态获取) | `ap-guangzhou` |
|
|
651
|
+
| `host_name` | 操作系统 hostname(动态获取) | `my-host` |
|
|
652
|
+
|
|
653
|
+
> **说明**:`cvm_*` 和 `host_name` 字段由 `instance-metadata` 模块通过腾讯云元数据接口动态获取,字段名格式与指标服务(Prometheus Remote Write)的 external labels 保持一致,便于在 CLS 控制台中通过相同字段名关联 trace 和指标数据。trace 模块只负责读取元数据,不负责启动/停止刷新(由指标服务统一管理)。非腾讯云环境下这些字段不会注入。
|
|
654
|
+
|
|
655
|
+
#### OTel GenAI 语义约定属性
|
|
656
|
+
|
|
657
|
+
Trace 数据遵循 [OpenTelemetry GenAI Semantic Conventions](https://opentelemetry.io/docs/specs/semconv/gen-ai/) 规范,包含以下标准属性:
|
|
658
|
+
|
|
659
|
+
| 属性 | 适用 Span | 说明 |
|
|
660
|
+
|------|-----------|------|
|
|
661
|
+
| `gen_ai.span.kind` | 所有 | Span 类型:`ENTRY` / `AGENT` / `STEP` / `LLM` / `TOOL` / `SESSION` |
|
|
662
|
+
| `gen_ai.operation.name` | 所有 | 操作名称:`enter` / `invoke_agent` / `react` / `chat` / `execute_tool` 等 |
|
|
663
|
+
| `gen_ai.provider.name` | LLM / Agent | 模型提供商名称 |
|
|
664
|
+
| `gen_ai.system` | LLM | 模型系统标识 |
|
|
665
|
+
| `gen_ai.request.model` | LLM | 请求使用的模型名称 |
|
|
666
|
+
| `gen_ai.response.model` | LLM | 响应返回的模型名称 |
|
|
667
|
+
| `gen_ai.input.messages` | LLM / Agent | 输入消息(OTel GenAI 格式:`[{role, parts: [{type, content}]}]`) |
|
|
668
|
+
| `gen_ai.output.messages` | LLM / Agent | 输出消息(OTel GenAI 格式) |
|
|
669
|
+
| `gen_ai.system_instructions` | LLM | 系统指令 |
|
|
670
|
+
| `gen_ai.response.finish_reasons` | LLM | 完成原因(如 `["stop"]`、`["toolUse"]`) |
|
|
671
|
+
| `gen_ai.usage.input_tokens` | LLM / Agent | 输入 Token 数 |
|
|
672
|
+
| `gen_ai.usage.output_tokens` | LLM / Agent | 输出 Token 数 |
|
|
673
|
+
| `gen_ai.usage.total_tokens` | LLM / Agent | 总 Token 数 |
|
|
674
|
+
| `gen_ai.usage.cache_read.input_tokens` | LLM | 缓存读取 Token 数 |
|
|
675
|
+
| `gen_ai.usage.cache_creation.input_tokens` | LLM | 缓存创建 Token 数 |
|
|
676
|
+
| `gen_ai.tool.name` | Tool | 工具名称 |
|
|
677
|
+
| `gen_ai.tool.call.id` | Tool | 工具调用 ID |
|
|
678
|
+
| `gen_ai.tool.call.arguments` | Tool | 工具调用参数 |
|
|
679
|
+
| `gen_ai.tool.call.result` | Tool | 工具调用结果 |
|
|
680
|
+
| `gen_ai.react.round` | Step | react 推理轮次 |
|
|
681
|
+
| `gen_ai.react.finish_reason` | Step | Step 结束原因 |
|
|
682
|
+
| `gen_ai.session.id` | 所有 | 会话 ID |
|
|
683
|
+
| `gen_ai.user.id` | Entry | 用户 ID |
|
|
684
|
+
| `gen_ai.agent.id` / `gen_ai.agent.name` | Agent | Agent 标识 |
|
|
685
|
+
|
|
686
|
+
#### OpenClaw 自定义属性
|
|
687
|
+
|
|
688
|
+
| 属性 | 说明 |
|
|
689
|
+
|------|------|
|
|
690
|
+
| `openclaw.version` | OpenClaw 版本 |
|
|
691
|
+
| `openclaw.session.id` | 会话 ID |
|
|
692
|
+
| `openclaw.run.id` | 运行 ID(支持临时 runId → 真实 runId 重绑定) |
|
|
693
|
+
| `openclaw.turn.id` | 轮次 ID |
|
|
694
|
+
| `openclaw.message.role` | 消息角色 |
|
|
695
|
+
| `openclaw.message.from` | 消息来源 |
|
|
696
|
+
| `openclaw.reset` | 是否发生了会话重置 |
|
|
697
|
+
| `openclaw.sweep` | 是否被孤儿清理机制关闭 |
|
|
698
|
+
|
|
699
|
+
#### APM 兼容索引属性
|
|
700
|
+
|
|
701
|
+
Trace 数据中同时包含以下 APM 兼容的索引属性,可在 CLS 控制台中进行搜索和分析:
|
|
702
|
+
|
|
703
|
+
| 属性 | 说明 |
|
|
704
|
+
|----------|------|
|
|
705
|
+
| `gen_ai.prompt` | LLM 输入的 prompt 消息聚合(值为 JSON 字符串,格式 `[{role,content}, ...]`) |
|
|
706
|
+
| `gen_ai.completion` | LLM 输出的 completion 消息聚合(值为 JSON 字符串,格式 `[{role,content,finish_reason,tool_calls?}, ...]`) |
|
|
707
|
+
| `gen_ai.usage.*` | Token 用量(input/output/cache/total) |
|
|
708
|
+
| `gen_ai.provider.name` / `gen_ai.model_name` | 模型提供商和模型名称 |
|
|
709
|
+
| `llm.request.type` | LLM 请求类型(chat) |
|
|
710
|
+
|
|
711
|
+
> ⚠️ **字段格式说明**:为避免 CLS 对带点号(`.`)的扁平化 key(如 `gen_ai.prompt.0.role`)按嵌套结构自动展开,从而在 `gen_ai` / `gen_ai.prompt` 等前缀上与其他 leaf 字段产生 `IndexAllFail: fields like 'a' and 'a.b' can not appear in the same topic` 索引冲突,`gen_ai.prompt` 与 `gen_ai.completion` 均采用**单个 leaf 字符串字段**承载整个消息列表(值为 JSON 字符串),CLS 不会再按 `.` 对其内部路径做索引展开。
|
|
712
|
+
>
|
|
713
|
+
> 📜 **超长截断策略**:上报前经 `truncateJsonAttr()` 对聚合结构进行**结构感知式渐进裁剪**,L1(未超限)→ 原样返回;L2(超限)→ 按“从中间向两端”顺序对消息的 `content` / `tool_calls[*].arguments` 逐条减半截断(保留首条 system prompt 与末条最新对话),每个被裁剪的字段保留 `... [truncated, total N chars]` 尾标;L3(极端情况)→ 丢弃中段消息,保留首条 + 末尾 N 条,并插入 `{role:"system", content:"[...omitted K messages, total L chars]"}` 占位。任一级输出均由 `JSON.stringify` 生成,保证为合法 JSON,消费方可直接 `JSON.parse` 并根据 `[truncated` / `[...omitted` 标记识别裁剪情况。
|
|
714
|
+
|
|
715
|
+
#### CLS LogItem 字段声明(完整)
|
|
716
|
+
|
|
717
|
+
每条 Trace 日志最终以 CLS LogItem 形式上报到腾讯云日志服务。LogItem 由 `ClsSpanSender` 的 `sendRecordCore()` 方法构建,对齐 **CLS Trace 标准日志格式**:所有结构化字段(`traceID`/`spanID`/`kind`/`start`/`end`/`duration` 等)作为 LogItem 的**顶层 Content** 直接写入,非结构化属性统一聚合到 `attribute` 字段(JSON 字符串)。
|
|
718
|
+
|
|
719
|
+
**CLS LogItem 顶层字段(均为 Content,对齐 CLS Trace 标准格式):**
|
|
720
|
+
|
|
721
|
+
| 字段名 | 类型 | 说明 | 来源 |
|
|
722
|
+
|---|---|---|---|
|
|
723
|
+
| `traceID` | string (32位hex) | 全局唯一链路追踪 ID,同一次请求的所有 Span 共享此值 | `TraceContext.traceId`,由 `generateId(32)` 在 `getOrCreateContext()` 中生成 |
|
|
724
|
+
| `spanID` | string (16位hex) | 当前 Span 的唯一标识 | `createSpan()` 中由 `generateId(16)` 生成,或由 `ensureEntrySpan`/`ensureAgentSpan`/`ensureStepSpan` 预分配 |
|
|
725
|
+
| `parentSpanID` | string (16位hex) | 父 Span ID,空字符串表示根节点 | `createSpan()` 的 `parentSpanId` 参数 |
|
|
726
|
+
| `kind` | string | CLS Trace 标准 Span 种类:`server`(ENTRY)/ `client`(LLM/TOOL)/ `internal`(其他) | 由 `mapSpanKindToString()` 将 `gen_ai.span.kind` 映射而来 |
|
|
727
|
+
| `name` | string | Span 名称,描述当前操作 | 如 `"enter_openclaw_system"`、`"chat tc-code-latest"`、`"execute_tool search"` |
|
|
728
|
+
| `links` | string (JSON) | 跨 trace 链接列表,固定为 `"[]"` | 硬编码 |
|
|
729
|
+
| `logs` | string (JSON) | Span 事件日志列表,固定为 `"[]"` | 硬编码 |
|
|
730
|
+
| `traceState` | string | W3C traceState,固定为空字符串 | 硬编码 |
|
|
731
|
+
| `start` | string | **开始时间(纳秒级 Unix 时间戳,字符串表示)** | `startMs × 1_000_000`(BigInt 运算避免精度丢失) |
|
|
732
|
+
| `end` | string | **结束时间(纳秒级 Unix 时间戳,字符串表示)** | `endMs × 1_000_000`(BigInt 运算避免精度丢失) |
|
|
733
|
+
| `duration` | string | **Span 持续时间(纳秒,字符串表示)** | `(endMs - startMs) × 1_000_000`(BigInt 运算避免精度丢失) |
|
|
734
|
+
| `attribute` | string (JSON) | 所有非结构化属性聚合的 JSON 对象字符串(包含 `gen_ai.*`、`openclaw.*`、Resource 属性、以及保留的 `durationMs` 等) | `sendRecordCore()` 收集非结构化字段后 `JSON.stringify` |
|
|
735
|
+
| `statusCode` | string | Span 状态码:`OK` / `ERROR` / `UNSET` | `export()` 根据 `error`/`error.type` 判断;`dispose()` / `sweepStaleOpenSpans()` 强制关闭时为 `UNSET` |
|
|
736
|
+
| `statusMessage` | string | 状态详细描述,当前固定为空字符串 | 硬编码 |
|
|
737
|
+
|
|
738
|
+
> **时间单位说明**:`start` / `end` / `duration` **均为纳秒**(与 OTel / CLS Trace 标准一致)。由于纳秒时间戳(约 `1.78×10^18`)超出 JavaScript `Number` 安全整数上限(`2^53-1 ≈ 9×10^15`),内部使用 `BigInt` 运算后转为字符串上报,避免精度丢失。
|
|
739
|
+
>
|
|
740
|
+
> **负值保护**:若出现 `endTime < startTime`(时钟回拨、异常时间戳等),`duration` 会被钳制为 `0`,并打印 warn 日志,避免上报负数污染 CLS 控制台耗时统计。
|
|
741
|
+
>
|
|
742
|
+
> **`LogItem.setTime()`** 使用 `startTime` 的秒级时间戳(`Math.floor(startMs / 1000)`),这是 CLS 日志的索引时间,与 `start` 字段(纳秒)含义相同但粒度不同。
|
|
743
|
+
>
|
|
744
|
+
> ---
|
|
745
|
+
>
|
|
746
|
+
> ### ⚠️ **重要:单位陷阱警告(`duration` vs `durationMs`)**
|
|
747
|
+
>
|
|
748
|
+
> 日志中**同时存在两个"耗时"字段**,单位不同,**切勿在查询/告警中混用**:
|
|
749
|
+
>
|
|
750
|
+
> | 字段 | 所在位置 | 单位 | 典型值(耗时 500ms 的 Span) |
|
|
751
|
+
> |---|---|---|---|
|
|
752
|
+
> | `duration` | LogItem 顶层 Content | **纳秒**(ns) | `"500000000"` |
|
|
753
|
+
> | `durationMs` | `attribute` JSON 对象内 | **毫秒**(ms) | `"500"` |
|
|
754
|
+
>
|
|
755
|
+
> - 🚫 **错误示例**:在 CLS SQL 中写 `duration > 5000` 想表达"耗时超过 5 秒" —— 实际只过滤出 **耗时超过 5 微秒(5000 ns)** 的 Span,几乎所有 Span 都会命中。
|
|
756
|
+
> - ✅ **正确写法**:
|
|
757
|
+
> - 按顶层字段查询:`duration > 5000000000`(5 秒 = 5×10⁹ ns)。
|
|
758
|
+
> - 按 attribute 查询:`attribute.durationMs > 5000`(5 秒 = 5000 ms)。
|
|
759
|
+
> - 两个字段都是**字符串类型**,CLS 数值比较需要确认索引配置;若配置为 text 则范围查询走字典序,可能不符合预期。
|
|
760
|
+
> - **底层精度仍为毫秒**:`Date.now()` 只有毫秒粒度,`duration` 末 6 位恒为 `0`;小于 1ms 的 Span 的 `duration` 为 `0`,不代表 Span 未执行。
|
|
761
|
+
>
|
|
762
|
+
> 如需要跨两个字段统一,建议固定使用 `attribute.durationMs`(可读性更好),仅在对接 OTel/CLS Trace 标准工具链时使用顶层 `duration`。
|
|
763
|
+
|
|
764
|
+
##### 二、Resource 级别属性(所有 Span 共有)
|
|
765
|
+
|
|
766
|
+
由 `getResourceAttrs()` 动态生成,在 `sendRecordCore()` 中放入 OTLP JSON 的 `resourceSpans[0].resource.attributes` 中。
|
|
767
|
+
|
|
768
|
+
| 字段名 | 类型 | 作用 | 来源 | 用途 |
|
|
769
|
+
|--------|------|------|------|------|
|
|
770
|
+
| `service.name` | string | 服务名称 | `config.serviceName` 或 `basename(process.cwd())` 或 `"openclaw-agent"` | 标识上报数据的服务,多服务环境下区分来源 |
|
|
771
|
+
| `service.instance.id` | string | 服务实例唯一标识 | 格式:`<serviceName>@<hostname>:<pid>`,如 `openclaw-agent@my-host:12345` | 区分同一服务的不同实例(多进程/多机器部署) |
|
|
772
|
+
| `host.name` | string | 主机名 | Node.js `os.hostname()` | 标识运行主机 |
|
|
773
|
+
| `telemetry.sdk.language` | string | SDK 语言 | 固定值 `"nodejs"` | OTel 规范要求,标识 SDK 语言 |
|
|
774
|
+
| `cvm_instance_id` | string | CVM 实例 ID | `instance-metadata.ts` 通过腾讯云 metadata API 获取 `instance-id` | 关联腾讯云 CVM 实例,与指标服务的 external labels 保持一致 |
|
|
775
|
+
| `cvm_instance_name` | string | CVM 实例名称 | `instance-metadata.ts` 通过腾讯云 metadata API 获取 `instance-name` | 在 CLS 控制台中显示可读的实例名称 |
|
|
776
|
+
| `cvm_instance_intra_ip` | string | CVM 内网 IP | `instance-metadata.ts` 通过腾讯云 metadata API 获取 `local-ipv4` | 网络定位和故障排查 |
|
|
777
|
+
| `cvm_instance_internet_ip` | string | CVM 公网 IP | `instance-metadata.ts` 通过腾讯云 metadata API 获取 `public-ipv4` | 网络定位和故障排查 |
|
|
778
|
+
| `cvm_instance_region` | string | CVM 所在地域 | `instance-metadata.ts` 通过腾讯云 metadata API 获取 `placement/region` | 标识实例所在地域 |
|
|
779
|
+
| `host_name` | string | 操作系统 hostname | `instance-metadata.ts` 中 `os.hostname()` | 与 `host.name` 类似,字段名格式与指标服务一致 |
|
|
780
|
+
|
|
781
|
+
> **说明**:`cvm_*` 和 `host_name` 字段仅在腾讯云环境下有值,非腾讯云环境不注入。
|
|
782
|
+
|
|
783
|
+
##### 三、通用 Span 属性(`createSpan()` 注入)
|
|
784
|
+
|
|
785
|
+
每个通过 `createSpan()` 创建的 Span 都会自动注入以下属性。
|
|
786
|
+
|
|
787
|
+
| 字段名 | 类型 | 作用 | 来源 | 用途 |
|
|
788
|
+
|--------|------|------|------|------|
|
|
789
|
+
| `openclaw.version` | string | OpenClaw 框架版本号 | `api.runtime?.version`,在 `registerHooks()` 中获取 | 版本追踪,排查版本相关问题 |
|
|
790
|
+
| `openclaw.session.id` | string | 会话 ID | `TraceContext.sessionId`(来自 hook event 的 `sessionId`),回退到 `channelId` | 关联同一会话的多次请求 |
|
|
791
|
+
| `gen_ai.session.id` | string | 会话 ID(OTel GenAI 规范) | 与 `openclaw.session.id` 相同 | OTel GenAI 语义约定的标准字段 |
|
|
792
|
+
| `openclaw.run.id` | string | 运行 ID | `TraceContext.runId`,来自 hook event 的 `runId`,支持临时 runId(`__temp_run_` 前缀)→ 真实 runId 重绑定 | 关联同一次 Agent 运行的所有 Span |
|
|
793
|
+
| `openclaw.turn.id` | string | 轮次 ID | `TraceContext.turnId`,由 `generateId(16)` 在 `getOrCreateContext()` 中生成 | 区分同一会话中的不同对话轮次 |
|
|
794
|
+
|
|
795
|
+
##### 四、Entry Span 字段(`ensureEntrySpan()`)
|
|
796
|
+
|
|
797
|
+
Entry Span 是长生命周期的 Root Span,在 `message_received` 时创建,在 `agent_end` 或 ACP 回退时关闭。
|
|
798
|
+
|
|
799
|
+
| 字段名 | 类型 | 作用 | 来源 | 用途 |
|
|
800
|
+
|--------|------|------|------|------|
|
|
801
|
+
| `gen_ai.span.kind` | string | 固定值 `"ENTRY"` | 硬编码 | 标识为入口 Span |
|
|
802
|
+
| `gen_ai.operation.name` | string | 固定值 `"enter"` | 硬编码 | 标识操作类型 |
|
|
803
|
+
| `gen_ai.user.id` | string | 用户 ID | `event.from` 或 `event.metadata?.senderId` 或 `hookCtx.trigger` 或 `"unknown"` | 标识发起请求的用户 |
|
|
804
|
+
| `openclaw.message.role` | string | 消息角色 | `message_received` 中为 `"user"`;`llm_input` 中为 `hookCtx.trigger` 或 `"system"` | 区分消息来源角色 |
|
|
805
|
+
| `openclaw.message.from` | string | 消息来源标识 | `event.from`;`llm_input` 中为 `hookCtx.agentId` 或 `"openclaw"` | 标识消息的具体来源 |
|
|
806
|
+
| `gen_ai.input.messages` | string (JSON) | 用户输入消息 | `TraceContext.userInput`,格式化为 `[{role:"user", parts:[{type:"text", content:...}]}]` | 记录用户原始输入,便于回溯 |
|
|
807
|
+
|
|
808
|
+
**关闭时追加的属性**(`agent_end` 延迟关闭 / ACP 回退关闭):
|
|
809
|
+
|
|
810
|
+
| 字段名 | 类型 | 作用 | 来源 | 用途 |
|
|
811
|
+
|--------|------|------|------|------|
|
|
812
|
+
| `request.duration_ms` | string | 请求总耗时(毫秒) | `endTime - rootSpanStartTime` | 衡量整个请求的端到端耗时 |
|
|
813
|
+
| `gen_ai.output.messages` | string (JSON) | 最终输出消息 | `TraceContext.lastOutput`,由 `message_sending`/`message_sent` hook 捕获,格式化为 OTel GenAI 格式 | 记录 Agent 最终回复内容 |
|
|
814
|
+
| `openclaw.acp_mode` | boolean | 是否为 ACP 模式 | 仅在 ACP 回退关闭时设为 `true` | 区分 ACP 模式和正常 Agent 模式的请求 |
|
|
815
|
+
|
|
816
|
+
##### 五、Agent Span 字段(`ensureAgentSpan()`)
|
|
817
|
+
|
|
818
|
+
Agent Span 是长生命周期 Span,在 `llm_input` / `before_agent_start` 时创建,在 `agent_end` 时关闭。
|
|
819
|
+
|
|
820
|
+
| 字段名 | 类型 | 作用 | 来源 | 用途 |
|
|
821
|
+
|--------|------|------|------|------|
|
|
822
|
+
| `gen_ai.span.kind` | string | 固定值 `"AGENT"` | 硬编码 | 标识为 Agent Span |
|
|
823
|
+
| `gen_ai.operation.name` | string | 固定值 `"invoke_agent"` | 硬编码 | 标识操作类型 |
|
|
824
|
+
| `gen_ai.provider.name` | string | 固定值 `"openclaw"` | 硬编码 | 标识 Agent 提供商 |
|
|
825
|
+
| `gen_ai.agent.id` | string | Agent 标识 | `hookCtx.agentId` 或 `"openclaw"` | 标识具体的 Agent |
|
|
826
|
+
| `gen_ai.agent.name` | string | Agent 名称 | 与 `gen_ai.agent.id` 相同 | Agent 的可读名称 |
|
|
827
|
+
|
|
828
|
+
**关闭时追加的属性**(`agent_end`):
|
|
829
|
+
|
|
830
|
+
| 字段名 | 类型 | 作用 | 来源 | 用途 |
|
|
831
|
+
|--------|------|------|------|------|
|
|
832
|
+
| `agent.duration_ms` | string | Agent 执行耗时 | `event.durationMs` 或 `0` | 衡量 Agent 执行耗时 |
|
|
833
|
+
| `agent.message_count` | string | Agent 处理的消息数 | `event.messages.length` | 统计 Agent 处理的消息总数 |
|
|
834
|
+
| `agent.tool_call_count` | string | Agent 发起的工具调用数 | `countToolCallsFromMessages(event.messages)` 统计 | 统计 Agent 使用工具的次数 |
|
|
835
|
+
| `gen_ai.usage.input_tokens` | string | 输入 Token 数 | `TraceContext.lastLlmUsage.input` | 统计 Agent 整体的 Token 消耗 |
|
|
836
|
+
| `gen_ai.usage.output_tokens` | string | 输出 Token 数 | `TraceContext.lastLlmUsage.output` | 统计 Agent 整体的 Token 消耗 |
|
|
837
|
+
| `gen_ai.usage.total_tokens` | string | 总 Token 数 | `TraceContext.lastLlmUsage.total` | 统计 Agent 整体的 Token 消耗 |
|
|
838
|
+
| `gen_ai.input.messages` | string (JSON) | 用户输入消息 | 同 Entry Span | 记录用户原始输入 |
|
|
839
|
+
|
|
840
|
+
##### 六、Step Span 字段(`ensureStepSpan()` / `endStepSpan()`)
|
|
841
|
+
|
|
842
|
+
Step Span 表示一轮 react 推理循环(LLM 调用 + Tool 调用),在 `llm_input` 时创建,在 tool batch 完成或 `agent_end` 时关闭。
|
|
843
|
+
|
|
844
|
+
| 字段名 | 类型 | 作用 | 来源 | 用途 |
|
|
845
|
+
|--------|------|------|------|------|
|
|
846
|
+
| `gen_ai.span.kind` | string | 固定值 `"STEP"` | 硬编码 | 标识为 Step Span |
|
|
847
|
+
| `gen_ai.operation.name` | string | 固定值 `"react"` | 硬编码 | 标识为 react 推理操作 |
|
|
848
|
+
| `gen_ai.react.round` | number | 推理轮次编号 | `TraceContext.stepRoundCounter`,每次创建新 Step 时递增 | 标识当前是第几轮推理 |
|
|
849
|
+
| `gen_ai.react.finish_reason` | string | Step 结束原因 | `"stop"`(正常结束)/ `"agent_end"`(Agent 结束时强制关闭)/ 其他 | 标识 Step 为何结束 |
|
|
850
|
+
|
|
851
|
+
##### 七、LLM Span 字段(`exportPendingLlmSpan()`)
|
|
852
|
+
|
|
853
|
+
LLM Span 是短生命周期 Span(fire-and-forget),由 `before_message_write` 或 `llm_output`(回退)触发导出。
|
|
854
|
+
|
|
855
|
+
| 字段名 | 类型 | 作用 | 来源 | 用途 |
|
|
856
|
+
|--------|------|------|------|------|
|
|
857
|
+
| `gen_ai.span.kind` | string | 固定值 `"LLM"` | 硬编码 | 标识为 LLM Span |
|
|
858
|
+
| `gen_ai.operation.name` | string | 固定值 `"chat"` | 硬编码 | 标识为 chat 操作 |
|
|
859
|
+
| `gen_ai.provider.name` | string | 模型提供商 | `TraceContext.llmProvider`,来自 `llm_input` event 的 `event.provider` | 标识使用的模型提供商(如 `anthropic`、`openai`) |
|
|
860
|
+
| `gen_ai.system` | string | 模型系统标识 | 与 `gen_ai.provider.name` 相同 | OTel GenAI 规范字段 |
|
|
861
|
+
| `gen_ai.request.model` | string | 请求模型名称 | `TraceContext.llmModel`,来自 `llm_input` event 的 `event.model` | 标识请求使用的模型(如 `claude-4-sonnet`) |
|
|
862
|
+
| `gen_ai.response.model` | string | 响应模型名称 | 与 `gen_ai.request.model` 相同 | 标识实际响应的模型 |
|
|
863
|
+
| `gen_ai.usage.input_tokens` | number | 输入 Token 数 | `event.usage.input`(来自 `before_message_write` 或 `llm_output`) | 统计单次 LLM 调用的输入 Token |
|
|
864
|
+
| `gen_ai.usage.output_tokens` | number | 输出 Token 数 | `event.usage.output` | 统计单次 LLM 调用的输出 Token |
|
|
865
|
+
| `gen_ai.usage.total_tokens` | number | 总 Token 数 | `event.usage.total` 或 `input + output + cacheRead + cacheCreation` | 统计单次 LLM 调用的总 Token |
|
|
866
|
+
| `gen_ai.usage.cache_read.input_tokens` | number | 缓存读取 Token 数 | `event.usage.cacheRead` | 统计 prompt cache 命中的 Token 数 |
|
|
867
|
+
| `gen_ai.usage.cache_creation.input_tokens` | number | 缓存创建 Token 数 | `event.usage.cacheWrite` | 统计 prompt cache 创建的 Token 数 |
|
|
868
|
+
| `gen_ai.system_instructions` | string (JSON) | 系统指令 | `TraceContext.llmPendingSystemInstructions`,由 `formatSystemInstructions(event.systemPrompt)` 格式化为 `[{type:"text", content:...}]` | 记录 LLM 的系统 prompt |
|
|
869
|
+
| `gen_ai.input.messages` | string (JSON) | 输入消息 | `TraceContext.llmPendingInputMessages` 或 `llmLastInputMessages`,由 `formatInputMessages()` 格式化为 OTel GenAI 格式 | 记录发送给 LLM 的完整输入(含历史消息) |
|
|
870
|
+
| `gen_ai.output.messages` | string (JSON) | 输出消息 | `formatAssistantOutputMessages()` 格式化 assistant 内容,支持多模态(text / tool_call / reasoning) | 记录 LLM 的完整输出 |
|
|
871
|
+
| `gen_ai.response.finish_reasons` | string (JSON) | 完成原因 | `JSON.stringify([stopReason])`,如 `["stop"]`、`["toolUse"]`、`["end_turn"]` | 标识 LLM 为何停止生成 |
|
|
872
|
+
| `llm.request.type` | string | 固定值 `"chat"` | 硬编码 | APM 兼容字段 |
|
|
873
|
+
|
|
874
|
+
**APM 索引属性**(由 `setIndexedPromptAttrs()` / `setIndexedCompletionAttrs()` 设置):
|
|
875
|
+
|
|
876
|
+
> 为规避 CLS JSON 索引按 `.` 自动展开产生的 `IndexAllFail: fields like 'a' and 'a.b' can not appear in the same topic` 冲突,prompt / completion 聚合为**单个 leaf 字符串字段**,值为 JSON 字符串整体,CLS 作为普通文本 leaf 索引、不再按内部路径展开。
|
|
877
|
+
|
|
878
|
+
| 字段名 | 类型 | 作用 | 来源 | 用途 |
|
|
879
|
+
|--------|------|------|------|------|
|
|
880
|
+
| `gen_ai.prompt` | string (JSON) | 所有 prompt 消息聚合数组 | 解析 `gen_ai.input.messages` JSON 得到 `ChatMessage[]`,最多保留 `MAX_INDEXED_PROMPT_MESSAGES`(20)条,每条形如 `{role, content?}`;`content` 截断到 `MAX_ATTR_CONTENT_LENGTH`(4096)字符;整体经 `truncateJsonAttr()` **结构感知式裁剪**(超限时逐条对中间消息的长字段减半,仍超限则丢弃中段消息并插入占位),总长度 ≤ `MAX_ATTR_LENGTH`(3,200,000)字符,并保证输出是合法 JSON | CLS 控制台中全文搜索 prompt 内容 |
|
|
881
|
+
| `gen_ai.completion` | string (JSON) | 所有 completion 消息聚合数组 | 由 assistant 输出构建,每条形如 `{role, content?, finish_reason, tool_calls?}`;`tool_calls` 由 `extractToolCallsFromContent()` 提取,元素形如 `{id, name, arguments}`;`content` 和 `arguments` 均截断到 4096 字符;整体经 `truncateJsonAttr()` **结构感知式裁剪**后≤ 3,200,000 字符,保证输出是合法 JSON | CLS 控制台中全文搜索 completion / tool_calls |
|
|
882
|
+
|
|
883
|
+
##### 八、Tool Span 字段(`after_tool_call`)
|
|
884
|
+
|
|
885
|
+
Tool Span 是短生命周期 Span,在 `after_tool_call` hook 中导出。
|
|
886
|
+
|
|
887
|
+
| 字段名 | 类型 | 作用 | 来源 | 用途 |
|
|
888
|
+
|--------|------|------|------|------|
|
|
889
|
+
| `gen_ai.span.kind` | string | 固定值 `"TOOL"` | 硬编码 | 标识为 Tool Span |
|
|
890
|
+
| `gen_ai.operation.name` | string | 固定值 `"execute_tool"` | 硬编码 | 标识操作类型 |
|
|
891
|
+
| `gen_ai.tool.name` | string | 工具名称 | `event.toolName`(来自 `before_tool_call`) | 标识调用的工具 |
|
|
892
|
+
| `gen_ai.tool.call.id` | string | 工具调用 ID | `event.toolCallId` 或自动生成 `call_<12位hex>` | 关联 LLM 的 tool_call 和 tool result |
|
|
893
|
+
| `gen_ai.tool.type` | string | 固定值 `"function"` | 硬编码 | 标识工具类型 |
|
|
894
|
+
| `gen_ai.tool.call.arguments` | string | 工具调用参数 | `event.params`,JSON 序列化后截断到 `MAX_ATTR_LENGTH`(3,200,000)字符 | 记录工具输入参数 |
|
|
895
|
+
| `gen_ai.tool.call.result` | string | 工具调用结果 | `event.result`,JSON 序列化后截断 | 记录工具返回结果(仅成功时) |
|
|
896
|
+
| `error.type` | string | 错误类型 | `event.error`(仅工具调用失败时) | 标识工具调用错误 |
|
|
897
|
+
| `tool.duration_ms` | number | 工具执行耗时 | `event.durationMs` 或 `Date.now() - toolStartTime` | 衡量工具执行耗时 |
|
|
898
|
+
|
|
899
|
+
##### 九、Session Span 字段(`session_start` / `session_end`)
|
|
900
|
+
|
|
901
|
+
Session Span 是短生命周期 Span,标记会话的开始和结束。
|
|
902
|
+
|
|
903
|
+
| 字段名 | 类型 | 作用 | 来源 | 用途 |
|
|
904
|
+
|--------|------|------|------|------|
|
|
905
|
+
| `gen_ai.span.kind` | string | 固定值 `"SESSION"` | 硬编码 | 标识为 Session Span |
|
|
906
|
+
| `event.type` | string | `"session_start"` | 仅 `session_start` Span | 区分会话开始事件 |
|
|
907
|
+
| `session.duration_ms` | number | 会话持续时间 | `event.durationMs`(仅 `session_end`) | 衡量会话总时长 |
|
|
908
|
+
| `session.message_count` | number | 会话消息数 | `event.messageCount`(仅 `session_end`) | 统计会话中的消息总数 |
|
|
909
|
+
| `session.total_tokens` | number | 会话总 Token 数 | `TraceContext.lastLlmUsage.total`(仅 `session_end`) | 统计会话的总 Token 消耗 |
|
|
910
|
+
| `gen_ai.output.messages` | string (JSON) | 会话最终输出 | `TraceContext.lastOutput`(仅 `session_end`,且有值时) | 记录会话最后的回复内容 |
|
|
911
|
+
|
|
912
|
+
##### 十、Gateway Span 字段(`gateway_start`)
|
|
913
|
+
|
|
914
|
+
Gateway Span 是短生命周期 Span,标记网关启动事件。每次 `gateway_start` 生成独立的 `traceId`。
|
|
915
|
+
|
|
916
|
+
| 字段名 | 类型 | 作用 | 来源 | 用途 |
|
|
917
|
+
|--------|------|------|------|------|
|
|
918
|
+
| `gen_ai.span.kind` | string | 固定值 `"ENTRY"` | 硬编码 | 标识为入口类型 |
|
|
919
|
+
| `openclaw.version` | string | OpenClaw 版本号 | `api.runtime?.version` | 版本追踪 |
|
|
920
|
+
| `gateway.port` | number | 网关监听端口 | `event.port` | 记录网关启动的端口号 |
|
|
921
|
+
|
|
922
|
+
> **注意**:Gateway Span 不通过 `createSpan()` 创建,因此不包含 `openclaw.session.id`、`openclaw.run.id`、`openclaw.turn.id` 等通用属性。
|
|
923
|
+
|
|
924
|
+
##### 十一、Compaction Span 字段(`after_compaction`)
|
|
925
|
+
|
|
926
|
+
Compaction Span 记录上下文压缩操作。
|
|
927
|
+
|
|
928
|
+
| 字段名 | 类型 | 作用 | 来源 | 用途 |
|
|
929
|
+
|--------|------|------|------|------|
|
|
930
|
+
| `gen_ai.span.kind` | string | 固定值 `"TOOL"` | 硬编码 | 标识为工具类型 |
|
|
931
|
+
| `gen_ai.operation.name` | string | 固定值 `"compaction"` | 硬编码 | 标识为压缩操作 |
|
|
932
|
+
| `openclaw.compaction.messages.after` | number | 压缩后消息数 | `event.messageCount` | 统计压缩后剩余的消息数 |
|
|
933
|
+
| `openclaw.compaction.tokens.after` | number | 压缩后 Token 数 | `event.tokenCount` | 统计压缩后剩余的 Token 数 |
|
|
934
|
+
| `openclaw.compaction.compacted_count` | number | 被压缩的消息数 | `event.compactedCount` | 统计本次压缩了多少条消息 |
|
|
935
|
+
|
|
936
|
+
##### 十二、Subagent Span 字段(`subagent_ended`)
|
|
937
|
+
|
|
938
|
+
Subagent Span 记录子 Agent 的调用。
|
|
939
|
+
|
|
940
|
+
| 字段名 | 类型 | 作用 | 来源 | 用途 |
|
|
941
|
+
|--------|------|------|------|------|
|
|
942
|
+
| `gen_ai.span.kind` | string | 固定值 `"AGENT"` | 硬编码 | 标识为 Agent 类型 |
|
|
943
|
+
| `gen_ai.operation.name` | string | 固定值 `"invoke_subagent"` | 硬编码 | 标识为子 Agent 调用 |
|
|
944
|
+
| `openclaw.subagent.session_key` | string | 子 Agent 会话标识 | `event.sessionKey` 或 `event.childSessionKey` 或 `"unknown-child"` | 标识具体的子 Agent 实例 |
|
|
945
|
+
|
|
946
|
+
##### 十三、特殊状态字段
|
|
947
|
+
|
|
948
|
+
以下字段在特定场景下出现,标记 Span 的异常状态。
|
|
949
|
+
|
|
950
|
+
| 字段名 | 类型 | 出现场景 | 来源 | 用途 |
|
|
951
|
+
|--------|------|----------|------|------|
|
|
952
|
+
| `openclaw.force_closed` | boolean | `dispose()` 或 `sweepStaleOpenSpans()` 强制关闭长生命周期 Span 时 | 固定值 `true` | 标识 Span 非正常关闭 |
|
|
953
|
+
| `openclaw.force_closed.reason` | string | 同上 | `"dispose"`(进程关闭)或 `"stale_sweep"`(超时清理) | 标识强制关闭的原因 |
|
|
954
|
+
| `openclaw.reset` | boolean | `before_reset` hook 触发时 | 固定值 `true`,通过 `patchOpenSpanAttributes()` 追加到 Root Span | 标识会话发生了重置 |
|
|
955
|
+
| `openclaw.reset.reason` | string | 同上 | `event.reason` 或 `"user_request"` | 标识重置原因 |
|
|
956
|
+
| `openclaw.acp_mode` | boolean | ACP 回退关闭 Entry Span 时 | 固定值 `true` | 标识当前请求处于 ACP 模式 |
|
|
957
|
+
| `error` | boolean | 工具调用失败时 | `true` | 标识 Span 存在错误 |
|
|
958
|
+
| `error.type` | string | 工具调用失败时 | `event.error` | 错误类型描述 |
|
|
959
|
+
|
|
960
|
+
##### 十四、字段值限制
|
|
961
|
+
|
|
962
|
+
| 限制项 | 值 | 来源常量 | 说明 |
|
|
963
|
+
|--------|-----|----------|------|
|
|
964
|
+
| 单个属性值最大长度 | 3,200,000 字符 | `MAX_ATTR_LENGTH` | `truncateAttr()` 裸截断,适用于 `gen_ai.input.messages`、`gen_ai.output.messages` 等字符串大字段;`gen_ai.prompt` / `gen_ai.completion` 使用 `truncateJsonAttr()` **结构感知式渐进裁剪**(逐条减半字段 → 丢弃中段消息),尽力保留原内容同时保证输出为合法 JSON |
|
|
965
|
+
| APM 索引内容最大长度 | 4,096 字符 | `MAX_ATTR_CONTENT_LENGTH` | `truncate()` 截断并添加 `... [truncated, total N chars]` 提示 |
|
|
966
|
+
| APM 索引最大 prompt 消息数 | 20 条 | `MAX_INDEXED_PROMPT_MESSAGES` | 超过时保留第 1 条和最后 19 条 |
|
|
967
|
+
| traceId 长度 | 32 位十六进制 | `generateId(32)` | 加密安全随机数(`crypto.randomBytes`) |
|
|
968
|
+
| spanId 长度 | 16 位十六进制 | `generateId(16)` | 加密安全随机数 |
|
|
969
|
+
|
|
970
|
+
#### Trace Context 传播机制
|
|
971
|
+
|
|
972
|
+
插件实现了完整的 Trace Context 传播,确保一次请求的所有 Span 形成完整的调用树:
|
|
973
|
+
|
|
974
|
+
1. **多维上下文管理**:通过 `contextByChannelId`、`contextByRunId`、`contextsByChannelId`、`activeContextByAgentChannel` 四个维度管理上下文
|
|
975
|
+
2. **跨通道链接**:agent 通道(`agent/xxx`)自动链接到最近的用户通道上下文,确保 agent 内部的 Span 与用户请求共享同一个 traceId
|
|
976
|
+
3. **runId 重绑定**:临时 runId(`__temp_run_`前缀)在收到真实 runId 后自动重绑定,更新所有关联的 Span 属性
|
|
977
|
+
4. **并发隔离**:同一通道的多个并发请求通过 `contextsByChannelId` 集合隔离
|
|
978
|
+
5. **上下文轮转**:新消息到达时自动创建新的 trace,避免与旧 trace 混淆
|
|
979
|
+
6. **任务队列**:通过 `traceTaskQueueByTraceId` 保证同一 trace 的操作按顺序执行
|
|
980
|
+
|
|
981
|
+
#### 分段式 LLM Span 导出
|
|
982
|
+
|
|
983
|
+
与传统的 `llm_input` → `llm_output` 一对一模式不同,v2 版本通过 `before_message_write` hook 实现分段式导出:
|
|
984
|
+
|
|
985
|
+
```
|
|
986
|
+
llm_input (开始追踪)
|
|
987
|
+
→ before_message_write (assistant 消息,含 tool_call) → 导出 LLM Span #1
|
|
988
|
+
→ before_tool_call / after_tool_call → 导出 Tool Span
|
|
989
|
+
→ 结束 Step #1,开始 Step #2
|
|
990
|
+
→ before_message_write (assistant 消息,最终回复) → 导出 LLM Span #2
|
|
991
|
+
→ 结束 Step #2
|
|
992
|
+
agent_end (结束所有长生命周期 Span)
|
|
993
|
+
```
|
|
994
|
+
|
|
995
|
+
这种方式的优势:
|
|
996
|
+
- 每次 LLM 响应都有独立的 Span,包含精确的输入/输出消息
|
|
997
|
+
- Tool call 后的下一轮 LLM 输入自动包含 tool result
|
|
998
|
+
- Step 层级清晰展示推理循环
|
|
999
|
+
|
|
1000
|
+
#### 孤儿 Span 清理
|
|
1001
|
+
|
|
1002
|
+
插件内置孤儿 Span 清理机制,每 60 秒扫描一次,超过 5 分钟未结束的 trace 会被自动关闭并标记 `openclaw.sweep=true`。
|
|
1003
|
+
|
|
1004
|
+
---
|
|
1005
|
+
|
|
452
1006
|
### 腾讯云实例元数据注入
|
|
453
1007
|
|
|
454
|
-
|
|
1008
|
+
插件会自动获取当前实例的元数据信息,并注入到 Prometheus 指标(Push 模式)中作为 `external_labels` 自动合并(`cvm_instance_id`、`cvm_instance_name`、`cvm_instance_intra_ip`)。
|
|
455
1009
|
|
|
456
1010
|
#### 获取的元数据字段
|
|
457
1011
|
|
|
458
|
-
| 字段 |
|
|
459
|
-
|
|
460
|
-
| `cvm_instance_id` | `/latest/meta-data/instance-id` | 实例 ID |
|
|
461
|
-
| `cvm_instance_name` |
|
|
462
|
-
| `cvm_instance_intra_ip` |
|
|
463
|
-
| `cvm_instance_internet_ip` | `/latest/meta-data/public-ipv4` | 公网 IPv4 地址 |
|
|
464
|
-
| `cvm_instance_region` | `/latest/meta-data/placement/region` | 所在地域 |
|
|
1012
|
+
| 字段 | 获取方式 | 说明 |
|
|
1013
|
+
|------|---------|------|
|
|
1014
|
+
| `cvm_instance_id` | 腾讯云 Metadata 接口 `/latest/meta-data/instance-id` | 实例 ID |
|
|
1015
|
+
| `cvm_instance_name` | `os.hostname()` 本机主机名 | 实例名称 |
|
|
1016
|
+
| `cvm_instance_intra_ip` | 遍历本地网卡接口(非回环、非虚拟网卡的 IPv4 地址) | 内网 IPv4 地址 |
|
|
465
1017
|
|
|
466
1018
|
#### 刷新策略
|
|
467
1019
|
|
|
468
|
-
-
|
|
469
|
-
-
|
|
470
|
-
-
|
|
1020
|
+
- 启动时优先从持久化文件(`openclaw.json`)的插件配置项(`plugins.entries["clawpro-diagnostics-metrics-cls"].config.instance_metadata.instance_id`)读取已缓存的 `instance-id`,若已有则直接使用,不发起网络请求
|
|
1021
|
+
- 若无持久化值,则立即通过 metadata 接口获取(请求超时 2 秒),成功后写入 `openclaw.json` 的插件配置项持久化,不再启动定时刷新
|
|
1022
|
+
- 首次获取失败的字段会在 30 秒后重试,最多重试 3 次,重试成功后同样持久化并停止重试
|
|
471
1023
|
- 非腾讯云环境下,元数据字段为空字符串,不影响插件正常运行
|
|
472
1024
|
|
|
473
1025
|
---
|
|
@@ -498,7 +1050,10 @@ npx --yes clawpro-diagnostics-metrics-cls-onboard-cli UpdateParameter [选项]
|
|
|
498
1050
|
| `--prometheusEnabled <bool>` | 是否开启 Prometheus 指标上报(`true`/`false`) | ✅ 需要重启网关 |
|
|
499
1051
|
| `--pushIntervalMs <ms>` | Prometheus Remote Write 推送间隔(毫秒) | ✅ 需要重启网关 |
|
|
500
1052
|
| `--externalLabels <labels>` | Prometheus 自定义标签(格式:`key1=value1,key2=value2`) | ✅ 需要重启网关 |
|
|
1053
|
+
| `--traceEnabled <bool>` | 是否启用 Trace 链路追踪功能(`true`/`false`) | ❌ 热加载(约 10 秒内生效) |
|
|
1054
|
+
| `--traceTopicId <id>` | Trace 数据上报使用的 CLS 日志主题 ID | ❌ 热加载(约 10 秒内生效) |
|
|
501
1055
|
|
|
1056
|
+
> **智能重启判断**
|
|
502
1057
|
> **智能重启判断**:CLI 会自动分析本次更新的参数,如果所有参数均支持热加载,则不会重启网关;如果包含需要重启的参数,CLI 会自动重启网关使配置生效。
|
|
503
1058
|
|
|
504
1059
|
### 更新示例
|
|
@@ -526,6 +1081,20 @@ npx clawpro-diagnostics-metrics-cls-onboard-cli UpdateParameter \
|
|
|
526
1081
|
--enableReport false
|
|
527
1082
|
```
|
|
528
1083
|
|
|
1084
|
+
更新 Trace 日志主题 ID(热加载,无需重启):
|
|
1085
|
+
|
|
1086
|
+
```bash
|
|
1087
|
+
npx clawpro-diagnostics-metrics-cls-onboard-cli UpdateParameter \
|
|
1088
|
+
--traceTopicId "new-trace-topic-id"
|
|
1089
|
+
```
|
|
1090
|
+
|
|
1091
|
+
关闭 Trace 链路追踪(热加载,无需重启):
|
|
1092
|
+
|
|
1093
|
+
```bash
|
|
1094
|
+
npx clawpro-diagnostics-metrics-cls-onboard-cli UpdateParameter \
|
|
1095
|
+
--traceEnabled false
|
|
1096
|
+
```
|
|
1097
|
+
|
|
529
1098
|
切换到 CVM 角色临时密钥模式(需要重启网关):
|
|
530
1099
|
|
|
531
1100
|
```bash
|
|
@@ -603,10 +1172,15 @@ npx clawpro-diagnostics-metrics-cls-onboard-cli UpdateParameter \
|
|
|
603
1172
|
| 字段 | 热更新行为 |
|
|
604
1173
|
|------|-----------|
|
|
605
1174
|
| `cls.metricTopicId` | 自动更新 Remote Write URL,切换到新的日志主题 |
|
|
606
|
-
| `cls.endpoint` | 自动更新 Remote Write URL
|
|
1175
|
+
| `cls.endpoint` | 自动更新 Remote Write URL,切换到新的接入点;同时影响 Trace 模块的 CLS 接入点 |
|
|
607
1176
|
| `cls.enableReport` | `false` 时立即停止 Remote Write 指标推送;`true` 时立即恢复推送 |
|
|
608
|
-
| `cls.secretId` | 自动更新静态密钥(static
|
|
609
|
-
| `cls.secretKey` | 自动更新静态密钥(static
|
|
1177
|
+
| `cls.secretId` | 自动更新静态密钥(static 模式),新密钥加密后回写配置文件;同时影响 Trace 模块的凭证 |
|
|
1178
|
+
| `cls.secretKey` | 自动更新静态密钥(static 模式),新密钥加密后回写配置文件;同时影响 Trace 模块的凭证 |
|
|
1179
|
+
| `cls.token` | 自动更新临时 Token(CVM 角色模式),同时影响 Trace 模块的凭证 |
|
|
1180
|
+
| `trace.enabled` | 动态启用/禁用 Trace 链路追踪功能。即使初始配置不完整导致 trace 禁用,后续补全配置后也可通过热更新自动恢复 |
|
|
1181
|
+
| `trace.traceTopicId` | 自动切换 Trace 数据上报的 CLS 日志主题 |
|
|
1182
|
+
| `trace.debug` | 动态开启/关闭 Trace 调试日志,无需重启即可在运行时排查问题 |
|
|
1183
|
+
| `trace.enabledHooks` | 动态调整启用的 hook 列表,可在运行时按需启用/禁用特定 hook,无需重启 |
|
|
610
1184
|
|
|
611
1185
|
> **注意**:`credentialMode`、`roleName` 等凭证模式相关字段,以及 `prometheusEnabled`、`pushIntervalMs`、`externalLabels` 等 Prometheus 配置字段变更后需要重启网关才能生效。可通过 `UpdateParameter` 命令更新,CLI 会自动判断是否需要重启。
|
|
612
1186
|
|
|
@@ -676,6 +1250,7 @@ ENC:<base64(iv + ciphertext)>
|
|
|
676
1250
|
3. **并发安全**:相同 `roleName` 共享同一个 Provider 实例,并发调用时只发起一次元数据请求
|
|
677
1251
|
4. **自愈机制**:凭证刷新失败时暂停 Remote Write 推送,下次刷新成功后自动恢复
|
|
678
1252
|
5. **Prometheus Remote Write 鉴权**:使用 `secretId` 作为 username,`secretKey#token` 作为 password 的 Basic Auth 方式进行鉴权
|
|
1253
|
+
6. **共享凭证管理**:trace 和指标(Prometheus Remote Write)服务通过 `SharedCredentialManager` 共用同一份临时密钥,避免各自独立获取导致的重复元数据请求和凭证状态不一致。管理器统一负责定时刷新和事件通知,两个服务通过订阅机制自动同步最新凭证
|
|
679
1254
|
|
|
680
1255
|
### 前置条件
|
|
681
1256
|
|
|
@@ -1086,6 +1661,10 @@ npx clawpro-diagnostics-metrics-cls-onboard-cli install --credentialMode cvmRole
|
|
|
1086
1661
|
"secretKey": "ENC:YW5vdGhlciBiYXNlNjQgZXhhbXBsZQ==",
|
|
1087
1662
|
"endpoint": "ap-guangzhou.cls.tencentcs.com",
|
|
1088
1663
|
"enableReport": true
|
|
1664
|
+
},
|
|
1665
|
+
"trace": {
|
|
1666
|
+
"enabled": true,
|
|
1667
|
+
"traceTopicId": "zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz"
|
|
1089
1668
|
}
|
|
1090
1669
|
}
|
|
1091
1670
|
}
|
|
@@ -1095,6 +1674,8 @@ npx clawpro-diagnostics-metrics-cls-onboard-cli install --credentialMode cvmRole
|
|
|
1095
1674
|
```
|
|
1096
1675
|
|
|
1097
1676
|
> **注意**:上述 `secretId` 和 `secretKey` 的值以 `ENC:` 前缀标识,表示已加密存储。使用 CLI 安装时会自动加密写入,手动配置时也可直接写入明文(插件启动时会自动加密后回写)。详见 [密钥加密存储](#密钥加密存储) 章节。
|
|
1677
|
+
>
|
|
1678
|
+
> **Trace 配置说明**:`trace` 配置只需指定 `traceTopicId`,凭证(`secretId` / `secretKey` / `endpoint`)自动复用 `cls` 配置中的值。
|
|
1098
1679
|
|
|
1099
1680
|
### CVM 角色临时密钥模式
|
|
1100
1681
|
|
|
@@ -1135,6 +1716,10 @@ npx clawpro-diagnostics-metrics-cls-onboard-cli install --credentialMode cvmRole
|
|
|
1135
1716
|
"metricTopicId": "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy",
|
|
1136
1717
|
"endpoint": "ap-guangzhou.cls.tencentcs.com",
|
|
1137
1718
|
"enableReport": true
|
|
1719
|
+
},
|
|
1720
|
+
"trace": {
|
|
1721
|
+
"enabled": true,
|
|
1722
|
+
"traceTopicId": "zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz"
|
|
1138
1723
|
}
|
|
1139
1724
|
}
|
|
1140
1725
|
}
|
|
@@ -1144,3 +1729,91 @@ npx clawpro-diagnostics-metrics-cls-onboard-cli install --credentialMode cvmRole
|
|
|
1144
1729
|
```
|
|
1145
1730
|
|
|
1146
1731
|
> **注意**:CVM 角色模式下无需配置 `secretId` / `secretKey`,插件运行时通过 CVM 元数据接口自动获取临时密钥。Prometheus Remote Write 的 `headers` 中也不包含 `Authorization`,由插件运行时动态鉴权。详见 [CVM 角色临时密钥](#cvm-角色临时密钥) 章节。
|
|
1732
|
+
>
|
|
1733
|
+
> **Trace 配置说明**:`trace` 配置只需指定 `traceTopicId`,凭证自动复用 `cls` 配置。CVM 角色模式下,trace 同样通过 `cls` 中的凭证进行鉴权。
|
|
1734
|
+
|
|
1735
|
+
---
|
|
1736
|
+
|
|
1737
|
+
## 开发与构建
|
|
1738
|
+
|
|
1739
|
+
### 本地开发
|
|
1740
|
+
|
|
1741
|
+
```bash
|
|
1742
|
+
# 安装依赖
|
|
1743
|
+
npm install
|
|
1744
|
+
```
|
|
1745
|
+
|
|
1746
|
+
### 构建
|
|
1747
|
+
|
|
1748
|
+
```bash
|
|
1749
|
+
# 编译 TypeScript
|
|
1750
|
+
npm run build
|
|
1751
|
+
|
|
1752
|
+
# 监听模式编译
|
|
1753
|
+
npm run dev
|
|
1754
|
+
|
|
1755
|
+
# 清理构建产物
|
|
1756
|
+
npm run clean
|
|
1757
|
+
```
|
|
1758
|
+
|
|
1759
|
+
### 项目结构
|
|
1760
|
+
|
|
1761
|
+
```
|
|
1762
|
+
diagnostics-metrics-cls/
|
|
1763
|
+
├── index.ts # 插件入口,注册 service 和 hook
|
|
1764
|
+
├── openclaw.plugin.json # 插件元数据和配置 schema
|
|
1765
|
+
├── package.json # NPM 包配置
|
|
1766
|
+
├── tsconfig.json # TypeScript 编译配置
|
|
1767
|
+
└── src/
|
|
1768
|
+
├── cls-service.ts # Prometheus 指标服务(Pull + Remote Write)
|
|
1769
|
+
├── trace-service.ts # 全链路 Trace 追踪服务(主入口)
|
|
1770
|
+
├── trace-sender.ts # CLS Span 发送器
|
|
1771
|
+
├── trace-config.ts # Trace 配置解析与热更新读取
|
|
1772
|
+
├── trace-types.ts # Trace 类型定义
|
|
1773
|
+
├── trace-constants.ts # Trace 常量定义
|
|
1774
|
+
├── trace-utils.ts # Trace 工具函数
|
|
1775
|
+
├── remote-write.ts # Prometheus Remote Write 客户端
|
|
1776
|
+
├── protobuf.ts # Protobuf 编码器
|
|
1777
|
+
├── credential.ts # CVM 角色临时密钥获取
|
|
1778
|
+
├── shared-credential.ts # 共享凭证管理器
|
|
1779
|
+
├── shared-constants.ts # 共享常量
|
|
1780
|
+
├── instance-metadata.ts # 腾讯云实例元数据获取
|
|
1781
|
+
└── crypto-utils.ts # 加解密工具
|
|
1782
|
+
```
|
|
1783
|
+
|
|
1784
|
+
### License
|
|
1785
|
+
|
|
1786
|
+
MIT
|
|
1787
|
+
|
|
1788
|
+
---
|
|
1789
|
+
|
|
1790
|
+
## 缺陷修复记录
|
|
1791
|
+
|
|
1792
|
+
### v2.0.x 修复
|
|
1793
|
+
|
|
1794
|
+
#### `trace-sender.ts`
|
|
1795
|
+
|
|
1796
|
+
| # | 缺陷描述 | 修复方案 |
|
|
1797
|
+
|---|----------|----------|
|
|
1798
|
+
| 1 | `sweepStaleOpenSpans()` 方法内部 `sendRecord` 调用块的代码缩进与外层不一致,影响可读性 | 统一缩进为 2 个空格,与文件其余代码风格保持一致 |
|
|
1799
|
+
| 2 | `dispose()` 方法遍历 `openSpans` 时使用了 `_spanId` 作为循环变量名,但实际未使用该变量(仅使用 `span`),产生无意义的命名 | 将循环变量改为 `_spanId`(已有下划线前缀表示有意忽略)或直接使用 `[, span]` 解构,消除 lint 警告 |
|
|
1800
|
+
| 3 | `endSpanById()` 中使用 `endTime \|\| Date.now()` 判断结束时间,当 `endTime` 传入 `0`(Unix 纪元)时会被错误替换为当前时间 | 改为 `endTime != null ? endTime : Date.now()`,正确区分"未传值"与"传入 0"两种情况 |
|
|
1801
|
+
|
|
1802
|
+
#### `index.ts`
|
|
1803
|
+
|
|
1804
|
+
| # | 缺陷描述 | 修复方案 |
|
|
1805
|
+
|---|----------|----------|
|
|
1806
|
+
| 4 | 热重载时 `cachedTracePlugin` 被复用,但其内部持有的 `sharedCredentialManager` 引用不会随热重载更新。若凭证模式切换或管理器实例重建,trace 服务将持有旧实例,无法感知新凭证,导致凭证订阅失效 | 在 `trace-service.ts` 中将 `sharedCredentialManager` 改为 `let`,并新增 `updateSharedCredentialManager()` 方法;在 `index.ts` 热重载复用 `cachedTracePlugin` 时调用该方法同步更新引用,并重新订阅新管理器的凭证变更事件 |
|
|
1807
|
+
|
|
1808
|
+
#### `shared-credential.ts`
|
|
1809
|
+
|
|
1810
|
+
| # | 缺陷描述 | 修复方案 |
|
|
1811
|
+
|---|----------|----------|
|
|
1812
|
+
| 5 | `stop()` 方法中直接将 `refreshingPromise = null`,丢弃了正在进行的刷新 Promise。这破坏了 `refreshCredentialInternal()` 的并发保护语义:下次调用时会误判为"无进行中的刷新",重复发起网络请求,与已在进行中的请求并发执行 | 移除 `stop()` 中对 `refreshingPromise` 的强制清零。正在进行的 `doRefresh()` 会在完成后通过 `finally` 块自行将其清零,且完成后会检查 `this.stopped` 为 `true` 而跳过监听器通知,行为安全 |
|
|
1813
|
+
|
|
1814
|
+
#### `instance-metadata.ts`
|
|
1815
|
+
|
|
1816
|
+
| # | 缺陷描述 | 修复方案 |
|
|
1817
|
+
|---|----------|----------|
|
|
1818
|
+
| 6 | `startInstanceMetadataRefresh()` 中 `initialized = true` 赋值在 `stopped = false` 之前执行。若此时有并发的 `scheduleRetry` 回调已进入执行队列,它会在 `initialized = true` 之后、`stopped = false` 之前检查 `stopped`,此时 `stopped` 仍为上次 `stop()` 留下的 `true`,导致回调错误地跳过写入操作 | 交换赋值顺序,先执行 `stopped = false` 再执行 `initialized = true`,消除并发窗口期内的状态不一致 |
|
|
1819
|
+
| 7 | `writeInstanceIdToConfig()` 中 `logger?.warn(...)` 调用行末尾存在多余的尾随空格 | 移除尾随空格 |
|