@blueking/open-telemetry 0.0.31 → 0.0.32

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.
@@ -4,23 +4,24 @@
4
4
 
5
5
  两份文档不重复维护同一套字段规范:一个记录实现现状,一个定义稳定承诺。
6
6
 
7
- 「[遥测字段字典](./telemetry-reference.md)」是由代码自动生成的事实清单,完整反映当前 SDK 实际产出的 Span 字段。它回答当前实际上报了哪些字段,其中可以包含新增、灰度和待废弃字段,不代表这些字段都已形成稳定承诺。
7
+ 「[遥测字段字典](./telemetry-reference.md)」对照当前 `src/` 列出 SDK **实际产出**的 Span 与 Attribute。它回答现在上报了哪些字段;其中可以包含新增、灰度和待废弃字段,不代表这些字段都已形成稳定承诺。
8
8
 
9
- 「RUM 语义约定」是前后端共同遵循的语义契约,围绕 RUM 核心字段定义含义、使用方式、约束和生命周期。字段经过评审并标记为 Stable 后,才成为对外稳定承诺;新增、灰度和废弃状态也在这里管理。
9
+ 「RUM 语义约定」是前后端共同遵循的语义契约。字段经过评审并标记为 Stable 后,才成为对外稳定承诺。本页字段口径与当前浏览器 SDK 实现对齐;后端入库后派生的字段单独标记为 Backend。
10
10
 
11
- 如果两份文档对不上,先查「遥测字段字典」确认 SDK 现在上报了什么,再查「RUM 语义约定」确认这是尚未完成的改动,还是实现偏差。代码中出现了某个字段,并不表示它已经稳定;只有在「RUM 语义约定」中标记为 Stable,前后端才需要长期兼容这个字段。
11
+ 如果两份文档对不上,先查「遥测字段字典」确认 SDK 现在上报了什么。代码中出现了某个字段,并不表示它已经稳定;只有在本页标记为 Stable,前后端才需要长期兼容这个字段。
12
12
 
13
13
  | 状态 | 描述 |
14
14
  | -------------------------------------------------------------- | -------------- |
