@blueking/open-telemetry 0.0.33 → 0.0.35-beta.1

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.
Files changed (54) hide show
  1. package/README.md +67 -57
  2. package/dist/bk-rum.global.js +2 -2
  3. package/dist/bk-rum.global.js.map +1 -1
  4. package/dist/core/action-activity.d.ts +5 -1
  5. package/dist/core/action-target.d.ts +19 -0
  6. package/dist/core/config.d.ts +15 -5
  7. package/dist/core/event-time.d.ts +7 -0
  8. package/dist/core/performance-timing.d.ts +13 -0
  9. package/dist/core/plugin.d.ts +37 -2
  10. package/dist/core/processor.d.ts +24 -2
  11. package/dist/core/request-timing.d.ts +3 -2
  12. package/dist/core/runtime-context.d.ts +62 -2
  13. package/dist/core/sdk.d.ts +23 -1
  14. package/dist/core/session-transition.d.ts +19 -0
  15. package/dist/core/trace-context.d.ts +6 -7
  16. package/dist/core/view-location.d.ts +10 -0
  17. package/dist/index.d.ts +2 -0
  18. package/dist/index.js +2955 -1483
  19. package/dist/index.js.map +1 -1
  20. package/dist/integrations/vue-router.d.ts +25 -0
  21. package/dist/plugins/action.d.ts +1 -1
  22. package/dist/plugins/blank-screen-detection.d.ts +11 -0
  23. package/dist/plugins/blank-screen.d.ts +10 -18
  24. package/dist/plugins/common.d.ts +4 -2
  25. package/dist/plugins/document-vitals.d.ts +25 -0
  26. package/dist/plugins/page-view.d.ts +1 -1
  27. package/dist/plugins/resource.d.ts +14 -1
  28. package/dist/plugins/session-state.d.ts +14 -2
  29. package/dist/plugins/session-store.d.ts +12 -0
  30. package/dist/plugins/session.d.ts +1 -0
  31. package/dist/plugins/web-vitals-metrics.d.ts +80 -0
  32. package/dist/plugins/web-vitals.d.ts +2 -59
  33. package/dist/schema/attributes.d.ts +63 -0
  34. package/dist/schema/span.d.ts +5 -2
  35. package/package.json +9 -4
  36. package/skills/bk-rum-otele/references/actions.md +54 -37
  37. package/skills/bk-rum-otele/references/api.md +72 -51
  38. package/skills/bk-rum-otele/references/architecture.md +16 -16
  39. package/skills/bk-rum-otele/references/blank-screen.md +72 -142
  40. package/skills/bk-rum-otele/references/capabilities.md +66 -60
  41. package/skills/bk-rum-otele/references/configuration.md +101 -98
  42. package/skills/bk-rum-otele/references/data-model.md +22 -17
  43. package/skills/bk-rum-otele/references/errors-security.md +13 -13
  44. package/skills/bk-rum-otele/references/extensions.md +5 -3
  45. package/skills/bk-rum-otele/references/framework-integration.md +7 -24
  46. package/skills/bk-rum-otele/references/network.md +62 -49
  47. package/skills/bk-rum-otele/references/performance.md +62 -30
  48. package/skills/bk-rum-otele/references/privacy.md +5 -5
  49. package/skills/bk-rum-otele/references/production.md +9 -13
  50. package/skills/bk-rum-otele/references/session-context.md +67 -38
  51. package/skills/bk-rum-otele/references/span-protocol.md +771 -315
  52. package/skills/bk-rum-otele/references/telemetry-reference.md +288 -213
  53. package/skills/bk-rum-otele/references/troubleshooting.md +2 -2
  54. package/skills/bk-rum-otele/references/views-routing.md +103 -27
@@ -18,7 +18,7 @@ SDK 不采集任何请求 Body、响应 Body、WebSocket 消息内容、消息
18
18
 
19
19
  `tracking.request=false` 时完全不 Patch Fetch/XHR。开启时,同一层实现负责:
20
20
 
21
- 1. 把 URL 转成绝对 URL。
21
+ 1. 按 `document.baseURI` 把 URL 转成绝对 URL,保留当前页面目录和 `<base href>` 的语义。
22
22
  2. 排除 SDK 自身上报和 `excludedUrls`。
23
23
  3. 按白名单探测既有 Trace Header,规划本次请求的 trace context。
24
24
  4. 创建 `span_type=resource` 的 CLIENT Span,并把关联后端 Trace 的 Span Link 一并写入。
@@ -30,6 +30,8 @@ SDK 不采集任何请求 Body、响应 Body、WebSocket 消息内容、消息
30
30
 
31
31
  Patch 在插件 shutdown 时仅在全局方法仍是 SDK 安装版本的情况下恢复,避免覆盖 SDK 之后由业务安装的其他 Patch。
32
32
 
33
+ 请求开始阶段的采集 Hook 异常会丢弃本条遥测,原生请求仍执行一次。SDK 不重试业务请求。XHR 每次 `send` 独立保存响应元数据,业务在完成回调中复用实例不会改写上一请求的结果。
34
+
33
35
  ## 请求 Span
34
36
 
35
37
  Fetch 和 XHR 都生成名称为 `<http.request.method> <url.template>` 的 Resource Span:
@@ -49,7 +51,7 @@ Fetch 和 XHR 都生成名称为 `<http.request.method> <url.template>` 的 Reso
49
51
 
50
52
  这套折叠规则与 View 的 `view.url_template` 共用,但入口独立:页面路由和 API 路径的模板规则通常不同,所以 `tracking.view.getUrlTemplate` 与 `tracking.request.getUrlTemplate` 互不影响。
51
53
 
52
- Span 开始时间使用请求开始时间,结束时间优先使用匹配到的 `PerformanceResourceTiming.responseEnd`;没有匹配时使用 Fetch resolve/reject 或 XHR `load/abort/timeout/error` 的完成时间。`http.response.status_code` 只表示真实 HTTP Response,不会上报 XHR 的本地占位值 `0`。
54
+ Span 开始时间使用捕获的 epoch 起点,结束时间用该起点加 Performance 相对耗时;有 RT 时优先采用其结束点,不再与固定 `timeOrigin` 混算。没有匹配时使用已知的 Fetch resolve/reject 或 XHR 终止时刻;shutdown 时仍在飞行的请求使用停止时刻。回退写入 `resource.timing.incomplete=true`;Fetch resolve 只代表响应头可用,这种回退耗时不能作为完整下载耗时。`http.response.status_code` 不上报 XHR 的本地占位值 `0`。
53
55
 
54
56
  ## Outcome 与 Status
55
57
 
@@ -61,7 +63,12 @@ Span 开始时间使用请求开始时间,结束时间优先使用匹配到的
61
63
  | Fetch TimeoutError / XHR `timeout` | `timeout` | ERROR,Fetch 记录 exception |
62
64
  | Fetch 其他 reject / XHR `error` | `error` | ERROR,Fetch 记录 exception |
63
65
  | XHR `load` 但无法取得有效状态 | `error` | ERROR |
64
- | 其余 | `success` | UNSET |
66
+ | 无可用 HTTP 状态且无明确失败证据 | `unknown` | UNSET |
67
+ | 有效的其他 HTTP 响应 | `success` | UNSET |
68
+
69
+ 静态资源与 Fetch opaque 响应无法取得有效 HTTP 状态时同样为 `unknown`,不因 `responseStatus=0` 推断成功或失败。
70
+
71
+ Fetch 使用 `init.signal` 覆盖后的有效 signal;等待 RT 期间的显式取消可识别为 abort/timeout。`success` 只描述已观测的 HTTP 结果,不保证正文完整传输或业务解析成功。SDK 不读取正文,因此响应头后断流、JSON 解析失败等需要业务侧补充诊断;RT 到达本身不能证明正文消费成功。
65
72
 
66
73
  当前 SDK **不写** `outcome.reason`。HTTP 失败请用 `http.response.status_code` 细分。SDK 不根据业务响应 JSON 中的 code 判断成功或失败;需要业务口径时使用 `request.beforeSend` 增加属性,或上报自定义事件。
67
74
 
@@ -101,7 +108,7 @@ tracking: {
101
108
  }
102
109
  ```
103
110
 
104
- 白名单应使用 URL-aware 函数或有边界的正则,不要用过宽字符串。
111
+ 字符串白名单必须是绝对 HTTP(S) URL,按 origin 和路径段边界匹配。例如 `https://api.example.com/v1` 匹配 `/v1`、`/v1/orders`,不匹配 `/v10` 或其他域名 query 中的该地址。字符串配置不接受用户名、密码、query、hash;复杂条件使用函数或正则。`excludedUrls` 保留子串匹配语义。
105
112
 
106
113
  ### 每个请求一条独立后端 Trace
107
114
 
@@ -115,7 +122,7 @@ traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
115
122
 
116
123
  这样后端按 trace-id 检索时,一条 Trace 只对应一个 API 请求,也不会把前端 Resource Span 混进后端链路。前后端的关联由 **Span Link** 承担,后端可按 `link.trace_id` 反查对应的 RUM Span。
117
124
 
118
- 每个命中白名单的请求恒定产出**一条** Link,结构固定为 trace context 加上记录来源的 `attributes`:
125
+ SDK 注入或能解析为合法 OTel id 的既有上下文会产出一条 Link:
119
126
 
120
127
  ```json
121
128
  {
@@ -125,17 +132,16 @@ traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
125
132
  "span_id": "00f067aa0ba902b7",
126
133
  "attributes": {
127
134
  "format": "traceparent",
128
- "injected": true,
129
- "traceparent": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
135
+ "injected": true
130
136
  }
131
137
  }
132
138
  ]
133
139
  }
134
140
  ```
135
141
 
136
- `attributes.format` 是命中的传播格式,`attributes.injected` 标记这条 trace context 是否由 SDK 生成——业务自带合法 `traceparent` 时 `format` 与 SDK 注入同值,只能靠 `injected` 区分。其余键是该格式的**原始 Header 名与原始取值**(不做补零或进制转换),例如 Datadog 会带上 `x-datadog-trace-id`、`x-datadog-parent-id`、`x-datadog-sampling-priority` 等。
142
+ `attributes.format` 是命中的传播格式,`attributes.injected` 标记该上下文是否由 SDK 生成。自动 Link 仅保存这两个属性及规范化 id,不上报原始厂商 Header。
137
143
 
138
- `tracestate` 不做记录:它在本链路上没有消费者(SDK 从不注入 `tracestate`,后端只解析 `traceparent`),而 W3C 允许 32 个成员、每成员 256 字符,最坏情况下会给每个被追踪请求平白增加数 KB 负载。这些 Link Attributes 也不经过 `privacy.redactAttributes` 脱敏。
144
+ `tracestate` 不注入、不记录。Link Attributes 与 Span、Event 属性统一经过 `privacy.redactAttributes`,全局 `tracking.beforeSend` 可通过 `links`、`events` 检查或移除对应导出副本。
139
145
 
140
146
  请求 Patch 只在当前 Session 已采样时创建 Span 并注入,因此发出的 flags 恒为 `01`。注入的 span-id 在前端没有对应的已上报 Span,后端 Trace 的根 Span 会以它作为 `parent_span_id`。
141
147
 
@@ -143,21 +149,22 @@ traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
143
149
 
144
150
  注入前 SDK 会先读取本次请求最终会发出的 Header,按下列优先级探测业务是否已经自行传播链路:
145
151
 