15
- | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | 已上报,保持现状。 |
15
+ | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | 当前 SDK 已上报,保持现状。 |
16
16
  | ![Deprecated](https://img.shields.io/badge/-deprecated-red) | 已上报,但需要废弃。 |
17
17
  | ![Development](https://img.shields.io/badge/-development-blue) | 新补充或字段位置变更。 |
18
18
  | ![Backend](https://img.shields.io/badge/-backend-orange) | 由后端生成,前端不直接上报。 |
19
19
 
20
20
  原则:
21
- * Aegis、DataDog 后续统一转成 Otel Span 协议,同领域字段尽量对齐前两个 SDK,需控制字段数量,避免出现需转换 150+ 字段的情况。
22
- * 控制字段规模:输出可覆盖 Otel & Aegis & DataDog SDK 最简协议,其他不必要字段统一省略,减少初始技术负债(字段一旦放出但没有真正使用,稳定后一旦有需求变更,就有兼容风险)。
23
- * 指标:Web Vitals 指标由接收端派生,SDK 无需埋点。
21
+
22
+ - 浏览器 SDK 只上报原始 RUM Span(OTLP/HTTP JSON Trace),不创建 Metric 或 Log Provider。
23
+ - Web Vitals 由 SDK 以 `browser.web_vital` Span 上报(`vital.metric` = `cls` / `fcp` / `inp` / `lcp` / `ttfb`);分位数和跨会话聚合由接收端派生。
24
+ - 控制字段规模:只约定当前实现真正使用的字段;未上报字段不要当作采集端契约。
24
25
 
25
26
  ## 0x01 用户真实监控模型
26
27
 
@@ -54,9 +55,9 @@ Session
54
55
  | `events` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | Event[] | 事件列表 | 异常详情和 Long Task 脚本明细通过 Span Event 承载。 |
55
56
  | `links` | ![Development](https://img.shields.io/badge/-development-blue) | Link[] | [Span 链接](https://opentelemetry.io/docs/concepts/signals/traces/#span-links) | 链接的存在是为了 Span 同其他 Span 建立关联,从而表明存在因果关系。 |
56
57
  | `attributes` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | object | 属性 | 浏览器、设备、网络、异常等各类语义标签和度量。 |
57
- | `start_time` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | int | 开始时间(微秒) | -- |
58
- | `end_time` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | int | 结束时间(微秒) | -- |
59
- | `elapsed_time` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | int | 耗时(微秒) | -- |
58
+ | `start_time` | ![Backend](https://img.shields.io/badge/-backend-orange) | int | 开始时间(微秒) | 后端入库字段。SDK 线上传输使用 OTLP `startTimeUnixNano`(纳秒十进制字符串)。 |
59
+ | `end_time` | ![Backend](https://img.shields.io/badge/-backend-orange) | int | 结束时间(微秒) | 同上,对应 `endTimeUnixNano`。 |
60
+ | `elapsed_time` | ![Backend](https://img.shields.io/badge/-backend-orange) | int | 耗时(微秒) | 后端由起止时间相减;浏览器 SDK 不单独上报该 Attribute。 |
60
61
 
61
62
  ### b. Resource
62
63
 
@@ -68,13 +69,13 @@ Session
68
69
  | `resource.session.sample_rate` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | Session 采样率 | 取值范围为 `0`~`1`。 |
69
70
  | `resource.telemetry.sdk.version` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | SDK 版本 | -- |
70
71
  | [`resource.telemetry.sdk.language`](https://opentelemetry.io/docs/specs/semconv/resource/#telemetry-sdk) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | enum | 语言 | `webjs`。 |
71
- | `resource.telemetry.sdk.name` *[1]* | ![Development](https://img.shields.io/badge/-development-blue) | str | SDK 名称 | -- |
72
- | `resource.device.type` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | enum | 设备类型 | `desktop`、`mobile`、`tablet`、`other`。 |
72
+ | `resource.telemetry.sdk.name` *[1]* | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | SDK 名称 | -- |
73
+ | `resource.device.type` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | enum | 设备类型 | 当前仅为 `desktop`、`mobile`。 |
73
74
  | `resource.user_agent.name` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 代理名称 | 通常指的是浏览器的名称,如 `Chrome`、`Edge`。 |
74
75
  | `resource.user_agent.version` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 代理版本 | 通常指的是浏览器的名称,如 `149`、`151`。 |
75
76
  | `resource.user_agent.os.name` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 操作系统名 | 如 `macOS`、`Windows`、`Android`。 |
76
77
 
77
- **[1] `resource.telemetry.sdk.name`**:`blueking`(蓝鲸 Otel SDK,当前值为 @blueking/open-telemetry,需修改。)|aegis(Aegis SDK)。
78
+ **[1] `resource.telemetry.sdk.name`**:当前浏览器 SDK 固定为 `blueking`。Aegis 等其他生产端可使用各自名称,不要把 npm 包名 `@blueking/open-telemetry` 当作该字段值。
78
79
 
79
80
  ### c. Status
80
81
 
@@ -88,7 +89,7 @@ Session
88
89
  | 字段 | 状态 | 类型 | 描述 | 备注 |
89
90
  | ----------------------------------------------------------------------- | ---------------------------------------------------------- | ------ | ------------- | -------------------- |
90
91
  | `events[].name` *[1]* | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 事件名 | |
91
- | `events[].timestamp` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | int | 事件发生时间(微秒) | |
92
+ | `events[].timestamp` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | int | 事件发生时间 | SDK 传输为 OTLP Event UnixNano;后端可转为微秒。 |
92
93
  | `events[].attributes.exception.type` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 异常类型 | 如 `TypeError`。 |
93
94
  | `events[].attributes.exception.message` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 异常消息 | 如 `Failed to fetch`。 |
94
95
  | `events[].attributes.exception.stacktrace` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 异常堆栈 | |
@@ -115,7 +116,7 @@ Session
115
116
  | `links[].trace_id` | ![Development](https://img.shields.io/badge/-development-blue) | str | 关联 TraceID | |
116
117
  | `links[].span_id` | ![Development](https://img.shields.io/badge/-development-blue) | str | 关联 SpanID | |
117
118
 
118
- **[1] `links[].trace_id`**:满足 `attributesspan_type=resource, attributes.resource.type=xhr or fetch` 时,需将前端生成的 TraceID 记录到该字段。
119
+ **[1] `links[].trace_id`**:`span_type=resource` 且 `resource.type` 为 `xhr` 或 `fetch`,并注入/探测到 Trace Header 时,把对应后端 Trace ID 记到 Link 上。
119
120
 
120
121
 
121
122
  ## 0x03 Attributes
@@ -244,7 +245,7 @@ Aegis 将「连接类型」「网络质量」整合成 `netType`(`wifi`、`wir
244
245
  | `attributes.view.dom_complete` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | DOM Complete 时间(ms) | 仅初始导航存在。 |
245
246
  | `attributes.view.load_event` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | Load Event 时间(ms) | 仅初始导航存在。 |
246
247
  | `attributes.view.phase` *[4]* | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | enum | 视图生命周期阶段 | 枚举值:<br>- start<br>- update<br>- end |
247
- | `attributes.view.started_at` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | 视图开始时间(ms) | 取 `performance.timeOrigin`。 |
248
+ | `attributes.view.started_at` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | 视图开始时间(ms) | 初始 View 取 `performance.timeOrigin`(epoch ms);后续 View 取创建时刻。 |
248
249
  | `attributes.view.version` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | int | 视图事件版本号 | 同一 `attributes.view.id` 从 `1` 开始递增,用于排序和幂等合并。 |
249
250
  | `attributes.view.end_reason` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 结束原因 | -- |
250
251
 
@@ -381,14 +382,14 @@ View B █████████████
381
382
  | `attributes.action.frustration.type` *[3]* | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str[] | 挫败类型 |
382
383
 
383
384
 
384
- **[1] `attributes.action.loading_time`**:`loading_time` 可直接使用外层 `elapsed_time`。
385
+ **[1] `attributes.action.loading_time`**:当前浏览器 SDK 不写该 Attribute。有页面活动时,用 Span 起止时间(后端 `elapsed_time`)表达持续时长。
385
386
 
386
387
  **[2] `attributes.action.type`**:
387
388
 
388
389
  | 常见值 | 描述 |
389
390
  | -------------------- | ---------------------------------- |
390
391
  | `click` | 鼠标或指针设备触发的点击,蓝鲸 Web 自动 Action 当前使用该值。 |
391
- | `custom` | 通过 SDK API 主动上报的自定义 Action。 |
392
+ | `custom` | 通过 `reportAction({ name })` 主动上报;Span 名为 `action.<name>`。 |
392
393
  | `tap` | 触屏设备上的单次轻触。 |
393
394
  | `scroll` | 页面或滚动容器的滚动操作。 |
394
395
  | `swipe` | 触屏设备上的滑动手势。 |
@@ -452,7 +453,7 @@ View B █████████████
452
453
 
453
454
  **[2] `attributes.vital.value`**:CLS 单位为 `1`,FCP、INP、LCP、TTFB 单位为 `ms`。
454
455
 
455
- **[3] `attributes.vital.cls.largest_shift_value` & `attributes.vital.cls.largest_shift_target`**:Aegis & DataDog 无采集,废弃。
456
+ **[3] `attributes.vital.cls.largest_shift_value` & `attributes.vital.cls.largest_shift_target`**:当前浏览器 SDK 不上报;也不上报 `vital.cls.load_state`。CLS 只带公共 `vital.metric` / `vital.value` / `vital.id`。
456
457
 
457
458
  **[4] `attributes.vital.inp.interaction_type`**:交互类型。
458
459
 
@@ -471,7 +472,7 @@ View B █████████████
471
472
 
472
473
  **[8] `attributes.vital.lcp.resource_load_duration`**:LCP 元素依赖的外部资源(图片/字体等)从请求到下载完成的时间(如果 LCP 是纯文本节点,此项可能缺失),用于排查 CDN/网络/资源体积问题。
473
474
 
474
- **[9] `attributes.vital.lcp.time_to_first_byte`**:从导航开始到收到首字节的耗时(LCP ≈ `time_to_first_byte` + `resource_load_duration` + `element_render_delay`)。
475
+ **[9] `attributes.vital.lcp.time_to_first_byte`**:当前浏览器 SDK 不上报该字段。LCP 归因只包含 url / target / element_render_delay / resource_load_duration。
475
476
 
476
477
  **[10] `attributes.vital.ttfb.waiting_duration`**:页面激活到浏览器开始 Fetch,或开始启动 Service Worker 前的时间(包含重定向及浏览器前置处理)。
477
478
 
@@ -488,7 +489,7 @@ View B █████████████
488
489
  | 字段 | 状态 | 类型 | 描述 | 备注 |
489
490
  | ---------------------------------------------- | --------------------------------------------------------------- | ------- | ---------------- | ------------------ |
490
491
  | `attributes.blank_screen.reason` *[1]* | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 白屏判定原因 | |
491
- | `attributes.blank_screen.empty_ratio` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | 有效样本中的空白比例 | |
492
+ | `attributes.blank_screen.empty_ratio` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | 有效样本中的空白比例 | 回退根节点检测时仍为实际比例,不是固定 `1`。 |
492
493
  | `attributes.blank_screen.empty_sample_count` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | int | 空白采样数 | |
493
494
 
494
495
  **[1] `attributes.blank_screen.reason`**:
@@ -519,7 +520,7 @@ View B █████████████
519
520
 
520
521
  ### n. custom
521
522
 
522
- `reportCustomEvent()` 产生 `custom.<name>` Span。
523
+ `reportCustomEvent()` 产生 `custom.<name>` Span。`reportAction({ name })` 产生 `action.<name>`,且 `action.type=custom`;不要把手动 Action 的 Span 名写成固定的 `action.custom`。
523
524
 
524
525
  ## 0x04 资源加载时序
525
526
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  # 遥测字段字典
4
4
 
5
- 本页是当前 SDK 实际产出的 RUM Span、Resource Attribute、Span Attribute 和 Span Event 字典。它面向 Collector 接入、后端建模、查询规则、告警配置和数据验收。SDK 当前只上报 Trace Span,不产出 OpenTelemetry Metric 或 Log。
5
+ 本页是对照 `src/` 手工维护的**当前实现事实**字典,记录 SDK 实际产出的 RUM Span、Resource Attribute、Span Attribute 和 Span Event。它面向 Collector 接入、后端建模、查询规则、告警配置和数据验收。SDK 当前只上报 Trace Span,不产出 OpenTelemetry Metric 或 Log。前后端长期兼容承诺见 [RUM 语义约定](./span-protocol.md)。
6
6
 
7
7
  ## 快速定位
8
8
 
@@ -12,7 +12,7 @@
12
12
  | ---------------------- | --------------------------------------------------------------- | ------------------------------------------------- |
13
13
  | SDK 与应用 Resource | `service.*`、`deployment.environment.name`、`telemetry.sdk.*` | 哪个应用、环境、版本和 SDK 产生了数据? |
14
14
  | RUM 运行时上下文 | `session.id`、`view.id`、`action.id`、`user.id`、设备和网络字段 | 事件属于哪个 Session、View、Action 和用户上下文? |
15
- | 公共分类 | `span_type`、`outcome.type`、`outcome.reason` | 这是什么信号,结果是否正常,失败属于哪类原因? |
15
+ | 公共分类 | `span_type`、`outcome.type` | 这是什么信号,结果是否正常? |
16
16
  | 领域属性 | `resource.*`、`error.*`、`vital.*`、`long_task.*` 等 | 具体发生了什么,耗时、来源和诊断细节是什么? |
17
17
  | OTLP Envelope 与 Event | `traceId`、`spanId`、时间戳、`status`、`events` | 如何定位单条记录、关联调用链和读取异常/脚本归因? |
18
18
 
@@ -72,16 +72,14 @@ Resource Attribute 描述产生 Span 的 SDK 实例和运行环境,同一实
72
72
  | `service.version` | string | 配置 `application.version` 时 | 来自应用版本配置,通常填写发布版本或构建号,用于比较发布前后性能和错误变化。 |
73
73
  | `deployment.environment.name` | string | 必有 | 来自 `application.environment`,未配置时为 `production`。用于区分生产、预发布、测试等环境。 |
74
74
  | `session.sample_rate` | number | 必有 | 解析后的 `session.sampleRate`,范围 `0..1`。表示配置的 Session 采样概率,不表示当前某条 Span 的采样结果。 |
75
- | `telemetry.sdk.name` | string | 必有 | 固定为 `@blueking/open-telemetry`,用于识别数据生产 SDK。 |
75
+ | `telemetry.sdk.name` | string | 必有 | 固定为 `blueking`,用于识别蓝鲸浏览器 RUM SDK。 |
76
76
  | `telemetry.sdk.language` | string | 必有 | 固定为 `webjs`,表示浏览器 JavaScript SDK。 |
77
77
  | `telemetry.sdk.version` | string | 必有 | 当前 npm 包版本,用于排查字段能力或采集行为是否受 SDK 版本影响。 |
78
78
  | `user_agent.name` | string | `tracking.device !== false` 且有浏览器环境 | 从 User-Agent 识别,取值 `Edge`、`Chrome`、`Firefox`、`Safari` 或 `Other`。识别不到时为 `Other`。 |
79
79
  | `user_agent.version` | string | 能识别浏览器版本时 | 从 User-Agent 提取的浏览器主版本号,不包含次版本和补丁版本;识别不到时缺失。 |
80
80
  | `user_agent.os.name` | string | `tracking.device !== false` 且有浏览器环境 | 从 User-Agent / User-Agent Client Hints 推断,取值 `iOS`、`Android`、`macOS`、`Windows`、`Linux` 或 `Other`。 |
81
81
  | `device.type` | string | `tracking.device !== false` 且有浏览器环境 | `mobile` 或 `desktop`。根据 `userAgentData.mobile` 或移动端 User-Agent 特征推断,不代表具体硬件型号。 |
82
- | `device.memory_gib` | number | 浏览器支持 `navigator.deviceMemory` 时 | 浏览器提供的近似设备内存容量,单位 GiB。该值通常已分档并可能受隐私策略限制,不应当作精确物理内存。 |
83
-
84
- `tracking.device=false` 时 SDK 构造的 Resource 不写入设备 Resource 字段。Resource 在 SDK 初始化时经过一次 `redactAttributes`,运行期间不会随窗口或网络状态变化。
82
+ `tracking.device=false` 时 SDK 构造的 Resource 不写入设备 Resource 字段。Resource 在 SDK 初始化时经过一次 `redactAttributes`,运行期间不会随窗口或网络状态变化。当前实现不读取 `navigator.deviceMemory` 或逻辑处理器数量,因此不会出现 `device.memory_gib` 一类硬件容量字段。
85
83
 
86
84
  ## 运行时公共属性
87
85
 
@@ -91,7 +89,6 @@ Resource Attribute 描述产生 Span 的 SDK 实例和运行环境,同一实
91
89
  | ------------------------- | -------- | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
92
90
  | `user.id` | string | 调用 `setUser({ id })` 后 | 业务提供的用户 ID,SDK 原样写入并经过 `redactAttributes`。建议使用不可逆标识,避免直接上传手机号、邮箱等个人信息。 |
93
91
  | `session.id` | string | Session 有效 | SDK 生成的 Session 关联 ID。15 分钟无活动或 4 小时最大生命周期到期后会更换;同一 Session 的事件可用它聚合。 |
94
- | `session.start_time` | number | Session 有效 | Session 创建时间,单位 epoch ms。它不会因用户活动续期而变化,可用于计算 Session 相对时间。 |
95
92
  | `session.type` | string | Session 有效 | 当前固定为 `user`,为未来区分其他 Session 类型保留。 |
96
93
  | `session.has_replay` | boolean | Session 有效 | 当前固定为 `false`,表示 SDK 没有随该 Session 上报 Session Replay 数据。 |
97
94
  | `view.id` | string | View 有效 | 当前页面视图的关联 ID。首次加载、有效 SPA 路由变化、BFCache 恢复或 Session 续建都会创建新 View。 |
@@ -115,11 +112,10 @@ Resource Attribute 描述产生 Span 的 SDK 实例和运行环境,同一实
115
112
 
116
113
  | 字段 | 类型 | 条件 | 来源与详细说明 |
117
114
  | ---------------- | ------ | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
118
- | `span_type` | string | 所有 SDK 原生 RUM Span | 稳定的 RUM 一级分类:`session`、`view`、`resource`、`error`、`vital`、`long_task`、`action`、`websocket` 或 `custom`。 |
119
- | `outcome.type` | string | 所有 SDK 原生 RUM Span | 归一结果:`success`、`warning`、`error`、`timeout` 或 `abort`。Fetch/XHR 主动取消使用 `abort`。 |
120
- | `outcome.reason` | string | 需要解释非正常结果时 | 当前内置值包括 `network`、`network_timeout`、`http_4xx`、`http_5xx`、`blank_screen` 和 `csp`。正常结果以及可由领域字段完整表达的结果不写该字段。 |
115
+ | `span_type` | string | 所有 SDK 原生 RUM Span | 稳定的 RUM 一级分类:`session`、`view`、`resource`、`error`、`vital`、`long_task`、`action`、`websocket` 或 `custom`。 |
116
+ | `outcome.type` | string | 所有 SDK 原生 RUM Span | 归一结果:`success`、`warning`、`error`、`timeout` 或 `abort`。Fetch/XHR 主动取消使用 `abort`。 |
121
117
 
122
- `outcome.type` 是 RUM 查询维度,OTLP `status` 是 OpenTelemetry 状态,二者用途不同。例如 Web Vital 评级不佳时为 `outcome.type=warning`,但不会把 OTLP Status 设置为 `ERROR`。
118
+ `outcome.type` 是 RUM 查询维度,OTLP `status` 是 OpenTelemetry 状态,二者用途不同。例如 Web Vital 评级不佳时为 `outcome.type=warning`,但不会把 OTLP Status 设置为 `ERROR`。当前 `buildRumSpanAttributes()` **不会写入** `outcome.reason`;HTTP 失败用 `http.response.status_code`,错误入口用 `error.source`,白屏用 `blank_screen.reason`,CSP 用 `error.source=csp`。
123
119
 
124
120
  ## Span 清单
125
121
 
@@ -133,7 +129,7 @@ Resource Attribute 描述产生 Span 的 SDK 实例和运行环境,同一实
133
129
  | `browser.resource_error` | INTERNAL | `error` | `error.source=resource` | 错误采集默认开启 | script、img、link、media 等元素资源加载失败。 |
134
130
  | `browser.web_vital` | INTERNAL | `vital` | `vital.metric` | 默认开启 | 单项用户体验指标及归因。 |
135
131
  | `action.click` | INTERNAL | `action` | `action.type=click` | 自动 Action 默认开启 | 自动点击 Action 及其页面活动、错误和挫败归因。 |
136
- | `action.custom` | INTERNAL | `action` | `action.type=custom` | `reportAction` | 主动上报的业务 Action。 |
132
+ | `action.<name>` | INTERNAL | `action` | `action.type=custom` | `reportAction({ name })` | 主动上报的业务 Action;Span 名是 `action.` 加上业务 name。 |
137
133
  | `browser.blank_screen` | INTERNAL | `error` | `blank_screen.reason` | 默认开启 | 视口九宫格连续检测确认的白屏。 |
138
134
  | `browser.long_task` | INTERNAL | `long_task` | `long_task.entry_type` | `tracking.longTask`,默认关闭 | Long Animation Frame,或不支持时回退的 Long Task。 |
139
135
  | `csp.violation` | INTERNAL | `error` | `error.source=csp` | `tracking.cspViolation`,默认关闭 | 浏览器 `securitypolicyviolation` 事件。 |
@@ -144,17 +140,14 @@ Resource Attribute 描述产生 Span 的 SDK 实例和运行环境,同一实
144
140
 
145
141
  适用于 `browser.session`。Session 默认在 15 分钟无活动后过期,并受 4 小时最大生命周期限制。
146
142
 
147
- | 字段 | 类型 | 条件/取值 | 来源与详细说明 |
148
- | -------------------------- | ------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
149
- | `session.phase` | string | `start`、`rotate`、`end` | `start` 表示没有可复用记录时创建;`rotate` 表示旧 Session 到期后创建新 Session;`end` 表示当前 Session 失效。 |
150
- | `session.id` | string | 所有阶段 | 当前生命周期事件所描述的 Session ID。`end` 事件写旧 ID,`rotate` 事件写新 ID。 |
151
- | `session.start_time` | number | 所有阶段,epoch ms | 该 Session 最初创建时间。`end` 时仍保留原始开始时间,不是结束时间。 |
152
- | `session.type` | string | 所有阶段,固定 `user` | 表示按真实用户浏览活动划分的 Session。 |
153
- | `session.has_replay` | boolean | 所有阶段,固定 `false` | 明确当前 SDK 没有为该 Session 关联回放数据。 |
154
- | `session.lifecycle.reason` | string | 所有阶段 | `init`:初始化时创建;`inactivity`:15 分钟无活动;`maxLifetime`:达到 4 小时上限;`external`:共享存储被删除、替换或记录无效,导致本 Tab 当前 Session 失效。 |
155
- | `session.previous_id` | string | `rotate` 且能识别旧 Session 时 | 新 Session 的前一个 ID,用于串联因 `inactivity` 或 `maxLifetime` 发生的轮换。首次 `start` 通常缺失。 |
143
+ | 字段 | 类型 | 条件/取值 | 来源与详细说明 |
144
+ | -------------------- | ------- | ---------------------- | ----------------------------------------------------------------------------------------------------------------- |
145
+ | `session.phase` | string | `start`、`rotate`、`end` | `start` 表示没有可复用记录时创建;`rotate` 表示旧 Session 到期后创建新 Session;`end` 表示当前 Session 失效。 |
146
+ | `session.id` | string | 所有阶段 | 当前生命周期事件所描述的 Session ID。`end` 事件写旧 ID,`rotate` 事件写新 ID。 |
147
+ | `session.type` | string | 所有阶段,固定 `user` | 表示按真实用户浏览活动划分的 Session。 |
148
+ | `session.has_replay` | boolean | 所有阶段,固定 `false` | 明确当前 SDK 没有为该 Session 关联回放数据。 |
156
149
 
157
- Session Span 允许没有 `view.id`:SDK 先建立 Session,再创建初始 View;结束 Session 时也会先结束当前 View。
150
+ Session Span 允许没有 `view.id`:SDK 先建立 Session,再创建初始 View;结束 Session 时也会先结束当前 View。过期原因(`init` / `inactivity` / `maxLifetime` / `external`)只出现在插件内部的 `notifySessionLifecycle` 事件上,**不会**写成 Span Attribute;`session.start_time`、`session.lifecycle.reason`、`session.previous_id` 当前均不上报。
158
151
 
159
152
  ## View 字段
160
153
 
@@ -239,11 +232,11 @@ Link 的 `trace_id` / `span_id` 优先取自解析结果;`sw8` 这类无法映
239
232
 
240
233
  如果公共时间点缺失、不是有限数或顺序异常,SDK 会放弃整组阶段字段,避免产生负耗时。Resource Timing 的跨域详细信息还受 `Timing-Allow-Origin` 响应头限制。
241
234
 
242
- 请求结果映射:HTTP `408/504` 为 `outcome.type=timeout,outcome.reason=network_timeout`;其他 `4xx` 为 `error/http_4xx`;其他 `5xx` 为 `error/http_5xx`;Fetch AbortError 或 XHR `abort` 为 `abort` 且不写 reason;Fetch TimeoutError 或 XHR `timeout` 为 `timeout/network_timeout`;其他网络失败为 `error/network`;其余为 `success` 且不写 reason。
235
+ 请求结果映射(只写 `outcome.type`,不写 `outcome.reason`):HTTP `408/504` 为 `timeout`;其他 `4xx`/`5xx` 为 `error`;Fetch AbortError 或 XHR `abort` 为 `abort`;Fetch TimeoutError 或 XHR `timeout` 为 `timeout`;其他网络失败为 `error`;其余为 `success`。细分失败原因请用 `http.response.status_code` 或 Fetch/XHR 的 exception Event。
243
236
 
244
237
  ## 错误字段
245
238
 
246
- 三类自动浏览器错误都设置 `outcome.type=error`、OTLP Status `ERROR`,并记录 `exception` Span Event。错误来源由 `error.source` 表达,不重复写入 `outcome.reason`。
239
+ 三类自动浏览器错误都设置 `outcome.type=error`、OTLP Status `ERROR`,并记录 `exception` Span Event。错误来源由 `error.source` 表达。
247
240
 
248
241
  | 字段 | 类型 | 适用范围 | 来源与详细说明 |
249
242
  | -------------------- | ------- | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -253,9 +246,11 @@ Link 的 `trace_id` / `span_id` 优先取自解析结果;`sw8` 这类无法映
253
246
  | `code.filepath` | string | `browser.error` | `ErrorEvent.filename`,即浏览器报告的脚本文件地址。浏览器未提供或跨域屏蔽时可能为空。 |
254
247
  | `code.lineno` | number | `browser.error` | `ErrorEvent.lineno`,错误在脚本中的 1-based 行号;浏览器无法定位时可能为 `0`。 |
255
248
  | `code.column` | number | `browser.error` | `ErrorEvent.colno`,错误列号;具体是否 0-based 由浏览器实现决定。 |
256
- | `error.cross_origin` | boolean | 跨域 `Script error.` | 仅当没有 Error 对象和 filename,且消息为 `Script error.` 时写 `true`,用于识别被同源策略屏蔽详情的脚本错误。 |
257
249
  | `url.full` | string | `browser.resource_error` | 加载失败元素的 `src`、`href` 或 `currentSrc`,经 `redactUrl` 脱敏。浏览器拿不到资源地址时可能为空字符串。 |
258
- | `html.tag` | string | `browser.resource_error` | 失败元素的 `tagName`,浏览器通常返回大写,例如 `SCRIPT`、`IMG`、`LINK`,可用于按资源载体分类。 |
250
+
251
+ 当前 **不上报** `error.cross_origin` 或 `html.tag`。跨域脚本错误只能通过 `error.message` 为 `Script error.`、且 `code.filepath` 为空来识别。资源加载失败的元素标签只出现在 `error.message` 里(格式 `Resource load failed: <tag> <原始 URL>`),需要按标签聚合时应在服务端解析该文本。
252
+
253
+ 注意 `browser.resource_error` 的 `error.message` 携带的是 **未脱敏** 的资源 URL(`url.full` 才是脱敏后的值)。有敏感 URL 风险时用 `redactAttributes` 重写 `error.message`。
259
254
 
260
255
  `context.attributes.error()` 返回的任意业务 Attribute 会合并到三类自动错误 Span,并统一经过 `redactAttributes`。字段命名和类型由接入方负责。
261
256
 
@@ -265,7 +260,7 @@ Link 的 `trace_id` / `span_id` 优先取自解析结果;`sw8` 这类无法映
265
260
 
266
261
  | Event 字段 | 类型 | 条件 | 来源与详细说明 |
267
262
  | ---------------------- | ------ | ----------------- | --------------------------------------------------------------------------------------------- |
268
- | `exception.type` | string | 能识别异常类型 | Error 的 `name`,或 SDK 为非 Error 值补充的类型,例如 `UnhandledRejection`、`ResourceError`。 |
263
+ | `exception.type` | string | 能识别异常类型 | Error 的 `name`;非 Error 值时由 SDK 补类型:Promise rejection 为 `UnhandledRejection`,资源错误为 `ResourceError`,`window.error` 抛非 Error 值时为 `window.error`。 |
269
264
  | `exception.message` | string | 能提取异常消息 | Error message 或归一后的异常文本,用于错误检索和展示。 |
270
265
  | `exception.stacktrace` | string | Error 带 stack 时 | 浏览器提供的调用栈。跨域、非 Error rejection 或浏览器安全策略可能导致缺失。 |
271
266
 
@@ -275,53 +270,50 @@ Link 的 `trace_id` / `span_id` 优先取自解析结果;`sw8` 这类无法映
275
270
 
276
271
  `browser.web_vital` 上报 CLS、FCP、INP、LCP 和 TTFB。FCP/LCP/TTFB 由 `web-vitals/attribution` 采集导航级指标;CLS/INP 按 SDK View 边界汇总,View 结束时上报。
277
272
 
278
- | 字段 | 类型 | 条件/单位 | 来源与详细说明 |
279
- | -------------- | ------ | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
280
- | `vital.metric` | string | 必有 | 指标名的小写值:`cls`、`fcp`、`inp`、`lcp`、`ttfb`。用于同一 Span 名下区分指标。 |
281
- | `vital.value` | number | 必有 | 指标值。CLS 是无量纲分数;FCP、INP、LCP、TTFB 单位均为 ms。非有限值不会上报。 |
282
- | `vital.rating` | string | 必有 | `good`、`needs-improvement` 或 `poor`。当前 good/needs-improvement/poor 分界:CLS `≤0.1/≤0.25/>0.25`;INP `≤200/≤500/>500ms`;FCP `≤1800/≤3000/>3000ms`;LCP `≤2500/≤4000/>4000ms`;TTFB `≤800/≤1800/>1800ms`。 |
283
- | `vital.id` | string | 必有 | 单条 Vital 的唯一 ID。FCP/LCP/TTFB 沿用 `web-vitals` ID;按 View 汇总的 CLS/INP 由 SDK 生成。 |
273
+ | 字段 | 类型 | 条件/单位 | 来源与详细说明 |
274
+ | -------------- | ------ | --------- | ------------------------------------------------------------------------------------------------------- |
275
+ | `vital.metric` | string | 必有 | 指标名的小写值:`cls`、`fcp`、`inp`、`lcp`、`ttfb`。用于同一 Span 名下区分指标。 |
276
+ | `vital.value` | number | 必有 | 指标值。CLS 是无量纲分数;FCP、INP、LCP、TTFB 单位均为 ms。非有限值不会上报。 |
277
+ | `vital.id` | string | 必有 | 单条 Vital 的唯一 ID。FCP/LCP/TTFB 沿用 `web-vitals` ID;按 View 汇总的 CLS/INP 由 SDK 生成。 |
284
278
 
285
- `vital.rating=good` 映射为 `outcome.type=success`;其他评级映射为 `outcome.type=warning`。具体体验评级由 `vital.rating` 表达,不重复写入 `outcome.reason`。
279
+ `web-vitals` / SDK 内部仍会计算 rating,但 **不会** 写入 `vital.rating`。`rating=good` 映射为 `outcome.type=success`,其余映射为 `outcome.type=warning`。当前分界:CLS `≤0.1/≤0.25`;INP `≤200/≤500ms`;FCP/LCP/TTFB 沿用 `web-vitals` 默认阈值。也不写入 `navigationType`。
286
280
 
287
281
  ### LCP Attribution
288
282
 
283
+ `onLCP` 回调会抽取以下字段;缺失表示浏览器或 `web-vitals` 未提供,不代表 LCP 无效。
284
+
289
285
  | 字段 | 类型 | 单位/条件 | 来源与详细说明 |
290
286
  | ---------------------------------- | ------ | ----------------------- | ---------------------------------------------------------------------------------- |
291
287
  | `vital.lcp.url` | string | LCP 是图片等 URL 资源时 | LCP 资源 URL,经 `redactUrl` 脱敏。文本 LCP 没有资源 URL,因此缺失。 |
292
288
  | `vital.lcp.target` | string | 能定位 LCP 元素时 | `web-vitals` 生成的目标元素选择器/描述,用于定位拖慢最大内容绘制的 DOM 元素。 |
293
- | `vital.lcp.element_render_delay` | number | ms | LCP 资源加载完成到元素最终绘制之间的耗时;无独立资源时表示等待元素绘制的主要时间。 |
294
- | `vital.lcp.resource_load_duration` | number | ms | LCP 资源自身从请求开始到响应结束的下载耗时;文本 LCP 或无独立资源时通常为 `0`。 |
295
- | `vital.lcp.time_to_first_byte` | number | ms | 从导航开始到主文档响应首字节的时间,是 LCP 总耗时的服务端/网络前置部分。 |
289
+ | `vital.lcp.element_render_delay` | number | ms | LCP 资源加载完成到元素最终绘制之间的耗时。 |
290
+ | `vital.lcp.resource_load_duration` | number | ms | LCP 资源自身从请求开始到响应结束的下载耗时。 |
291
+
292
+ 当前 **不上报** `vital.lcp.time_to_first_byte`。
296
293
 
297
294
  ### CLS Attribution
298
295
 
299
- | 字段 | 类型 | 单位/条件 | 来源与详细说明 |
300
- | -------------------------------- | ------ | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
301
- | `vital.cls.largest_shift_target` | string | 能定位元素时 | 对 CLS 贡献最大的单次布局偏移中的首个目标元素描述。按 SDK View 汇总时取最大 Session Window 内最大的单次偏移目标。 |
302
- | `vital.cls.largest_shift_value` | number | 无量纲 | 上述单次布局偏移的分数,不是整个 `vital.value`;可用于判断 CLS 是否由一次大跳动还是多次小跳动组成。 |
303
- | `vital.cls.load_state` | string | 当前不产生 | 字段提取逻辑已预留,用于表达最大偏移发生时的文档加载阶段;当前 CLS 由 SDK 按 View 自行汇总,未向归因对象写入 load state,因此 Collector 数据中不会出现。 |
296
+ CLS 由 SDK 按 View 自行汇总,上报时 `attribution` 为空对象。因此 `vital.cls.load_state`、`vital.cls.largest_shift_target`、`vital.cls.largest_shift_value` **当前均不产生**。
304
297
 
305
298
  CLS 忽略 `hadRecentInput=true` 的偏移,并按“相邻偏移间隔小于 1 秒、窗口最长 5 秒”的 Session Window 规则取最大窗口累计值。View 结束时即使没有布局偏移也会上报 `vital.value=0`。
306
299
 
307
300
  ### INP Attribution
308
301
 
302
+ View 结束时,SDK 把选中的 p98 候选写入以下字段:
303
+
309
304
  | 字段 | 类型 | 单位/条件 | 来源与详细说明 |
310
305
  | ------------------------------- | ------ | ---------------- | -------------------------------------------------------------------------------------------------------- |
311
306
  | `vital.inp.interaction_target` | string | 能定位交互元素时 | 触发 INP 候选交互的元素描述。SDK 优先 `data-bk-action-name`、`aria-label`、`name`,否则使用小写 tag 名。 |
312
- | `vital.inp.interaction_type` | string | 必有 | SDK 按 View 汇总时为 Performance Event 名,例如 `click`、`keydown`;用于识别哪类输入产生延迟。 |
313
- | `vital.inp.input_delay` | number | ms | 从用户输入发生到浏览器开始执行事件处理器的延迟,主要反映主线程此前被其他任务占用。 |
314
- | `vital.inp.processing_duration` | number | ms | 从事件处理开始到处理结束的耗时,主要反映业务事件处理器本身及同步工作。 |
315
- | `vital.inp.presentation_delay` | number | ms | 事件处理完成到下一帧呈现的耗时,包含样式、布局、绘制、合成等后续工作。三段之和应近似 `vital.value`。 |
307
+ | `vital.inp.interaction_type` | string | 必有 | Performance Event 名,例如 `click`、`keydown`。 |
308
+ | `vital.inp.input_delay` | number | ms | 从用户输入发生到浏览器开始执行事件处理器的延迟。 |
309
+ | `vital.inp.processing_duration` | number | ms | 从事件处理开始到处理结束的耗时。 |
310
+ | `vital.inp.presentation_delay` | number | ms | 事件处理完成到下一帧呈现的耗时。三段之和应近似 `vital.value`。 |
316
311
 
317
312
  INP 只在 View 内至少观察到一次带 `interactionId` 的 Event Timing 时上报;Performance Observer 的 `durationThreshold` 为 40ms,值最大限制为 60,000ms。SDK 保留最慢的 10 个交互候选,并按交互数量选择近似 p98 候选。
318
313
 
319
314
  ### FCP Attribution
320
315
 
321
- | 字段 | 类型 | 单位/条件 | 来源与详细说明 |
322
- | ------------------------------ | ------ | --------- | --------------------------------------------------------------------------------------------------------------------- |
323
- | `vital.fcp.time_to_first_byte` | number | ms | 导航开始到主文档响应首字节的时间。`FCP - TTFB` 可近似观察首字节后到首次内容绘制的前端阶段。 |
324
- | `vital.fcp.load_state` | string | 必有 | FCP 发生时的文档加载阶段,例如 `loading`、`dom-interactive`、`dom-content-loaded`、`complete`;越早绘制通常体验越好。 |
316
+ `onFCP` 只上报公共 `vital.*` 字段。`pickAttribution()` 对 `FCP` 没有抽取分支,因此 **不上报** `vital.fcp.time_to_first_byte` 或 `vital.fcp.load_state`。
325
317
 
326
318
  ### TTFB Attribution
327
319
 
@@ -340,33 +332,29 @@ Attribution 字段受浏览器能力和指标实现影响,缺失不代表指
340
332
  | ------------------------- | -------- | ------------------------ | -------------- | --------------------------------------------------------------------------------------------------------- |
341
333
  | `action.id` | string | 必有 | 必有 | 当前 Action 的唯一 ID。自动 Action 活动期间,其他 Span 上的同名运行时字段也是单个 string。 |
342
334
  | `action.type` | string | 固定 `click` | 固定 `custom` | 区分自动点击和 `reportAction` 主动上报。 |
343
- | `action.name` | string | `Click on <target>` | payload `name` | 可读名称。自动点击目标名默认来自显式 `data-bk-action-name`,启用文本采集后才会回退到可访问名称/DOM 文本。 |
344
335
  | `action.target.name` | string | 必有 | 不写 | 自动点击提取的目标名称,最多 100 字符;默认隐私模式下未显式命名时通常为小写 tag 名。 |
345
- | `action.target.tag` | string | 必有 | 不写 | 实际点击目标的原始 `tagName`,SDK 转为小写,用于按按钮、链接等元素类型聚合。 |
346
- | `action.loading_time` | number | 点击后有可观察页面活动时 | 不写 | 单位 ms,从点击发生到相关请求、资源、Long Task、DOM 活动安静的耗时;无页面副作用时不写,而不是 `0`。 |
336
+ | `action.target.tag` | string | 必有 | 不写 | 实际点击目标的原始 `tagName`,SDK 转为小写。 |
347
337
  | `action.frustration.type` | string[] | 检出挫败信号时 | 不写 | 可包含 `rage_click`、`dead_click`、`error_click`;一次 Action 可同时命中多个值。 |
348
338
 
339
+ 自动 Action 的可读名称不单独写成 `action.name` Attribute,而是体现在目标字段和 Span 名 `action.click` 上。手动 Action 的 Span 名为 `action.<payload.name>`,`action.type=custom`。点击后若有可观察页面活动,SDK 用 Span 的 `startTimeUnixNano`/`endTimeUnixNano` 表达持续时间,**不上报** `action.loading_time` 属性。无页面副作用时 Span 起止时间相同。
340
+
349
341
  `rage_click` 表示 1 秒内、100px 范围内连续至少三次点击;`dead_click` 表示点击后没有页面活动、输入、滚动或选择等反馈(链接、表单输入等会排除);`error_click` 表示 Action 生命周期内出现错误。有错误或任一挫败信号时为 `outcome.type=warning`,具体类型只由 `action.frustration.type` 表达。
350
342
 
351
- `reportAction({ attributes })` 的业务 Attribute 会合并到 `action.custom`,但固定分类和事件字段由 SDK 写入。业务应避免复用本页保留字段名。
343
+ `reportAction({ attributes })` 的业务 Attribute 会合并到该 Span,但固定分类和事件字段由 SDK 写入。业务应避免复用本页保留字段名。
352
344
 
353
345
  ## 白屏字段
354
346
 
355
347
  白屏插件在页面活动稳定后对视口九宫格采样,并在默认 200ms 间隔后再次确认;每个 View 最多上报一次。
356
348
 
357
- | 字段 | 类型 | 条件/单位 | 来源与详细说明 |
358
- | ----------------------------------- | ------- | ------------------ | -------------------------------------------------------------------------------------------------- |
359
- | `blank_screen.reason` | string | 必有 | `missing_root`:配置的根节点不存在;`empty_viewport`:根节点存在,但有效采样点的空白比例达到阈值。 |
360
- | `blank_screen.empty_ratio` | number | `0..1` | `empty_sample_count / valid_sample_count`。默认阈值为 `0.8`;根节点缺失时固定为 `1`。 |
361
- | `blank_screen.sample_count` | number | 必有,当前固定 `9` | 视口 20%、50%、80% 横纵坐标组合形成的九宫格总采样点数。 |
362
- | `blank_screen.valid_sample_count` | number | 必有 | 参与空白比例计算的点数,等于总采样点减去 ignored 点。为 `0` 时不报告白屏。 |
363
- | `blank_screen.empty_sample_count` | number | 必有 | 有效采样点中,没有发现根节点内可见内容元素的点数。 |
364
- | `blank_screen.ignored_sample_count` | number | 必有 | 命中 loading、Skeleton 或业务 `ignoreSelectors` 的点数;这些点从比例分母中排除。 |
365
- | `blank_screen.root_selector` | string | 必有 | 实际检测使用的根节点 CSS selector,默认 `body`。用于判断是否因挂载点配置错误产生 `missing_root`。 |
366
- | `blank_screen.root_found` | boolean | 必有 | 是否找到根节点。`false` 与 `reason=missing_root` 对应。 |
367
- | `blank_screen.center_element` | string | 必有,可能为空 | 视口中心最上层元素的小写 tag,存在 ID 时为 `tag#id`;用于快速判断遮罩、容器或空 body。 |
349
+ | 字段 | 类型 | 条件/单位 | 来源与详细说明 |
350
+ | ----------------------------- | ------ | ------------------ | -------------------------------------------------------------------------------------------------- |
351
+ | `blank_screen.reason` | string | 必有 | `missing_root`:配置的根节点不存在,检测回退到 `body`/`documentElement`;`empty_viewport`:配置根节点存在。 |
352
+ | `blank_screen.empty_ratio` | number | `0..1` | `emptyCount / validCount`。默认阈值为 `0.8`。根节点缺失时仍是回退后的实际比例,不是固定 `1`。 |
353
+ | `blank_screen.empty_sample_count` | number | 必有 | 有效采样点中,没有发现根节点内可见内容元素的点数。 |
368
354
 
369
- 该 Span 固定为 `outcome.type=error,outcome.reason=blank_screen`,但检测是基于采样点的启发式判断,应结合截图、错误和 View 数据进一步确认。
355
+ 当前 **不上报** `blank_screen.sample_count`、`valid_sample_count`、`ignored_sample_count`、`root_selector`、`root_found`、`center_element`。分母为 0 时不报告白屏。
356
+
357
+ 该 Span 固定为 `span_type=error`、`outcome.type=error`,不写 `outcome.reason`,也不设置 OTLP Status ERROR,不记录 exception Event。检测是基于采样点的启发式判断,应结合错误和 View 数据进一步确认。
370
358
 
371
359
  ## Long Task 字段
372
360
 
@@ -378,14 +366,14 @@ Attribution 字段受浏览器能力和指标实现影响,缺失不代表指
378
366
  | ------------------------------------ | ------ | ------------------------------- | -------------------------------------------------------------------------------------------------- |
379
367
  | `long_task.id` | string | 必有 | 当前 Long Task/LoAF 事件的随机唯一 ID,用于关联其 Span Event。 |
380
368
  | `long_task.entry_type` | string | 必有 | `long-animation-frame` 或 `long-task`,表示使用的浏览器 Performance Entry 类型。 |
381
- | `long_task.duration` | number | 必有,ms | 整个 LoAF/Long Task 持续时间,来源 `PerformanceEntry.duration`。 |
382
369
  | `long_task.name` | string | 必有 | 浏览器 Performance Entry 的 `name`;Legacy Long Task 常见为 `self` 或跨上下文归因名称。 |
383
- | `long_task.start_time` | number | 仅 LoAF,相对 timeOrigin ms | LoAF 的 `startTime`。Span 自身开始时间已转换为 epoch,这个字段保留原始 Performance Timeline 坐标。 |
384
- | `long_task.blocking_duration` | number | 仅 LoAF,ms | 浏览器计算的阻塞时间,表示超出可接受帧预算、真正阻塞交互响应的部分,不等于完整 `duration`。 |
370
+ | `long_task.blocking_duration` | number | 仅 LoAF,ms | 浏览器计算的阻塞时间,表示超出可接受帧预算、真正阻塞交互响应的部分,不等于完整持续时间。 |
385
371
  | `long_task.first_ui_event_timestamp` | number | LoAF 提供时,相对 timeOrigin ms | 该帧内首个 UI Event 的时间点;可判断长帧是否直接阻塞用户输入。 |
386
372
  | `long_task.render_start` | number | LoAF 提供时,相对 timeOrigin ms | 浏览器开始本帧渲染阶段的时间点。 |
387
373
  | `long_task.style_and_layout_start` | number | LoAF 提供时,相对 timeOrigin ms | 本帧开始样式计算和布局的时间点,可与 `render_start` 分析渲染阶段开销。 |
388
374
 
375
+ Span 持续时间就是 `PerformanceEntry.duration`(起止时间已转换为 epoch)。当前 **不上报** `long_task.duration` 或 `long_task.start_time` Attribute;需要原始 Performance Timeline 坐标时,用 `long_task.render_start` 等 LoAF 字段,或从 Span 起始时间减去 Resource 上的 SDK 时间基准。
376
+
389
377
  ### `long_task.script` Span Event
390
378
 
391
379
  每个 LoAF `scripts` 条目生成一个同名 Event;Event 自身时间戳为 `performance.timeOrigin + script.startTime`。
@@ -419,7 +407,7 @@ Legacy Long Task 的每个 attribution 条目生成一个 Event,Event 时间
419
407
 
420
408
  ## CSP 字段
421
409
 
422
- 适用于 `csp.violation`,来自浏览器 `SecurityPolicyViolationEvent`。该 Span 固定为 `outcome.type=error,outcome.reason=csp,error.source=csp`。
410
+ 适用于 `csp.violation`,来自浏览器 `SecurityPolicyViolationEvent`。该 Span 固定为 `span_type=error`、`outcome.type=error`、`error.source=csp`,不写 `outcome.reason`,也不设置 OTLP Status ERROR。
423
411
 
424
412
  | 字段 | 类型 | 条件 | 来源与详细说明 |
425
413
  | ------------------------- | ------ | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
@@ -452,7 +440,7 @@ Legacy Long Task 的每个 attribution 条目生成一个 Event,Event 时间
452
440
  | `websocket.close.reason` | string | 仅 `close` | 对端/调用方提供的关闭原因文本,可能为空。该值不是 URL,不经过 `redactUrl`,敏感内容应由 `redactAttributes` 清理。 |
453
441
  | `websocket.close.was_clean` | boolean | 仅 `close` | 浏览器判断连接是否完成干净关闭握手;`false` 常见于网络中断或异常终止。 |
454
442
 
455
- `error` 阶段设置 `outcome.type=error,outcome.reason=network`;其他阶段为 `outcome.type=success` 且不写 reason。SDK 当前不采集 WebSocket 消息内容、数量或字节数。
443
+ `error` 阶段设置 `outcome.type=error`;其他阶段为 `outcome.type=success`。当前不写 `outcome.reason`。SDK 不采集 WebSocket 消息内容、数量或字节数。
456
444
 
457
445
  ## Custom Event 字段
458
446
 
@@ -485,8 +473,15 @@ Legacy Long Task 的每个 attribution 条目生成一个 Event,Event 时间
485
473
  当前源码不会产生:
486
474
 
487
475
  - `/v1/metrics`、`/v1/logs` 请求,以及 Counter、Histogram、Gauge、LogRecord。
488
- - `session.duration`、`view.duration`;持续时间应由服务端根据生命周期事件派生。
489
- - `vital.cls.load_state`;字段提取代码已预留,但当前按 View 汇总的 CLS 不提供该归因值。
476
+ - `session.duration`、`view.duration`、`session.start_time`、`session.lifecycle.reason`、`session.previous_id`。
477
+ - `outcome.reason`;失败细分由领域字段表达。
478
+ - `vital.rating`、`vital.cls.*` 归因、`vital.fcp.*` 归因、`vital.lcp.time_to_first_byte`。
479
+ - `action.name`、`action.loading_time`;手动 Action 的 name 只进入 Span 名 `action.<name>`。
480
+ - `blank_screen.sample_count`、`valid_sample_count`、`ignored_sample_count`、`root_selector`、`root_found`、`center_element`。
481
+ - `long_task.duration`、`long_task.start_time`。
482
+ - `error.cross_origin`、`html.tag`。
483
+ - `device.memory_gib` 及逻辑处理器数字段;Device Resource 只读 UA/UA-CH,不读 `navigator.deviceMemory` 或 `hardwareConcurrency`。
484
+ - `tracestate`;SDK 既不注入也不记录。
490
485
  - Action、Resource、Error、Long Task count;计数和分布应从原始 Span 聚合。
491
486
  - 错误 fingerprint/group;错误分组由服务端根据 exception、message、stack 等字段计算。
492
487
  - HTTP request/response Body、Header;Trace Context 注入不代表采集请求内容。
@@ -38,8 +38,8 @@ https://rum.example.com/collector/v1/rum
38
38
  { "key": "service.name", "value": { "stringValue": "order-center" } },
39
39
  { "key": "session.sample_rate", "value": { "intValue": 1 } },
40
40
  { "key": "telemetry.sdk.language", "value": { "stringValue": "webjs" } },
41
- { "key": "telemetry.sdk.name", "value": { "stringValue": "@blueking/open-telemetry" } },
42
- { "key": "telemetry.sdk.version", "value": { "stringValue": "0.0.22" } }
41
+ { "key": "telemetry.sdk.name", "value": { "stringValue": "blueking" } },
42
+ { "key": "telemetry.sdk.version", "value": { "stringValue": "0.0.29" } }
43
43
  ],
44
44
  "droppedAttributesCount": 0
45
45
  },
@@ -56,7 +56,6 @@ https://rum.example.com/collector/v1/rum
56
56
  "endTimeUnixNano": "1784592000248000000",
57
57
  "attributes": [
58
58
  { "key": "session.id", "value": { "stringValue": "session-01" } },
59
- { "key": "session.start_time", "value": { "intValue": 1784592000000 } },
60
59
  { "key": "session.type", "value": { "stringValue": "user" } },
61
60
  { "key": "session.has_replay", "value": { "boolValue": false } },
62
61
  { "key": "view.id", "value": { "stringValue": "view-01" } },
@@ -94,6 +93,7 @@ Wire 编码注意点:
94
93
  - Status `code=0` 是 UNSET,ERROR 为 `2`;
95
94
  - `startTimeUnixNano`、`endTimeUnixNano` 和 Event `timeUnixNano` 编码为十进制字符串;
96
95
  - Attribute 不是普通扁平对象,而是 `{ key, value }` 数组;value 使用 `stringValue`、`intValue`、`doubleValue`、`boolValue` 或 `arrayValue`;
96
+ - `session.sample_rate` 在值为整数 `1`/`0` 时常见为 `intValue`,小数采样率(如 `0.2`)为 `doubleValue`;按 number 读取即可;
97
97
  - `traceId` 是 32 位十六进制字符串,`spanId` / `parentSpanId` 是 16 位;没有父 Span 时省略 `parentSpanId`;
98
98
  - `events` 中的 exception/Long Task Event 也使用同样的 Attribute Value 包装;
99
99
  - `flags=257` 是当前 transformer 写出的“本地 sampled Span 且携带 isRemote 有效位”组合值,不要把它当作业务字段;
@@ -239,8 +239,8 @@ transport: {
239
239
 
240
240
  | 项目 | 当前行为 |
241
241
  | --- | --- |
242
- | `timeoutMillis` | 默认 `10000ms`,且必须是大于 0 的有限数值 |
243
- | `concurrencyLimit` | 默认 `30` 个待处理 export Promise |
242
+ | `timeoutMillis` | 不配置时为 `10000ms`,且必须是大于 0 的有限数值 |
243
+ | `concurrencyLimit` | 不配置时为 `30` 个待处理 export Promise |
244
244
  | 可重试 HTTP 状态 | `429`、`502`、`503`、`504` |
245
245
  | 可重试网络错误 | 浏览器 Fetch 的部分 `TypeError` 网络错误 |
246
246
  | 最大重试次数 | 初次请求后最多再尝试 5 次,但不能超过总体超时期限 |
@@ -291,18 +291,23 @@ session: {
291
291
 
292
292
  排查“手动事件没有上报”时,先暂时设置 `sampleRate: 1`。
293
293
 
294
- ## 导出前过滤
294
+ ## 两层拦截:Sampler 与 Filtering Processor
295
295
 
296
- 一个已结束 Span 还需要通过 `FilteringSpanProcessor` 才会进入 simple/batch processor。以下数据会被丢弃:
296
+ 采样和过滤发生在两个不同阶段,排查时要分清:
297
297
 
298
- - 没有命中当前 Session 采样;
299
- - 没有关联有效 Session;
300
- - 除 `browser.session` 外,没有关联有效 View;
301
- - 命中 SDK 自身的 `/v1/rum` 上报地址;
302
- - 被内部插件标记为丢弃;
298
+ **第一层 · Provider Sampler(Span 创建时)。** `WebTracerProvider` 使用 `RumSessionSampler`:Session 未命中采样时返回 `NOT_RECORD`,Span 直接是 non-recording 的,不记录属性也不会触发 `onEnd`。这一层对**所有** Span 生效,包括通过 `useInstrumentation()` 注册的第三方 OpenTelemetry Instrumentation——它们不需要自己判断采样。
299
+
300
+ **第二层 · `FilteringSpanProcessor`(Span 结束时)。** 已结束的 recording Span 还要通过它才进入 simple/batch processor。以下会被丢弃:
301
+
302
+ - 没有关联有效 `session.id`;
303
+ - 除 `browser.session` 外,没有关联有效 `view.id`;
304
+ - 属性里的 `url.full` 或 `http.url` 命中 SDK 自身的 `/v1/rum` 上报地址(后者用于识别第三方 HTTP Instrumentation 产生的自上报请求);
305
+ - 被请求插件的 `request.beforeSend` 标记为丢弃;
303
306
  - 被全局 `tracking.beforeSend` 返回 `false` 拒绝。
304
307
 
305
- 过滤层会在 Span 创建时补齐缺失的运行时和页面属性;Span 结束时执行上述过滤,并在交给 exporter 前移除仅供内部使用的采样标记。详细顺序见[架构与数据流](./architecture.md)。
308
+ 同一个 Processor 还负责在 `onStart` 阶段补齐 Span 尚未显式写入的运行时属性和 `context.attributes.page()`;已有同名属性不会被覆盖。
309
+
310
+ 因此“未采样”和“被过滤”的现象不同:未采样时浏览器里根本没有成形的 Span;被过滤时 Span 已创建但没有进入导出队列,`debug: true` 也不会打印它。详细顺序见[架构与数据流](./architecture.md)。
306
311
 
307
312
  ## CORS 基线
308
313
 
@@ -228,7 +228,7 @@ console.log({
228
228
  ### 3. Payload 和 Hook
229
229
 
230
230
  - `name` 类型必须是 string,并建议使用非空、低基数枚举;
231
- - 当前运行时不拒绝空字符串:`reportCustomEvent({ name: '' })` 会尝试生成名为 `custom.` 的 Span,`reportAction({ name: '' })` 会生成空的 `action.name`,都不会仅因空 name 抛错或丢弃,但数据不可用,不应依赖该行为;
231
+ - 当前运行时不拒绝空字符串:`reportCustomEvent({ name: '' })` 会尝试生成名为 `custom.` 的 Span,`reportAction({ name: '' })` 会生成名为 `action.` 的 Span,都不会仅因空 name 抛错或丢弃,但数据不可用,不应依赖该行为;
232
232
  - attributes 只能使用合法 OTel Attribute 类型;
233
233
  - `context.attributes.page/custom` 与 `privacy.redactAttributes` 不应抛错;
234
234
  - `tracking.beforeSend` 不能按 name 返回 false;Custom Event 不经过请求级 `request.beforeSend`。
@@ -144,6 +144,7 @@ Span 名固定为 `browser.view`:
144
144
  - 初始加载:只要能取得 `loadEventEnd`,即使前 100ms 没有其他活动,也可生成 Loading Time;最终取 `loadEventEnd` 与页面活动结束耗时的较大值。
145
145
  - SPA/BFCache/Session Renewal:只有观察到页面活动时才生成自动 Loading Time,取 View 开始到页面活动结束的耗时。
146
146
  - 页面在计算期间变为 hidden:取消自动计算,不上报不可靠结果。
147
+ - **创建 View 时页面已经是 hidden:完全不启动自动计算**,之后回到前台也不会补算。后台标签页预加载、`target=_blank` 打开的页面因此常常没有 `view.loading_time`;这类场景需要 `setViewLoadingTime()` 手动确认。
147
148
 
148
149
  `view.loading_time_source=auto` 表示自动计算。
149
150