146
- | 优先级 | `format` | Header | 能否解析出合法 OTel id |
147
- | --- | --- | --- | --- |
148
- | 1 | `traceparent` | `traceparent` | 能 |
149
- | 2 | `sentry-trace` | `sentry-trace` | 能 |
150
- | 3 | `b3` | `b3`(单头) | 能,64 位 trace-id 左侧补零到 128 位 |
151
- | 4 | `b3-multi` | `x-b3-traceid` / `x-b3-spanid` / `x-b3-parentspanid` / `x-b3-sampled` / `x-b3-flags` | 同上 |
152
- | 5 | `datadog` | `x-datadog-trace-id` / `x-datadog-parent-id` / `x-datadog-sampling-priority` / `x-datadog-origin` / `x-datadog-tags` | 能,十进制换算为 hex,128 位时从 `x-datadog-tags` 的 `_dd.p.tid` 拼接高位 |
153
- | 6 | `sw8` | `sw8` | **不能**,SkyWalking 的 trace-id 不是 16 字节 hex |
152
+ | 优先级 | `format` | Header | 能否解析出合法 OTel id |
153
+ | ------ | -------------- | -------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
154
+ | 1 | `traceparent` | `traceparent` | 能 |
155
+ | 2 | `sentry-trace` | `sentry-trace` | 能 |
156
+ | 3 | `b3` | `b3`(单头) | 能,64 位 trace-id 左侧补零到 128 位 |
157
+ | 4 | `b3-multi` | `x-b3-traceid` / `x-b3-spanid` / `x-b3-parentspanid` / `x-b3-sampled` / `x-b3-flags` | 同上 |
158
+ | 5 | `datadog` | `x-datadog-trace-id` / `x-datadog-parent-id` / `x-datadog-sampling-priority` / `x-datadog-origin` / `x-datadog-tags` | 能,十进制换算为 hex,128 位时从 `x-datadog-tags` 的 `_dd.p.tid` 拼接高位 |
159
+ | 6 | `sw8` | `sw8` | **不能**,SkyWalking 的 trace-id 不是 16 字节 hex |
154
160
 
155
- 一旦探测到上表任一 Header,SDK **完全不注入**,Link Attributes 里 `injected` 为 `false`,并写入命中格式的原始 Header。只记录命中格式自己的 Header——若 `traceparent` 与 `b3` 同时存在,按优先级只记录前者。
161
+ 探测到有效 W3C 上下文或其他厂商 Header 时,SDK 保留业务传播方式;能够解析合法 id 时写入 `injected=false` 的 Link。多种格式共存时按上述优先级处理。
156
162
 
157
163
  几点需要注意:
158
164
 
159
165
  - `traceparent` 与 SDK 注入的是同一个 Header,因此**格式非法时视作没有既有链路并覆盖注入**——这种值后端同样无法接续。其余格式只要 Header 存在就一律尊重。
160
- - 解析不出合法 id 时(`sw8`、或 `b3: 0` 这类只有采样标记的值),SDK 会补一对随机 id 让 Link 结构保持统一,但**不会注入**。这对占位 id 在后端不存在对应 Trace,只有 `attributes` 里的原始 Header 有排查价值。
166
+ - W3C v00 必须是固定长度的小写十六进制格式,不接受扩展后缀或大写 id。
167
+ - 解析不出合法 id 时(`sw8`、或 `b3: 0` 这类只有采样标记的值),SDK 不生成 Link,也不额外注入,不创建虚假的可跳转 id。
161
168
  - 后端只解析 W3C `traceparent`。如果业务发的是 `b3` / `sw8` / Datadog 这类格式,SDK 不注入意味着后端不会生成 Trace,Link 中的 trace-id 在后端查不到。
162
169
  - Fetch 读取的是 `init.headers`,缺省时回退到输入 `Request` 的 Headers;XHR 因为浏览器不提供读取请求头的 API,SDK 会额外 Patch `setRequestHeader()` 做快照,这层 Patch 仅在配置了 `allowedTracingUrls` 时安装,`open()` 时快照同步失效。
163
170
 
@@ -202,44 +209,50 @@ type BkOTRequestOutcome =
202
209
  第二个参数保留原始浏览器上下文:
203
210
 
204
211
  - Fetch:`request`、可选 `response`、可选 `error`。
205
- - XHR:原始 `XMLHttpRequest` 实例。
212
+ - XHR:原始 `XMLHttpRequest` 实例,以及本次 send 的 `responseSnapshot`(status/statusText/responseURL/responseType)。原始实例可能已被业务复用,需要历史响应元数据时读取快照;快照不保存正文。
206
213
 
207
- Hook 可原地修改 `event.attributes`;返回 `false` 时给 Span 写入内部丢弃标记,Span 正常结束但不会进入 Exporter。Hook 抛错时默认继续发送,`debug=true` 输出 warning。
214
+ Hook 在 Resource Timing 合并或回退完成后调用,可以读取 `event.attributes` 中的缓存、大小及阶段字段;Timing 缺失时通过 `resource.timing.incomplete=true` 识别。Hook 可原地修改、删除 `event.attributes`;删除也会移除请求开始时已写入的对应字段。返回 `false` 时 Span 正常结束但不会进入 Exporter。请求级 Hook 抛错时默认继续后续脱敏,`debug=true` 输出 warning。
208
215
 
209
216
  之后仍会执行 `privacy.redactAttributes` 和全局 `tracking.beforeSend`。
210
217
 
211
218
  ## Resource Timing 匹配
212
219
 
213
- Fetch resolve/reject 和 XHR 终止事件往往早于 Resource Timing Observer 回调。SDK 在请求完成后最多等待 200ms:
220
+ 成功 Fetch 的 Promise resolve 只代表响应头可用,SDK 继续等待浏览器在正文完成后产生的 Resource Timing,不主动克隆或读取业务响应体。XHR 终止事件和 Fetch reject 已有结束证据,只额外等待最多 200ms 补齐 Observer 通知。
214
221
 
215
- - 以去掉 fragment 后的绝对 URL 匹配;
216
- - 同 URL 并发请求选择开始时间最接近的 Entry;
217
- - 允许 Performance Entry 比 JavaScript 请求开始早最多 50ms;
218
- - 待匹配队列最多 100 条,超出时释放最旧请求。
222
+ - 使用去掉 fragment 后的绝对 URL 和 `initiatorType` 匹配。
223
+ - 以 `performance.now()` 记录原生 `fetch` / `send` 的调用窗口,只接收该窗口内开始的 Entry;不将 Date 毫秒时间用于匹配。
224
+ - 同 URL 请求按开始时间消歧,时钟精度不足而无法区分时放弃补全,不按入队顺序猜测。
225
+ - 已结束记录暂时保留以拦截迟到 Entry,防止它被其他请求认领;记录总量最多 1000 条,超出时淘汰最旧记录;已取得响应或终止结果的请求立即回退,尚未取得结果的请求在结果到达后回退。
226
+ - 成功 Fetch 从响应头可用起最多等待 60 秒。浏览器不支持 Resource Timing、等待超限、容量释放或 SDK shutdown 时使用有界回退,并标记 `resource.timing.incomplete=true`。
219
227
 
220
- 匹配成功后,请求 Span 使用 Resource Timing 的结束时间,并增加 DNS、连接、TLS、TTFB、下载、Body Size 等字段。匹配失败不影响基础请求 Span。
228
+ 匹配成功后,请求 Span 使用 Resource Timing 的结束时间,并增加 DNS、连接、TLS、TTFB、下载、大小和缓存字段。页面活动在 Resource 完成或回退时结束,Session/View/Action 归属仍按请求起点固定。回退保留基础请求信息,不伪造下载完成时间或缺失的 Timing 字段。
221
229
 
222
230
  ## 静态资源
223
231
 
224
- `tracking.resource=true` 时,Resource PerformanceObserver 为非 Fetch/XHR Entry 创建独立 Resource CLIENT Span。Span 名使用 `GET <url.template>`。Fetch 和 `xmlhttprequest` Entry 只回填已有请求 Span,不重复上报。
232
+ `tracking.resource=true` 时,Resource PerformanceObserver 为非 Fetch/XHR Entry 创建独立 Resource CLIENT Span。Timing-only 资源不猜测 HTTP method;Beacon 可明确标记 POST。缺少 method 时 Span 名为 `resource.<type>`。资源按 Performance 相对区间关联 Session/View/Action。
233
+
234
+ Fetch/XHR Entry 优先回填已有请求 Span。仅首次即允许采集时,SDK 安装前的 buffered API 可补报 Timing-only Resource;仍遵守请求开关和 URL 排除。配置请求级 `beforeSend` 时关闭这种补报,因为历史 RT 无法提供真实 Request/XHR 上下文,也不能绕过过滤规则。运行期匹配失败不会再次生成独立资源。
235
+
236
+ 重新授权仅采集新授权区间,旧条目和未授权期间开始的请求不重放。`flush`/`shutdown` 先同步读取 Observer 队列,再执行聚合检查点和请求结算。单条 Hook 异常不会阻断其他资源的处理。
225
237
 
226
- 常见资源类型来自 `initiatorType`,例如 `script`、`link`、`img`、`css`、`beacon`、`other`。
238
+ | 字段 | 说明 |
239
+ | --------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
240
+ | `url.full` | 绝对 URL,经 `redactUrl` 脱敏 |
241
+ | `url.template` | 脱敏后的低基数 pathname;默认折叠数字 ID、UUID 和长十六进制段 |
242
+ | `resource.type` | 归一化类型:document/xhr/fetch/beacon/css/js/image/font/media/other |
243
+ | `resource.initiator_type` | 浏览器原始 initiatorType,例如 script/link/img/xmlhttprequest |
244
+ | `resource.size` | `decodedBodySize`(解压后 Body Size) |
245
+ | `resource.encoded_body_size` | 编码后 Body Size |
246
+ | `resource.decoded_body_size` | 解码后 Body Size |
247
+ | `resource.transfer_size` | `transferSize`(规范化传输大小;本地缓存为 `0`,协商缓存为 `300`,普通网络响应为 encoded size + `300`,不是精确流量) |
248
+ | `resource.protocol` | `nextHopProtocol`,例如 `h2` |
249
+ | `resource.cache.hit` | 确认 HTTP 缓存命中时写 `true`;先排除有 Service Worker 或其静态路由参与证据的条目,再判断交付类型及兜底条件 |
250
+ | `resource.delivery_type` | 浏览器提供的 delivery type;空字符串归一为 `other`,不支持时省略 |
251
+ | `resource.render_blocking_status` | blocking / non-blocking,浏览器支持时 |
227
252
 
228
- | 字段 | 说明 |
229
- | --------------------------------- | ---------------------------------------------------------- |
230
- | `url.full` | 绝对 URL,经 `redactUrl` 脱敏 |
231
- | `url.template` | 脱敏后的低基数 pathname;默认折叠数字 ID、UUID 和长十六进制段 |
232
- | `resource.type` | initiator type |
233
- | `resource.size` | `decodedBodySize`(解压后 Body Size) |
234
- | `resource.encoded_body_size` | 编码后 Body Size |
235
- | `resource.decoded_body_size` | 解码后 Body Size |
236
- | `resource.transfer_size` | `transferSize`(网络传输字节数,缓存命中可能为 `0`) |
237
- | `resource.protocol` | `nextHopProtocol`,例如 `h2` |
238
- | `resource.cache.hit` | deliveryType=cache,或 transferSize=0 且 decodedBodySize>0 |
239
- | `resource.delivery_type` | 浏览器提供的 delivery type |
240
- | `resource.render_blocking_status` | blocking / non-blocking,浏览器支持时 |
253
+ 当 `transferSize`、`encodedBodySize`、`decodedBodySize` 均为 `0`,同时 `requestStart`、`responseStart` 为 `0` 且 `nextHopProtocol` 为空时,SDK 将其视为浏览器未暴露 Resource Timing 详情,不上报大小和协议字段,避免把“未知”误报为真实的 `0`。
241
254
 
242
- 当三个 Size 均为 `0`,同时 `requestStart`、`responseStart` 和 `nextHopProtocol` 也缺失时,SDK 将其视为浏览器未暴露 Resource Timing 详情,不上报 Size 和协议字段,避免把“未知”误报为真实的 `0`。
255
+ `resource.cache.hit` 缺失表示无法确认 HTTP 缓存命中,不能按 `false` 统计。`delivery_type=other` 只表示浏览器返回空交付类型,也不代表明确未命中。原始 SW 路由来源统一映射到 `resource.timing.worker_matched_router_source` / `worker_final_router_source`,同时识别规范的 `*RouterSource` 名称和浏览器的 `*SourceType` 名称。
243
256
 
244
257
  ## 网络阶段字段
245
258
 
@@ -274,12 +287,12 @@ Timing-Allow-Origin: https://your-web.example.com
274
287
 
275
288
  `tracking.websocket=true` 时,SDK 使用 Proxy 包装全局 `WebSocket` 构造器,并保持静态常量、`instanceof` 和子类构造行为。每条连接生成 `websocket.connection.id`,随后发送四类瞬时 CLIENT Span:
276
289
 
277
- | phase | 时机 | 额外字段 |
278
- | --------- | --------------------- | --------------------------------------------- |
279
- | `connect` | 构造 WebSocket 后立即 | 基础目标字段 |
280
- | `open` | `open` Event | 无 |
290
+ | phase | 时机 | 额外字段 |
291
+ | --------- | --------------------- | ----------------------------------------------- |
292
+ | `connect` | 构造 WebSocket 后立即 | 基础目标字段 |
293
+ | `open` | `open` Event | 无 |
281
294
  | `error` | `error` Event | `websocket.error.phase`:`connect` 或 `runtime` |
282
- | `close` | `close` Event | code、reason、was_clean |
295
+ | `close` | `close` Event | code、reason、was_clean |
283
296
 
284
297
  Span 名按 `websocket.<websocket.phase>` 生成,不使用长连接 Span。基础字段包括:
285
298
 
@@ -303,7 +316,7 @@ Span 名按 `websocket.<websocket.phase>` 生成,不使用长连接 Span。基
303
316
 
304
317
  ### 有请求 Span 但没有网络阶段
305
318
 
306
- 可能原因:浏览器不支持 Resource Timing、200ms 内没有匹配到 Entry、同 URL 高并发匹配失败、跨域缺少 TAO,或浏览器返回乱序时间点。
319
+ 先检查 `resource.timing.incomplete`:为 `true` 时表示没有可用的匹配 Entry,例如浏览器不支持、同 URL 消歧失败、等待或容量达到上限、SDK 已停止。字段未标记但详细阶段缺失时,再检查跨域 TAO 限制或浏览器返回的时间点顺序。
307
320
 
308
321
  ### 后端没有收到 traceparent
309
322
 
@@ -8,15 +8,15 @@
8
8
 
9
9
  `span_type=vital` 覆盖以下指标,Span 名使用大写指标名:
10
10
 
11
- | 指标 | `vital.metric` | 单位 | 当前采集方式 |
12
- | --- | --- | --- | --- |
13
- | Cumulative Layout Shift | `cls` | 无量纲 | 每个 SDK View 独立计算 session window |
14
- | First Contentful Paint | `fcp` | ms | `web-vitals/attribution` |
15
- | Interaction to Next Paint | `inp` | ms | 每个 SDK View 维护交互候选近似 p98 |
16
- | Largest Contentful Paint | `lcp` | ms | `web-vitals/attribution` |
17
- | Time to First Byte | `ttfb` | ms | `web-vitals/attribution` |
11
+ | 指标 | `vital.metric` | 单位 | 当前采集方式 |
12
+ | ------------------------- | -------------- | ------ | ------------------------------------- |
13
+ | Cumulative Layout Shift | `cls` | 无量纲 | 每个 SDK View 独立计算 session window |
14
+ | First Contentful Paint | `fcp` | ms | 原生 Paint Timing observer |
15
+ | Interaction to Next Paint | `inp` | ms | 每个 SDK View 维护交互候选近似 p98 |
16
+ | Largest Contentful Paint | `lcp` | ms | 原生 LCP observer 与候选快照 |
17
+ | Time to First Byte | `ttfb` | ms | 文档 Navigation Timing |
18
18
 
19
- 插件通过动态 import 加载 `web-vitals/attribution`。加载失败只禁用该插件;`debug=true` 时输出 warning,不影响其他采集。
19
+ SDK 自行管理这些 observer、事件监听和任务;停止采集时会结算已观测值并释放它们,不再依赖第三方 `onFCP/onLCP/onTTFB` 回调的生命周期。浏览器不支持某项 API 时跳过该项,不用 0 伪造支持。
20
20
 
21
21
  ## 公共 Vital 字段
22
22
 
@@ -25,29 +25,45 @@
25
25
  - `vital.metric`
26
26
  - `vital.value`
27
27
  - `vital.id`
28
+ - `vital.version`:同一指标快照递增版本
29
+ - `vital.scope`:`document`、`activation` 或 `view`
28
30
  - `span_type=vital`
29
31
  - `outcome.type=success`,仅内部 rating=good
30
32
  - `outcome.type=warning`,其他 rating
31
33
 
32
- Vital Span 是瞬时 INTERNAL Span。非法 `NaN`、`Infinity` 不上报。当前 **不写** `vital.rating` 或 `navigationType` Attribute;good/poor 只通过 `outcome.type` 体现。
34
+ Vital Span 是瞬时 INTERNAL Span,时间表示生成本次快照的时刻;Session/View 关联仍使用原归属时间。非法、负数值不报。当前不写 `vital.rating` 或 `navigationType` Attribute;good/poor 只通过 `outcome.type` 体现。
35
+
36
+ `vital.id` 在一个作用域中的同一指标上保持稳定;值、归因或 View 上下文变化时增加版本。服务端按 `vital.id` 取最大 `vital.version`,**不能把修订值相加,也不能把每次快照当成一个新的页面样本**。重复 flush 没有变化时不重复发;同一 View 替换上下文时删除的业务字段不会在后续指标中残留。没有 `vital.final` 字段。
37
+
38
+ Session/View 身份固定在原作用域;仍活跃的所属 View 在生成快照前刷新用户、View 名称和业务上下文,结束后冻结最终上下文。文档指标晚于 SPA 导航到达时继续使用已结束初始 View 的上下文,不借用新页面的用户或业务字段。
33
39
 
34
40
  ## Navigation Type
35
41
 
36
- web-vitals 指标对象可能带 `navigationType`,但当前插件 **不会** 把它写入 Span。不要按该字段查询 Collector 数据。
42
+ 当前插件不写 `navigationType`,使用 `vital.scope` 和所属 View 的加载类型区分文档、BFCache 恢复与逻辑 View。不要按 `navigationType` 查询 Collector 数据。
37
43
 
38
44
  ## FCP、LCP 与 TTFB 的 View 归属
39
45
 
40
- 这三类指标使用第一个 Performance Entry 的真实发生时间回查历史 Runtime Context。因此即使路由已经切换,较晚回调的初始导航指标仍会归到发生时的旧 View,而不是当前 View。
46
+ 文档初始 FCP/LCP/TTFB 固定关联文档初始 View,`vital.scope=document`。即使 SPA 首屏重定向发生在第一次 paint 之前,也不会把文档累计值挂到新的 route_change View。缺少合法的文档初始 View 时不向当前 View 补报这些指标。
47
+
48
+ BFCache 的 `back-forward-cache` 指标绑定本次恢复的 View,`vital.scope=activation`,也不会随恢复后的 SPA 路由漂移。普通 SPA 导航没有新的文档 Navigation Timing;当前没有开启 Soft Navigation 采集,不能将这些文档指标理解为每次 SPA 路由的 FCP/LCP。
49
+
50
+ BFCache 恢复时的 FCP/LCP 使用 `pageshow` 后双 `requestAnimationFrame` 近似恢复绘制时间,不代表浏览器重新产生了文档首次绘制;TTFB 为 0。恢复指标按本次 activation 时间匹配恢复 View,View 尚未就绪时先有界暂存。
51
+
52
+ 初始化会恢复保留范围内的 View 历史,再归属浏览器 buffered 条目;首次手动 View 尚未建立时也会有界暂存待关联指标。首次手动 View 建立后可接收文档指标,不能无限追溯已被浏览器或 SDK 裁剪的条目。撤销同意会停止采集,再授权继续当前文档,不会重新制造 `initial_load` 或重复采集该文档的 FCP/LCP/TTFB;重新加载文档仍是新的首屏。
53
+
54
+ 文档 LCP 会保留当前候选,检查点可在首次交互之前上报;首次可信 click/keydown、页面隐藏或结束会收束观测窗口。显式 shutdown 会先取出已排队条目并导出最后已知候选。FCP/LCP 排除首次隐藏后的首屏绘制;prerender 的文档指标以激活时间为基准。
41
55
 
42
56
  Attribution 字段:
43
57
 
44
- | 指标 | 字段 |
45
- | --- | --- |
46
- | LCP | `vital.lcp.url`、`target`、`element_render_delay`、`resource_load_duration` |
47
- | FCP | 当前无独立归因字段 |
48
- | TTFB | `vital.ttfb.waiting_duration`、`dns_duration`、`connection_duration`、`request_duration` |
58
+ | 指标 | 字段 |
59
+ | ---- | ------------------------------------------------------------------------------------------------------------------------ |
60
+ | LCP | `vital.lcp.url`、`target`、`time_to_first_byte`、`resource_load_delay`、`element_render_delay`、`resource_load_duration` |
61
+ | FCP | 当前无独立归因字段 |
62
+ | TTFB | `vital.ttfb.waiting_duration`、`cache_duration`、`dns_duration`、`connection_duration`、`request_duration` |
63
+
64
+ LCP URL 经过 `redactUrl`;目标 selector 在观测候选时按 Action 的节点隐私与结构定位规则快照,之后节点移除不会清空已经安全捕获的 selector。无法安全定位时省略,不回退透传原始 DOM ID。LCP 资源关联选择该候选前最后一条同 URL 的 Resource Timing。
49
65
 
50
- LCP URL 经过 `redactUrl`;DOM target 等字段可能包含高基数或业务语义,应纳入隐私审查。
66
+ TTFB 五段按 Navigation Timing 的相邻边界裁剪,均非负,合计等于 TTFB。`vital.ttfb.cache_duration` 表示请求开始准备后至 DNS 阶段前的耗时;它不是缓存命中标记,也不能据此推断 memory/disk cache。
51
67
 
52
68
  ## 每个 View 的 CLS
53
69
 
@@ -57,8 +73,10 @@ SDK 不直接使用整页生命周期累计的 web-vitals CLS,而是按 SDK Vi
57
73
  - 相邻 Shift 间隔小于 1 秒且窗口总长小于 5 秒时进入同一 session window;
58
74
  - 取所有 session window 中累计值最大的一个;
59
75
  - 数值保留四位小数;
60
- - 记录该窗口中最大的单次 Shift 及目标元素(仅用于内部累计 `vital.value`,**不上报** `vital.cls.largest_shift_*`);
61
- - View 结束时发送一条 CLS Span,即使值为 0。
76
+ - 浏览器支持 Layout Shift 时,检查点可以发送值为 0 的 CLS;不支持时不伪造 0;
77
+ - 归因选取最大累计窗口内最大的单次 Shift;同一窗口后续的小位移不会覆盖主要来源;
78
+ - 从该 Shift 的 sources 中按 previous/current 两个矩形的并集面积选择节点,覆盖节点消失等情况;不把两个矩形间的空白算进面积;
79
+ - 同时记录该 Shift 的相对 View 时间、前后矩形和当时的 DPR;累计最大窗口改变时同步切换归因。
62
80
 
63
81
  Rating 阈值(只影响 `outcome.type`,不写 `vital.rating`):
64
82
 
@@ -66,19 +84,19 @@ Rating 阈值(只影响 `outcome.type`,不写 `vital.rating`):
66
84
  - `<= 0.25`:needs-improvement → `warning`
67
85
  - `> 0.25`:poor → `warning`
68
86
 
69
- 当前 **不上报** `vital.cls.load_state`。按 View 汇总的 CLS 传入空 attribution。
87
+ 归因字段为 `vital.cls.time`(相对 View 起点的 ms)、`vital.cls.target_selector`、`vital.cls.previous_rect.x/y/width/height`、`vital.cls.current_rect.x/y/width/height`(CSS px)和 `vital.cls.device_pixel_ratio`。DPR 不改变矩形原始 CSS 像素值。浏览器没有 source/矩形或节点无法安全定位时,对应字段缺失。不输出 `vital.cls.load_state` 或 `vital.cls.largest_shift_*`。
70
88
 
71
89
  ## 每个 View 的 INP
72
90
 
73
- SDK 使用 Event Timing Observer,`durationThreshold=40`:
91
+ SDK 将交互计数与慢交互候选分开:
74
92
 
75
- 1. 按 interactionId 合并同一次交互,只保留 duration 最大 Entry。
76
- 2. duration 上限保护为 60,000ms。
93
+ 1. 按 interactionId 选择同次交互中 duration 最大的候选;归因额外合并同交互,以及渲染时刻落在固定参考帧 ±8ms 内的条目,取最早输入/处理开始与最晚处理结束。
94
+ 2. duration 上限保护为 60,000ms;三段耗时共用这个值对应的终点并裁剪处理区间,保证非负且总和等于上报值。模态对话框等导致 processingEnd 超出绘制终点时也遵守此约束。
77
95
  3. 保留当前 View 最慢的 10 个候选。
78
- 4. 优先使用浏览器 `performance.interactionCount` 估算总交互数。
79
- 5. 浏览器不提供时,根据 interactionId 范围近似交互数。
96
+ 4. 候选 observer 的 durationThreshold 为 40ms,并用 first-input 覆盖第一次较快交互。
97
+ 5. 独立低阈值 observer(配置 durationThreshold=0,实际分辨率受浏览器约束)维护 interactionId 范围,避免只有慢候选参与计数;原生 interactionCount 可用时优先结合它,View 结束冻结原生边界并校正跨边界迟到交互。
80
98
  6. 取排序候选中接近 p98 的位置,而不是简单最大值。
81
- 7. View 没有有效 Interaction 时不发送 INP Span。
99
+ 7. 无交互或 API 不支持时不发 INP;明确观测到交互但尚无候选时可发 0,之后迟到候选以更高版本修订。它不是“未支持”的替代值。
82
100
 
83
101
  Rating 阈值:
84
102
 
@@ -90,15 +108,29 @@ Attribution:
90
108
 
91
109
  - `vital.inp.interaction_target`
92
110
  - `vital.inp.interaction_type`
111
+ - `vital.inp.target_selector`
112
+ - `vital.inp.time`:相对所属 View 起点的 ms
93
113
  - `vital.inp.input_delay`
94
114
  - `vital.inp.processing_duration`
95
115
  - `vital.inp.presentation_delay`
96
116
 
97
- Target 优先读取 `data-bk-action-name`、`aria-label`、`name`,否则使用标签名。
117
+ 同一 observer 批次先完成处理区间分组,再裁剪分组状态;较短条目也能补全保留候选的处理阶段。跨批次只保留与最慢 10 个候选相关的分组,内存不随交互总数增长。
118
+
119
+ Target 采用 Action 的显式命名模式:显式 Action 名称或标签回退,不自动采集整段 DOM 文本;selector 使用同一节点隐私策略。可信输入发生时先捕获经过隐私处理的字符串,快照匹配有效期为 10 秒,缓存最多 100 项;条目到达时按事件类型和时间匹配,用于目标已被路由卸载的情况。匹配到“禁止采集”结果时不会回退读取 DOM,定位歧义或信息不足时允许缺失。计数降级仍基于 interactionId 间隔估算,受浏览器实现和可交付条目限制,并非精确的业务点击计数。
98
120
 
99
121
  ## SPA 语义
100
122
 
101
- FCP、LCP、TTFB 本质上仍主要来自文档导航;CLS 和 INP 按 SDK View 划分,适合 SPA 路由。View 名称变化只更新当前 Vital Runtime Attributes,不会清空当前 CLS/INP;创建新 View 才会 finalize 旧值并重置。
123
+ FCP、LCP、TTFB 使用文档/恢复作用域;CLS 和 INP 为 `vital.scope=view`。同 ID 的名称或上下文修改不清空指标;新 View 创建时关闭旧 View 的时间区间,开始新的累计。
124
+
125
+ View 另存与 `PerformanceEntry.startTime` 同一时钟的相对起止时间,用于条目归属和 CLS/INP 的相对时间;epoch 时间保留给 Session 上下文和 OTLP 时间戳。系统墙钟调整不会直接移动性能条目的 View 边界。Hash、History 和手动 SPA View 使用同样的区间规则;MPA 新文档重新开始文档指标。
126
+
127
+ 结束 View 的 CLS/INP 状态最多接受 5 分钟内到达的条目,并限制总保留状态最多 100 个;过期状态在后续处理时裁剪。条目按 startTime 落入的 View 半开区间归属,迟到修订保持原 Session/View,超出保留范围的条目不改挂当前 View。
128
+
129
+ `flush()`、hidden、freeze、View 边界和显式 shutdown 会先读取 observer 已排队条目,再生成有变化的快照;结束 View 的迟到条目到达后也会更新快照。普通 flush/hidden 不结束 View、不为 Session 续期。shutdown 结算后才关闭回调;它无法等待尚未由浏览器产生的未来条目,也不保证系统强杀前一定发送成功。
130
+
131
+ 活跃 View 的 CLS/INP 变化和文档 LCP 候选更新会调度一个约 3 秒的合并检查点,多次变化共用待执行任务;没有变化不会持续发定时快照。浏览器后台节流可能延迟任务,网络发送还遵循导出器批处理,3 秒不是服务端可见时间保证。检查点不算用户活动。
132
+
133
+ 当前不提供 Soft Navigation 指标、自定义 duration vital 或自动将 INP 关联到 LoAF 脚本根因;下面的 Long Task/LoAF 是独立采集能力。
102
134
 
103
135
  ## Long Task 启用
104
136
 
@@ -179,8 +211,8 @@ Long Task 会为每个 Entry 建 Span,并可能附带多个脚本 Event;在
179
211
 
180
212
  在控制台检查 `PerformanceObserver.supportedEntryTypes`。不支持两种 API时是正常降级;还要确认插件已开启、Session 被采样、存在有效 View、Entry duration 达到阈值。
181
213
 
182
- ### CLS/INP 只在切换页面后出现
214
+ ### 查看当前 CLS/INP
183
215
 
184
- CLS/INP 在 View finalize 时发送;当前 View 通常在下一次路由、pagehide、Session 过期或 shutdown 时结束。需要调试最终值时可以触发一次 View 切换或 `await bkOT.shutdown()`。
216
+ 调用 `await bkOT.flush()` 可以生成当前指标检查点,不需要切换 View 或关闭 SDK。检索时按 `vital.id` 选择最大版本;检查点之后仍可能出现同一 View 的指标修订。
185
217
 
186
218
  完整字段见[遥测字段字典](./telemetry-reference.md#web-vitals-字段)和[Long Task 字段](./telemetry-reference.md#long-task-字段)。
@@ -128,16 +128,16 @@ privacy: {
128
128
  - SDK 构造 Resource attributes 时调用一次;
129
129
  - `BkOpenTelemetry.startSpan()` 合并运行时与显式属性后调用;
130
130
  - 内置/自定义插件也可以显式调用 `context.applyRedact()`;
131
- - Filter processor 不会在 `tracking.beforeSend` 之后再次调用脱敏 Hook。
131
+ - Filter processor 在全局 `tracking.beforeSend` 前后统一处理 Span、Event 和 Link 属性;同一对象可能经过多次脱敏,Hook 应保持幂等。
132
132
 
133
- 该 Hook 的类型只覆盖 Attributes,不会自动处理 Span Event。当前例外是 Long Task 插件在写入自己的 script/attribution Event 前显式调用;`recordException()` 产生的 exception message/stacktrace 不会经过这个 Hook。
133
+ 每次调用接收一个 Attributes 对象,包括第三方 Instrumentation 的字段和 `recordException()` 产生的 exception message/stacktrace。任一最终脱敏调用抛错时,整条遥测会丢弃,不回退发送原始数据。
134
134
 
135
- 因此 `tracking.beforeSend` 如果新增或改写字段,必须保证写入值已经安全:
135
+ `tracking.beforeSend` 的新增字段也会再次脱敏:
136
136
 
137
137
  ```ts
138
138
  tracking: {
139
139
  beforeSend(event) {
140
- // 这里写入的字段不会再自动走 redactAttributes
140
+ // 导出前仍会经过 redactAttributes
141
141
  event.attributes['biz.region'] = 'ap-guangzhou';
142
142
  },
143
143
  }
@@ -232,7 +232,7 @@ tracking: {
232
232
  错误可能包含:
233
233
 
234
234
  - `error.message`;
235
- - exception message、type 和 stacktrace;这些 Span Event 不对 `tracking.beforeSend` 暴露;
235
+ - exception message、type 和 stacktrace;可通过 `redactAttributes` 或 `tracking.beforeSend` 的 events 导出副本治理;
236
236
  - 源文件 URL、行列号;
237
237
  - Promise rejection 的字符串化内容;
238
238
  - 资源加载失败 URL。
@@ -35,9 +35,7 @@ export const rum = new BkOpenTelemetry({
35
35
  },
36
36
  request: {
37
37
  excludedUrls: [/\/health(?:\?|$)/],
38
- allowedTracingUrls: [
39
- rawUrl => new URL(rawUrl, location.href).origin === 'https://api.example.com',
40
- ],
38
+ allowedTracingUrls: [rawUrl => new URL(rawUrl, location.href).origin === 'https://api.example.com'],
41
39
  },
42
40
  blankScreen: {
43
41
  rootSelector: '#app',
@@ -50,9 +48,7 @@ export const rum = new BkOpenTelemetry({
50
48
  url.password = '';
51
49
  url.search = '';
52
50
  url.hash = '';
53
- url.pathname = url.pathname
54
- .replace(/\/orders\/[^/]+/g, '/orders/:id')
55
- .replace(/\/users\/[^/]+/g, '/users/:id');
51
+ url.pathname = url.pathname.replace(/\/orders\/[^/]+/g, '/orders/:id').replace(/\/users\/[^/]+/g, '/users/:id');
56
52
  return url.href;
57
53
  },
58
54
  redactAttributes(attributes) {
@@ -154,7 +150,7 @@ allowedTracingUrls: [
154
150
  const url = new URL(rawUrl, location.href);
155
151
  return url.origin === 'https://api.example.com' && url.pathname.startsWith('/v2/');
156
152
  },
157
- ]
153
+ ];
158
154
  ```
159
155
 
160
156
  不要使用宽泛 `/./` 或所有 URL,避免向第三方发送 Trace ID,也避免触发额外预检。API 端必须允许 `traceparent` header,并正确接续 W3C Trace Context。
@@ -190,11 +186,11 @@ Action:
190
186
 
191
187
  默认关闭项应按问题启用:
192
188
 
193
- | 能力 | 适合场景 | 主要风险 |
194
- | --- | --- | --- |
195
- | Long Task | 主线程卡顿和脚本归因 | 事件量、脚本 URL/函数信息 |
196
- | CSP | 安全策略诊断 | `original_policy`、高频 violation |
197
- | WebSocket | 连接失败/断开分析 | 全局 Proxy、长连接生命周期 |
189
+ | 能力 | 适合场景 | 主要风险 |
190
+ | --------- | -------------------- | --------------------------------- |
191
+ | Long Task | 主线程卡顿和脚本归因 | 事件量、脚本 URL/函数信息 |
192
+ | CSP | 安全策略诊断 | `original_policy`、高频 violation |
193
+ | WebSocket | 连接失败/断开分析 | 全局 Proxy、长连接生命周期 |
198
194
 
199
195
  先小流量评估平均每 Session 增量,再扩大采样。
200
196
 
@@ -241,7 +237,7 @@ SDK 设计为:
241
237
  服务端需要理解:
242
238
 
243
239
  - Session/View 生命周期通过多条 Span 表达,`session.phase`、`view.phase` 与 `view.version` 用于重建状态;
244
- - `action.id` 负责关联窗口,不提供客户端 count;
240
+ - Action 自身为单值 `action.id`,Resource/Error/Long Task 用 `action.ids` 数组关联;服务端需按数组展开和去重派生 count,客户端不双写旧字段;
245
241
  - `vital.value` 是原始指标;分位数服务端计算;
246
242
  - 资源网络阶段只在浏览器可见时出现;
247
243
  - IP/geo、错误指纹、聚合数不是 SDK 字段;