@blueking/open-telemetry 0.0.31 → 0.0.33

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 (32) hide show
  1. package/README.md +71 -33
  2. package/dist/bk-rum.global.js +2 -2
  3. package/dist/bk-rum.global.js.map +1 -1
  4. package/dist/core/plugin.d.ts +3 -0
  5. package/dist/index.js +461 -354
  6. package/dist/index.js.map +1 -1
  7. package/dist/plugins/web-vitals.d.ts +1 -1
  8. package/dist/schema/attributes.d.ts +1 -0
  9. package/dist/schema/span.d.ts +10 -0
  10. package/package.json +1 -1
  11. package/skills/bk-rum-otele/SKILL.md +1 -1
  12. package/skills/bk-rum-otele/evals/evals.json +1 -1
  13. package/skills/bk-rum-otele/references/actions.md +16 -10
  14. package/skills/bk-rum-otele/references/api.md +6 -3
  15. package/skills/bk-rum-otele/references/architecture.md +4 -4
  16. package/skills/bk-rum-otele/references/blank-screen.md +10 -19
  17. package/skills/bk-rum-otele/references/capabilities.md +18 -18
  18. package/skills/bk-rum-otele/references/configuration.md +5 -5
  19. package/skills/bk-rum-otele/references/data-model.md +14 -16
  20. package/skills/bk-rum-otele/references/errors-security.md +9 -8
  21. package/skills/bk-rum-otele/references/extensions.md +8 -0
  22. package/skills/bk-rum-otele/references/framework-integration.md +1 -1
  23. package/skills/bk-rum-otele/references/getting-started.md +21 -2
  24. package/skills/bk-rum-otele/references/network.md +21 -22
  25. package/skills/bk-rum-otele/references/performance.md +14 -25
  26. package/skills/bk-rum-otele/references/privacy.md +1 -1
  27. package/skills/bk-rum-otele/references/session-context.md +16 -12
  28. package/skills/bk-rum-otele/references/span-protocol.md +192 -282
  29. package/skills/bk-rum-otele/references/telemetry-reference.md +97 -99
  30. package/skills/bk-rum-otele/references/transport-sampling.md +19 -14
  31. package/skills/bk-rum-otele/references/troubleshooting.md +4 -4
  32. package/skills/bk-rum-otele/references/views-routing.md +5 -4
@@ -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 以 `span_type=vital` 的 Span 上报(`vital.metric` = `cls` / `fcp` / `inp` / `lcp` / `ttfb`);分位数和跨会话聚合由接收端派生。
24
+ - 控制字段规模:只约定当前实现真正使用的字段;未上报字段不要当作采集端契约。
24
25
 
25
26
  ## 0x01 用户真实监控模型
26
27
 
@@ -41,40 +42,56 @@ Session
41
42
  | 字段 | 状态 | 类型 | 描述 | 备注 |
42
43
  | ---------------- | -------------------------------------------------------------- | ------- | ------------------------------------------------------------------------------ | --------------------------------------------------------------------------- |
43
44
  | `time` | ![Backend](https://img.shields.io/badge/-backend-orange) | str | 数据上报时间(毫秒时间戳字符串) | -- |
44
- | `app_name` | ![Backend](https://img.shields.io/badge/-backend-orange) | str | 应用名称 | -- |
45
+ | `app_name` | ![Backend](https://img.shields.io/badge/-backend-orange) | str | 应用 | -- |
45
46
  | `bk_biz_id` | ![Backend](https://img.shields.io/badge/-backend-orange) | str | 业务 ID | -- |
46
47
  | `trace_id` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | Trace ID | -- |
47
- | `trace_state` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | Trace 状态 | -- |
48
- | `span_name` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | Span 名称 | -- |
48
+ | `trace_state` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | Trace 扩展信息 | -- |
49
+ | `span_name` *[1]* | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 可读的低基数 Span 名称 | 由 SDK 根据 `span_type` 和领域字段生成。 |
49
50
  | `span_id` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | Span ID | -- |
50
51
  | `parent_span_id` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 父 Span ID | -- |
51
52
  | `status` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | Status | Span 执行状态 | 包含 `code` 和 `message`。 |
52
- | `kind` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | enum | [Span 类型](https://opentelemetry.io/zh/docs/concepts/signals/traces/#span-kind) | 枚举值:<br>- 未定义:0<br>- 内部调用:1<br>- 同步被调:2<br>- 同步主调:3<br>- 异步主调:4<br>- 异步被调:5 |
53
+ | `kind` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | enum | [调用类型](https://opentelemetry.io/zh/docs/concepts/signals/traces/#span-kind) | 枚举值:<br>- 未定义:0<br>- 内部调用:1<br>- 同步被调:2<br>- 同步主调:3<br>- 异步主调:4<br>- 异步被调:5 |
53
54
  | `resource` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | object | 资源信息 | 服务、环境、SDK 等描述信息。 |
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。 |
61
+
62
+ **[1] `span_name`**:
63
+
64
+ | `attributes.span_type` | 生成规则 | 示例 |
65
+ | --- | --- | --- |
66
+ | `session` | `session.<attributes.session.phase>` | `session.start` |
67
+ | `view` | `attributes.view.name` | `订单详情` |
68
+ | `resource` | `<attributes.http.request.method> <attributes.url.template>` | `GET /api/orders/:id` |
69
+ | `action` | `attributes.action.name` | `click 提交订单` |
70
+ | `error` | 异常类型;白屏和 CSP 使用稳定分类 | `TypeError`、`blank_screen.empty_viewport`、`csp.script-src-elem` |
71
+ | `vital` | 大写的 `attributes.vital.metric` | `LCP` |
72
+ | `long_task` | `attributes.long_task.entry_type` | `long-animation-frame` |
73
+ | `websocket` | `websocket.<attributes.websocket.phase>` | `websocket.connect` |
74
+ | `custom` | `custom.<name>` | `custom.checkout_success` |
75
+
76
+ 名称不包含 ID、完整 URL、错误消息、时间戳或数值。业务传入的 View、Action 和 Custom 名称必须保持稳定。
60
77
 
61
78
  ### b. Resource
62
79
 
63
80
  | 字段 | 状态 | 类型 | 描述 | 备注 |
64
81
  | ---------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- | ------ | ----------- | ---------------------------------------------- |
65
- | `resource.service.name` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 服务名 | -- |
82
+ | `resource.service.name` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 服务 | -- |
66
83
  | `resource.service.version` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 版本 | -- |
67
84
  | [`resource.deployment.environment.name`](https://opentelemetry.io/docs/specs/semconv/resource/deployment-environment/) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 环境 | 如 `development`、`production`、`staging`、`test`。 |
68
- | `resource.session.sample_rate` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | Session 采样率 | 取值范围为 `0`~`1`。 |
85
+ | `resource.session.sample_rate` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | 会话采样率 | 取值范围为 `0`~`1`。 |
69
86
  | `resource.telemetry.sdk.version` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | SDK 版本 | -- |
70
- | [`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`。 |
73
- | `resource.user_agent.name` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 代理名称 | 通常指的是浏览器的名称,如 `Chrome`、`Edge`。 |
74
- | `resource.user_agent.version` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 代理版本 | 通常指的是浏览器的名称,如 `149`、`151`。 |
75
- | `resource.user_agent.os.name` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 操作系统名 | 如 `macOS`、`Windows`、`Android`。 |
87
+ | [`resource.telemetry.sdk.language`](https://opentelemetry.io/docs/specs/semconv/resource/#telemetry-sdk) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | enum | SDK 语言 | `webjs`。 |
88
+ | `resource.telemetry.sdk.name` *[1]* | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | SDK 名称 | -- |
89
+ | `resource.device.type` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | enum | 设备类型 | 当前仅为 `desktop`、`mobile`。 |
90
+ | `resource.user_agent.name` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 浏览器 | 通常指的是浏览器的名称,如 `Chrome`、`Edge`。 |
91
+ | `resource.user_agent.version` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 浏览器版本 | 通常指的是浏览器的名称,如 `149`、`151`。 |
92
+ | `resource.user_agent.os.name` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 操作系统 | 如 `macOS`、`Windows`、`Android`。 |
76
93
 
77
- **[1] `resource.telemetry.sdk.name`**:`blueking`(蓝鲸 Otel SDK,当前值为 @blueking/open-telemetry,需修改。)|aegis(Aegis SDK)。
94
+ **[1] `resource.telemetry.sdk.name`**:当前浏览器 SDK 固定为 `blueking`。Aegis 等其他生产端可使用各自名称,不要把 npm 包名 `@blueking/open-telemetry` 当作该字段值。
78
95
 
79
96
  ### c. Status
80
97
 
@@ -87,22 +104,22 @@ Session
87
104
 
88
105
  | 字段 | 状态 | 类型 | 描述 | 备注 |
89
106
  | ----------------------------------------------------------------------- | ---------------------------------------------------------- | ------ | ------------- | -------------------- |
90
- | `events[].name` *[1]* | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 事件名 | |
91
- | `events[].timestamp` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | int | 事件发生时间(微秒) | |
107
+ | `events[].name` *[1]* | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 事件 | |
108
+ | `events[].timestamp` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | int | 事件发生时间 | SDK 传输为 OTLP Event UnixNano;后端可转为微秒。 |
92
109
  | `events[].attributes.exception.type` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 异常类型 | 如 `TypeError`。 |
93
110
  | `events[].attributes.exception.message` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 异常消息 | 如 `Failed to fetch`。 |
94
111
  | `events[].attributes.exception.stacktrace` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 异常堆栈 | |
95
112
  | `events[].attributes.long_task.script.duration` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | 脚本耗时(ms) | |
96
113
  | `events[].attributes.long_task.script.execution_start` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | 脚本执行开始时间(ms) | |
97
114
  | `events[].attributes.long_task.script.forced_style_and_layout_duration` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | 强制样式与布局耗时(ms) | |
98
- | `events[].attributes.long_task.script.pause_duration` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | 暂停耗时(ms) | |
115
+ | `events[].attributes.long_task.script.pause_duration` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | 脚本暂停耗时(ms) | |
99
116
  | `events[].attributes.long_task.script.source_char_position` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | 源码字符位置 | |
100
117
  | `events[].attributes.long_task.script.start_time` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | 脚本开始时间(ms) | |
101
- | `events[].attributes.long_task.script.invoker` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 调用方 | |
102
- | `events[].attributes.long_task.script.invoker_type` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 调用方类型 | |
103
- | `events[].attributes.long_task.script.source_function_name` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 源函数名 | |
118
+ | `events[].attributes.long_task.script.invoker` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 脚本触发来源 | |
119
+ | `events[].attributes.long_task.script.invoker_type` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 脚本触发类型 | |
120
+ | `events[].attributes.long_task.script.source_function_name` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 脚本函数名 | |
104
121
  | `events[].attributes.long_task.script.source_url` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 已脱敏的脚本 URL | |
105
- | `events[].attributes.long_task.script.window_attribution` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | Window 归因 | |
122
+ | `events[].attributes.long_task.script.window_attribution` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 脚本窗口关系 | |
106
123
 
107
124
  **[1] `events[].name`**:「异常」需将事件名固定为 `exception`。
108
125
 
@@ -115,7 +132,7 @@ Session
115
132
  | `links[].trace_id` | ![Development](https://img.shields.io/badge/-development-blue) | str | 关联 TraceID | |
116
133
  | `links[].span_id` | ![Development](https://img.shields.io/badge/-development-blue) | str | 关联 SpanID | |
117
134
 
118
- **[1] `links[].trace_id`**:满足 `attributesspan_type=resource, attributes.resource.type=xhr or fetch` 时,需将前端生成的 TraceID 记录到该字段。
135
+ **[1] `links[].trace_id`**:`span_type=resource` 且 `resource.type` 为 `xhr` 或 `fetch`,并注入/探测到 Trace Header 时,把对应后端 Trace ID 记到 Link 上。
119
136
 
120
137
 
121
138
  ## 0x03 Attributes
@@ -125,8 +142,8 @@ Session
125
142
  | 字段 | 状态 | 类型 | 描述 | 备注 |
126
143
  | ---------------------------- | ---------------------------------------------------------- | ---- | ------- | ---------------------------------------------- |
127
144
  | `attributes.user.id` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 用户 ID | -- |
128
- | `attributes.span_type` *[1]* | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | enum | Span 类型 | -- |
129
- | `attributes.outcome.type` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | enum | 执行结果 | `success`、`warning`、`error`、`timeout`、`abort`。 |
145
+ | `attributes.span_type` *[1]* | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | enum | 事件类型 | -- |
146
+ | `attributes.outcome.type` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | enum | 执行结果 | `success`、`warning`(警告)、`error`、`timeout`、`abort`。 |
130
147
 
131
148
 
132
149
  **[1] `attributes.span_type`**:
@@ -137,9 +154,9 @@ Session
137
154
  | `view` | 页面首次加载或路由视图生命周期。 |
138
155
  | `resource` | 静态资源、Fetch 或 XHR 请求。 |
139
156
  | `error` | 浏览器错误、白屏或 CSP 违规。 |
140
- | `vital` | Web Vitals 指标。 |
157
+ | `vital` | Web 性能指标。 |
141
158
  | `long_task` | Long Task 或长动画帧。 |
142
- | `action` | 用户交互或主动上报的 Action。 |
159
+ | `action` | 用户操作或主动上报的 Action。 |
143
160
  | `websocket` | WebSocket 生命周期事件。 |
144
161
  | `custom` | 主动上报的自定义事件。 |
145
162
 
@@ -150,7 +167,7 @@ Session
150
167
  | ------------------------------- | ----------------------------------------------------------- | ------- | ---------- | -------------------------------------------------------------------- |
151
168
  | `attributes.error.message` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 错误信息 | -- |
152
169
  | `attributes.error.handled` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | boolean | 错误是否被捕获 | -- |
153
- | `attributes.error.source` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 错误来源 | 枚举值:<br/>- window.error(固定值)<br/>- resource<br/>- unhandledrejection |
170
+ | `attributes.error.source` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 错误来源 | 枚举值:<br/>- window.error(脚本错误,固定值)<br/>- resource(资源加载错误)<br/>- unhandledrejection(未处理的 Promise 异常) |
154
171
  | `attributes.code.column` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | int | 代码列号 | |
155
172
  | `attributes.code.filepath` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 代码文件路径 | |
156
173
  | `attributes.code.lineno` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | int | 代码行号 | |
@@ -161,8 +178,8 @@ Session
161
178
  | ------------------------------------ | ---------------------------------------------------------- | --- | ------- | ---------------------------- |
162
179
  | `attributes.browser.screen.height` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | int | 屏幕尺寸的高度 | Aegis 使用 `sr = 1728 * 1117`。 |
163
180
  | `attributes.browser.screen.width` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | int | 屏幕尺寸的宽度 | -- |
164
- | `attributes.browser.viewport.height` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | int | 视口尺寸的高度 | Aegis 使用 `vp = 576 * 918`。 |
165
- | `attributes.browser.viewport.width` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | int | 视口尺寸的宽度 | -- |
181
+ | `attributes.browser.viewport.height` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | int | 可视区域高度 | Aegis 使用 `vp = 576 * 918`。 |
182
+ | `attributes.browser.viewport.width` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | int | 可视区域宽度 | -- |
166
183
 
167
184
  ### d. [device](https://opentelemetry.io/docs/specs/semconv/registry/attributes/device/)
168
185
 
@@ -174,10 +191,10 @@ Session
174
191
 
175
192
  | 字段 | 状态 | 类型 | 描述 | 备注 |
176
193
  | ------------------------------------ | -------------------------------------------------------------- | ---- | ------- | ----------------------------------------------------------- |
177
- | `attributes.network.connection.type` | ![Development](https://img.shields.io/badge/-development-blue) | enum | 连接类型 | 直接使用浏览器 `navigator.connection.type` 的原始值;字段不存在时不上报。 |
178
- | `attributes.network.effective_type` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 有效网络质量 | -- |
179
- | `attributes.network.status` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | enum | 网络连接状态 | `connected`、`not_connected`。 |
180
- | `attributes.network.protocol.name` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | enum | 应用层网络协议 | 仅 WebSocket Span 上报,固定为 `websocket`;HTTP Resource Span 不上报。 |
194
+ | `attributes.network.connection.type` | ![Development](https://img.shields.io/badge/-development-blue) | enum | 网络连接类型 | 直接使用浏览器 `navigator.connection.type` 的原始值;字段不存在时不上报。 |
195
+ | `attributes.network.effective_type` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 网络质量 | -- |
196
+ | `attributes.network.status` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | enum | 网络状态 | `connected`、`not_connected`。 |
197
+ | `attributes.network.protocol.name` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | enum | 应用层协议 | 仅 WebSocket Span 上报,固定为 `websocket`;HTTP Resource Span 不上报。 |
181
198
 
182
199
  **[1] `attributes.network.connection.type`**:表示设备当前使用的物理网络连接方式。SDK 直接上报
183
200
  [`navigator.connection.type`](https://wicg.github.io/netinfo/#connectiontype-enum);浏览器不支持该 API
@@ -202,19 +219,19 @@ Aegis 将「连接类型」「网络质量」整合成 `netType`(`wifi`、`wir
202
219
 
203
220
  | 值 | 描述 | 备注 |
204
221
  | --------- | ---- | -------------------------- |
205
- | `slow-2g` | 极慢网络 | 页面和图片加载非常慢,通常只能满足少量文本传输。 |
206
- | `2g` | 较慢网络 | 简单页面可以打开,图片、脚本和接口请求可能明显缓慢。 |
207
- | `3g` | 中等网络 | 普通网页基本可用,但大资源和复杂页面仍可能等待。 |
208
- | `4g` | 较快网络 | 延迟和带宽表现较好,适合大多数 Web 应用。 |
222
+ | `slow-2g` | 极慢 | 页面和图片加载非常慢,通常只能满足少量文本传输。 |
223
+ | `2g` | 较慢 | 简单页面可以打开,图片、脚本和接口请求可能明显缓慢。 |
224
+ | `3g` | 中等 | 普通网页基本可用,但大资源和复杂页面仍可能等待。 |
225
+ | `4g` | 较快 | 延迟和带宽表现较好,适合大多数 Web 应用。 |
209
226
 
210
227
  ### f. session
211
228
 
212
229
  | 字段 | 状态 | 类型 | 描述 | 备注 |
213
230
  | ------------------------------------- | ----------------------------------------------------------- | ------- | -------- | ----------------------------------------------- |
214
- | `attributes.session.has_replay` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | boolean | 是否回放 | -- |
231
+ | `attributes.session.has_replay` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | boolean | 有无回放 | -- |
215
232
  | `attributes.session.id` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 会话 ID | -- |
216
233
  | `attributes.session.type` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | enum | 会话类型 | 当前固定为 `user`。 |
217
- | `attributes.session.phase` *[1]* | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | enum | 会话生命周期阶段 | -- |
234
+ | `attributes.session.phase` *[1]* | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | enum | 会话阶段 | -- |
218
235
 
219
236
  **[1] `attributes.session.phase`**:
220
237
 
@@ -228,36 +245,36 @@ Aegis 将「连接类型」「网络质量」整合成 `netType`(`wifi`、`wir
228
245
 
229
246
  | 字段 | 状态 | 类型 | 描述 | 备注 |
230
247
  | ------------------------------------- | -------------------------------------------------------------- | ------ | ----------------------- | --------------------------------------------- |
231
- | `attributes.view.id` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 视图 ID | |
232
- | `attributes.view.name` *[1]* | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 视图名称 | 如 `/apm/home`。 |
233
- | `attributes.view.loading_type` *[2]* | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 视图加载类型 | -- |
234
- | `attributes.view.url` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 视图 URL | 如 `https://example.com/cur`。 |
235
- | `attributes.view.previous` | ![Development](https://img.shields.io/badge/-development-blue) | str | 视图 URL(前一个) | 如 `https://example.com/pre`。 |
236
- | `attributes.view.previous_url_template` | ![Development](https://img.shields.io/badge/-development-blue) | str | 前序视图路径模板 | 与 `view.url_template` 使用同一规则;初始 View 为空字符串。 |
237
- | `attributes.view.referrer` | ![Development](https://img.shields.io/badge/-development-blue) | str | 初始来源页面 URL | 仅首次加载且值非空时上报,需进行 URL 脱敏。 |
238
- | `attributes.view.url_template` *[3]* | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 视图路径分组 | 如 `/apm/home`。 |
239
- | `attributes.view.loading_time` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | 视图加载耗时(ms) | 加载时间计算完成后上报。 |
240
- | `attributes.view.loading_time_source` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | enum | 视图加载耗时来源 | `auto`、`manual`。 |
241
- | `attributes.view.first_byte` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | 首字节时间(ms) | 仅初始导航存在。 |
248
+ | `attributes.view.id` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 页面 ID | |
249
+ | `attributes.view.name` *[1]* | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 页面名称 | 如 `/apm/home`。 |
250
+ | `attributes.view.loading_type` *[2]* | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 页面加载类型 | -- |
251
+ | `attributes.view.url` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 页面 URL | 如 `https://example.com/cur`。 |
252
+ | `attributes.view.previous` | ![Development](https://img.shields.io/badge/-development-blue) | str | 上一页面 URL | 如 `https://example.com/pre`。 |
253
+ | `attributes.view.previous_url_template` | ![Development](https://img.shields.io/badge/-development-blue) | str | 上一个页面 | 与 `view.url_template` 使用同一规则;初始 View 为空字符串。 |
254
+ | `attributes.view.referrer` | ![Development](https://img.shields.io/badge/-development-blue) | str | 初始来源 URL | 仅首次加载且值非空时上报,需进行 URL 脱敏。 |
255
+ | `attributes.view.url_template` *[3]* | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 页面 | 如 `/apm/home`。 |
256
+ | `attributes.view.loading_time` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | 页面加载耗时(ms) | 加载时间计算完成后上报。 |
257
+ | `attributes.view.loading_time_source` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | enum | 页面加载计时方式 | `auto`、`manual`。 |
258
+ | `attributes.view.first_byte` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | 页面首字节耗时(ms) | 仅初始导航存在。 |
242
259
  | `attributes.view.dom_interactive` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | DOM 可交互时间(ms) | 仅初始导航存在。 |
243
260
  | `attributes.view.dom_content_loaded` | ![Stable\|43](https://img.shields.io/badge/-stable-lightgreen) | number | DOMContentLoaded 时间(ms) | 仅初始导航存在。 |
244
- | `attributes.view.dom_complete` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | DOM Complete 时间(ms) | 仅初始导航存在。 |
245
- | `attributes.view.load_event` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | Load Event 时间(ms) | 仅初始导航存在。 |
246
- | `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.version` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | int | 视图事件版本号 | 同一 `attributes.view.id` 从 `1` 开始递增,用于排序和幂等合并。 |
249
- | `attributes.view.end_reason` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 结束原因 | -- |
261
+ | `attributes.view.dom_complete` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | DOM 完成时间(ms) | 仅初始导航存在。 |
262
+ | `attributes.view.load_event` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | Load 事件时间(ms) | 仅初始导航存在。 |
263
+ | `attributes.view.phase` *[4]* | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | enum | 页面阶段 | 枚举值:<br>- start<br>- update<br>- end |
264
+ | `attributes.view.started_at` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | 页面开始时间(ms) | 初始 View 取 `performance.timeOrigin`(epoch ms);后续 View 取创建时刻。 |
265
+ | `attributes.view.version` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | int | 页面快照版本 | 同一 `attributes.view.id` 从 `1` 开始递增,用于排序和幂等合并。 |
266
+ | `attributes.view.end_reason` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 页面结束原因 | -- |
250
267
 
251
- **[1] `attributes.view.name`**:仅由 `startView({ name })` 或 `setViewName` 显式设置;未设置时,展示层可回退到 `view.url_template`。
268
+ **[1] `attributes.view.name`**:优先使用 `startView({ name })` 或 `setViewName()` 设置的名称;未显式设置时,默认取 `attributes.view.url_template`。
252
269
 
253
270
  **[2] `attributes.view.loading_type`**:
254
271
 
255
272
  | 值 | 描述 |
256
273
  | ----------------- | --------------- |
257
- | `initial_load` | 首次页面加载(初始导航) |
258
- | `route_change` | SPA 路由切换 |
274
+ | `initial_load` | 初始加载 |
275
+ | `route_change` | 路由切换 |
259
276
  | `session_renewal` | 会话续期后重建 |
260
- | `bf_cache` | 从浏览器 BFCache 恢复 |
277
+ | `bf_cache` | 前进后退缓存恢复 |
261
278
 
262
279
  **[3] `attributes.view.url_template`**:用于聚合的页面路径。默认把数字 ID、UUID 和长十六进制段替换为 `:id`,也可由 `tracking.view.getUrlTemplate` 自定义。
263
280
 
@@ -331,64 +348,102 @@ View B █████████████
331
348
  | `attributes.resource.type` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | enum | 资源类型 | -- |
332
349
  | `attributes.resource.size` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | int | 资源大小 | -- |
333
350
  | `attributes.resource.transfer_size` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | int | 传输大小 | -- |
334
- | `attributes.resource.decoded_body_size` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | int | 解码后正文大小 | -- |
335
- | `attributes.resource.encoded_body_size` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | int | 编码后正文大小 | -- |
336
- | `attributes.resource.protocol` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 传输协议 | 浏览器提供下一跳协议时存在,如 `h2`、`h3`、`http/1.1`。 |
337
- | `attributes.resource.cache.hit` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | boolean | 是否缓存命中 | -- |
338
- | `attributes.resource.delivery_type` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 交付类型 | 浏览器提供交付类型时存在,如 `cache`、`navigational-prefetch`、`cache-storage`、`other`。 |
339
- | `attributes.resource.render_blocking_status` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | enum | 渲染阻塞状态 | 浏览器提供渲染阻塞状态时存在,枚举值为 `blocking`、`non-blocking` |
351
+ | `attributes.resource.decoded_body_size` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | int | 解压后大小 | -- |
352
+ | `attributes.resource.encoded_body_size` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | int | 压缩后大小 | -- |
353
+ | `attributes.resource.protocol` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 资源协议 | 浏览器提供下一跳协议时存在,如 `h2`、`h3`、`http/1.1`。 |
354
+ | `attributes.resource.cache.hit` *[3]* | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | boolean | 缓存命中 | 命中时为 `true`,未命中或无法判断时不写入。 |
355
+ | `attributes.resource.delivery_type` *[4]* | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 资源获取方式 | 浏览器返回非空值时存在。 |
356
+ | `attributes.resource.render_blocking_status` *[5]* | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | enum | 渲染阻塞状态 | 浏览器支持该字段时存在。 |
340
357
  | `attributes.resource.redirect.start` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | 重定向开始时间 | -- |
341
358
  | `attributes.resource.redirect.duration` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | 重定向耗时 | -- |
342
359
  | `attributes.resource.worker.start` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | Service Worker 开始时间 | -- |
343
360
  | `attributes.resource.worker.duration` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | Service Worker 耗时 | -- |
344
- | `attributes.resource.dns.start` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | DNS 查询开始时间 | -- |
345
- | `attributes.resource.dns.duration` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | DNS 查询耗时 | -- |
346
- | `attributes.resource.connect.start` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | 传输连接开始时间 | -- |
347
- | `attributes.resource.connect.duration` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | 传输连接耗时 | -- |
361
+ | `attributes.resource.dns.start` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | DNS 解析开始时间 | -- |
362
+ | `attributes.resource.dns.duration` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | DNS 解析耗时 | -- |
363
+ | `attributes.resource.connect.start` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | 连接开始时间 | -- |
364
+ | `attributes.resource.connect.duration` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | 连接耗时(含 TLS) | -- |
348
365
  | `attributes.resource.ssl.start` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | TLS 握手开始时间 | -- |
349
366
  | `attributes.resource.ssl.duration` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | TLS 握手耗时 | -- |
350
- | `attributes.resource.first_byte.start` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | 首字节阶段开始时间 | -- |
351
- | `attributes.resource.first_byte.duration` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | 首字节耗时 | -- |
352
- | `attributes.resource.download.start` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | 下载开始时间 | -- |
353
- | `attributes.resource.download.duration` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | 下载耗时 | -- |
367
+ | `attributes.resource.first_byte.start` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | 首字节等待开始时间 | -- |
368
+ | `attributes.resource.first_byte.duration` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | 首字节等待耗时 | -- |
369
+ | `attributes.resource.download.start` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | 内容下载开始时间 | -- |
370
+ | `attributes.resource.download.duration` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | 内容下载耗时 | -- |
354
371
 
355
372
  **[1] `attributes.resource.type`**:
356
373
 
357
374
  | 值 | 描述 |
358
375
  | ------------------ | -------------------------------------------- |
359
- | `fetch` | Fetch API 请求。 |
360
- | `xhr` | XMLHttpRequest 请求。 |
361
- | `script` | `<script>`、模块或 Worker 加载的脚本资源。 |
362
- | `link` | `<link>` 加载的资源,如样式表、预加载和预取资源。 |
363
- | `img` / `image` | HTML 或 SVG 图片元素加载的资源。 |
364
- | `css` | CSS `url()`、`@import` 等规则加载的资源,不表示 CSS 文件本身。 |
376
+ | `fetch` | Fetch 请求。 |
377
+ | `xhr` | XHR 请求。 |
378
+ | `script` | 脚本加载:`<script>`、模块或 Worker 加载的脚本资源。 |
379
+ | `link` | Link 标签加载:`<link>` 加载的资源,如样式表、预加载和预取资源。 |
380
+ | `img` / `image` | 图片:HTML 或 SVG 图片元素加载的资源。 |
381
+ | `css` | CSS 规则加载:CSS `url()`、`@import` 等规则加载的资源,不表示 CSS 文件本身。 |
365
382
  | `iframe` / `frame` | 内嵌页面加载的文档资源。 |
366
- | `other` | 浏览器未识别发起方式、`initiatorType` 为空或未单列的其他类型。 |
383
+ | `other` | 其他:浏览器未识别发起方式、`initiatorType` 为空或未单列的其他类型。 |
367
384
  | ... | .. |
368
385
 
369
386
  **[2] 单位**:`xx_size` 为 bytes,`duration` & `start` 为毫秒。
370
387
 
371
- **[3] 阶段图**:产品阶段划分见 [0x04 资源加载时序](#0x04-资源加载时序)。
388
+ **[3] `attributes.resource.cache.hit`**
389
+
390
+ 满足任一条件时,SDK 写入 `true`:
391
+
392
+ | 条件 | 结果 |
393
+ | --- | --- |
394
+ | `deliveryType=cache` | `true` |
395
+ | `transferSize=0 && decodedBodySize>0` | `true` |
396
+ | 其他 | 不写入,不写 `false` |
397
+
398
+ ETag(实体标签)是服务器提供的资源版本标识。浏览器按以下流程校验缓存:
399
+
400
+ ```text
401
+ 缓存 ETag → If-None-Match → 304 → 复用本地正文
402
+ ```
403
+
404
+ `deliveryType=cache` 与 `transferSize>0` 不冲突。[Resource Timing](https://www.w3.org/TR/resource-timing/) 把 `validated` 缓存的 `transferSize` 记为 `300`,不是正文大小。
405
+
406
+ **[4] `attributes.resource.delivery_type`**:
407
+
408
+ | 常见值 | 描述 |
409
+ | ----------------------- | ---------------------------------------------------------- |
410
+ | `cache` | 由浏览器 HTTP 缓存交付,包括本地缓存和网络校验缓存。 |
411
+ | `navigational-prefetch` | 由导航预取缓冲区交付。 |
412
+ | `cache-storage` | 由 Cache Storage 交付,包括 Service Worker Fetch Handler 和静态路由命中。 |
413
+
414
+ 规范来源:[Resource Timing](https://www.w3.org/TR/resource-timing/)、[Prefetch](https://wicg.github.io/nav-speculation/prefetch.html)、[Chromium Cache Storage Timing](https://chromium.googlesource.com/chromium/src/+/lkgr/docs/experiments/service-worker-static-routing-api-timing-info.md)。
415
+
416
+ SDK 原样上报浏览器返回的非空值,空值不写入。
417
+
418
+ **[5] `attributes.resource.render_blocking_status`**:
419
+
420
+ | 值 | 描述 |
421
+ | -------------- | ----------- |
422
+ | `blocking` | 可能阻塞渲染。 |
423
+ | `non-blocking` | 不阻塞渲染。 |
424
+
425
+ 取值定义见 [Resource Timing](https://www.w3.org/TR/resource-timing/)。浏览器不支持该字段时,SDK 不写入。
372
426
 
373
427
  ### i. action
374
428
 
375
429
  | 字段 | 状态 | 类型 | 描述 |
376
430
  | ------------------------------------------ | ---------------------------------------------------------- | ----- | ------ |
377
- | `attributes.action.id` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | -- |
378
- | `attributes.action.type` *[2]* | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 动作类型 |
379
- | `attributes.action.target.name` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 目标元素名称 |
380
- | `attributes.action.target.tag` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 目标元素标签 |
381
- | `attributes.action.frustration.type` *[3]* | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str[] | 挫败类型 |
431
+ | `attributes.action.id` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 操作 ID |
432
+ | `attributes.action.name` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 可读的低基数动作名称,也是 `span_name` |
433
+ | `attributes.action.type` *[2]* | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 操作类型 |
434
+ | `attributes.action.target.name` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 操作元素 |
435
+ | `attributes.action.target.tag` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 操作元素标签 |
436
+ | `attributes.action.frustration.type` *[3]* | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str[] | 异常点击类型 |
382
437
 
383
438
 
384
- **[1] `attributes.action.loading_time`**:`loading_time` 可直接使用外层 `elapsed_time`。
439
+ **[1] `attributes.action.loading_time`**:当前浏览器 SDK 不写该 Attribute。有页面活动时,用 Span 起止时间(后端 `elapsed_time`)表达持续时长。
385
440
 
386
441
  **[2] `attributes.action.type`**:
387
442
 
388
443
  | 常见值 | 描述 |
389
444
  | -------------------- | ---------------------------------- |
390
445
  | `click` | 鼠标或指针设备触发的点击,蓝鲸 Web 自动 Action 当前使用该值。 |
391
- | `custom` | 通过 SDK API 主动上报的自定义 Action。 |
446
+ | `custom` | 通过 `reportAction({ name })` 主动上报;Span 名使用 `action.name`。 |
392
447
  | `tap` | 触屏设备上的单次轻触。 |
393
448
  | `scroll` | 页面或滚动容器的滚动操作。 |
394
449
  | `swipe` | 触屏设备上的滑动手势。 |
@@ -399,11 +454,11 @@ View B █████████████
399
454
 
400
455
  **[3] `attributes.action.frustration.type`**:
401
456
 
402
- | 值 | 描述 |
403
- | ------------- | ------------------------------------------------------------------------------------ |
404
- | `rage_click` | [a] `1 s` 内对同一 DOM 元素连续点击至少 `3` 次,且相邻点击距离不超过 `100 px`。<br />[b] 滚动或文本选择会中断或撤销判定。 |
405
- | `dead_click` | [a] 点击后未检测到请求、资源加载、长任务、DOM 变化、打开窗口或错误,也没有输入、滚动或文本选择。<br />[b] 链接、文本输入框等难以可靠判断的目标会排除。 |
406
- | `error_click` | Action 生命周期内检测到前端错误。 |
457
+ | 值 | 描述 | 备注 |
458
+ | ------------- | ---- | ------------------------------------------------------------------------------------ |
459
+ | `rage_click` | 愤怒点击 | [a] `1 s` 内对同一 DOM 元素连续点击至少 `3` 次,且相邻点击距离不超过 `100 px`。<br />[b] 滚动或文本选择会中断或撤销判定。 |
460
+ | `dead_click` | 无响应点击 | [a] 点击后未检测到请求、资源加载、长任务、DOM 变化、打开窗口或错误,也没有输入、滚动或文本选择。<br />[b] 链接、文本输入框等难以可靠判断的目标会排除。 |
461
+ | `error_click` | 报错点击 | Action 生命周期内检测到前端错误。 |
407
462
 
408
463
  该字段为数组,同一个 Action 可以同时命中多个值;未命中时不写入。消费端应按集合处理,不依赖数组顺序。这些信号是 SDK 的启发式判断,不等同于业务操作失败。
409
464
 
@@ -412,47 +467,47 @@ View B █████████████
412
467
  | 字段 | 状态 | 类型 | 描述 | |
413
468
  | -------------------------------------- | ---------------------------------------------------------- | --- | ------ | ---------------------------- |
414
469
  | `attributes.url.full` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 完整 URL | 如 `https://example.com/apm`。 |
415
- | `attributes.url.scheme` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 协议 | 如 `ws`、`wss`。 |
416
- | `attributes.url.template` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 请求路径 | 如 `/api/users/`。 |
417
- | `attributes.http.request.method` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 请求方法 | 如 `GET`、`POST`。 |
418
- | `attributes.http.response.status_code` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | int | 返回码 | 如 `200`、`404`。 |
419
- | `attributes.server.address` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 地址 | 如 `example.com`。 |
420
- | `attributes.server.port` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | int | 端口 | -- |
470
+ | `attributes.url.scheme` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | URL 协议 | 如 `ws`、`wss`。 |
471
+ | `attributes.url.template` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 资源路径 | 如 `/api/users/`。 |
472
+ | `attributes.http.request.method` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 请求方法 | Fetch/XHR 使用实际方法;其他 Resource 固定为 `GET`。 |
473
+ | `attributes.http.response.status_code` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | int | HTTP 状态码 | 如 `200`、`404`。 |
474
+ | `attributes.server.address` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 服务器地址 | 如 `example.com`。 |
475
+ | `attributes.server.port` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | int | 服务器端口 | -- |
421
476
 
422
477
  ### k. vital
423
478
 
424
479
  | 字段 | 状态 | 类型 | 描述 | 备注 |
425
480
  | --------------------------------------------- | ---------------------------------------------------------- | ------ | -------------------- | ------------------------------------ |
426
- | `attributes.vital.id` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | Vital 唯一标识 | |
427
- | `attributes.vital.metric` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | enum | 指标名 | |
428
- | `attributes.vital.value` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | 指标测量值 | |
481
+ | `attributes.vital.id` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 指标 ID | |
482
+ | `attributes.vital.metric` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | enum | 指标 | |
483
+ | `attributes.vital.value` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | 指标值 | |
429
484
  | `attributes.vital.inp.input_delay` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | 输入延迟(ms) | 用户发起交互(如点击)到事件处理器开始执行的等待时间,反映主线程繁忙程度 |
430
- | `attributes.vital.inp.interaction_target` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 交互目标元素标识 | 如 `div`。 |
485
+ | `attributes.vital.inp.interaction_target` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 交互目标元素 | 如 `div`。 |
431
486
  | `attributes.vital.inp.interaction_type` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 交互类型 | -- |
432
- | `attributes.vital.inp.processing_duration` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | 处理耗时(ms) | -- |
487
+ | `attributes.vital.inp.processing_duration` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | 交互处理耗时(ms) | -- |
433
488
  | `attributes.vital.inp.presentation_delay` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | 呈现延迟(ms) | -- |
434
- | `attributes.vital.lcp.target` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | DOM 选择器 | 如 `div`。 |
435
- | `attributes.vital.lcp.url` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 元素对应资源 URL(已脱敏) | -- |
436
- | `attributes.vital.lcp.resource_load_duration` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | 资源加载耗时(ms) | -- |
437
- | `attributes.vital.lcp.element_render_delay` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | 元素渲染延迟(ms) | -- |
438
- | `attributes.vital.ttfb.waiting_duration` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | 请求就绪后的等待耗时(ms) | -- |
439
- | `attributes.vital.ttfb.dns_duration` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | DNS 解析耗时(ms) | -- |
440
- | `attributes.vital.ttfb.connection_duration` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | TCP + TLS 连接建立耗时(ms) | -- |
441
- | `attributes.vital.ttfb.request_duration` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | 请求发送后等待首字节耗时(ms) | -- |
489
+ | `attributes.vital.lcp.target` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | LCP 元素选择器 | 如 `div`。 |
490
+ | `attributes.vital.lcp.url` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | LCP 资源 URL(已脱敏) | -- |
491
+ | `attributes.vital.lcp.resource_load_duration` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | LCP 资源加载耗时(ms) | -- |
492
+ | `attributes.vital.lcp.element_render_delay` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | LCP 元素渲染延迟(ms) | -- |
493
+ | `attributes.vital.ttfb.waiting_duration` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | 请求准备耗时(ms) | -- |
494
+ | `attributes.vital.ttfb.dns_duration` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | DNS 阶段耗时(ms) | -- |
495
+ | `attributes.vital.ttfb.connection_duration` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | 连接耗时(含 TLS)(ms) | -- |
496
+ | `attributes.vital.ttfb.request_duration` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | 请求阶段耗时(ms) | -- |
442
497
 
443
498
  **[1] `attributes.vital.metric`**:
444
499
 
445
500
  | 值 | 描述 | 备注 |
446
501
  | ---- | --------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
447
- | cls | [累积布局偏移(Cumulative Layout Shift)](https://web.dev/articles/cls) | 在整个页面生命周期中,每次出现的意外布局变化所导致的最大布局变化得分的累积值,为了提供良好的用户体验,网站的 CLS 得分应控制在 0.1 或更低。 |
448
- | inp | [交互到下一次绘制(Interaction to Next Paint)](https://web.dev/articles/inp) | 记录用户与页面进行的所有交互的延迟情况,并给出一个综合评分(通常是 P98,忽略极端值),较低的 INP 值意味着页面能够快速响应所有或绝大多数用户交互,INP 值在用户离开页面时会被计算出来。 |
449
- | lcp | [最大内容绘制(Largest Contentful Paint, LCP)](https://web.dev/articles/lcp) | Core Web Vital 中的一个重要且稳定的指标,测量从页面开始加载,到视窗中可见的最大图像、文本块或视频的渲染时间,用于衡量页面加载的速度(用户感受:页面主要内容出来了)。 |
450
- | fcp | [首次内容绘制(First Contentful Paint)](https://web.dev/articles/fcp) | 从用户首次访问页面,到页面第一次显示出任何内容的耗时(用户感受:不是白屏了)。 |
451
- | ttfb | [首字节时间(Time to First Byte)](https://web.dev/articles/ttfb) | 从用户开始导航到页面到响应首字节开始传输之间所经历的时间。 |
502
+ | cls | [CLS(累积布局偏移)](https://web.dev/articles/cls) | 在整个页面生命周期中,每次出现的意外布局变化所导致的最大布局变化得分的累积值,为了提供良好的用户体验,网站的 CLS 得分应控制在 0.1 或更低。 |
503
+ | inp | [INP(交互到下一次绘制)](https://web.dev/articles/inp) | 记录用户与页面进行的所有交互的延迟情况,并给出一个综合评分(通常是 P98,忽略极端值),较低的 INP 值意味着页面能够快速响应所有或绝大多数用户交互,INP 值在用户离开页面时会被计算出来。 |
504
+ | lcp | [LCP(最大内容绘制)](https://web.dev/articles/lcp) | Core Web Vital 中的一个重要且稳定的指标,测量从页面开始加载,到视窗中可见的最大图像、文本块或视频的渲染时间,用于衡量页面加载的速度(用户感受:页面主要内容出来了)。 |
505
+ | fcp | [FCP(首次内容绘制)](https://web.dev/articles/fcp) | 从用户首次访问页面,到页面第一次显示出任何内容的耗时(用户感受:不是白屏了)。 |
506
+ | ttfb | [TTFB(首字节耗时)](https://web.dev/articles/ttfb) | 从用户开始导航到页面到响应首字节开始传输之间所经历的时间。 |
452
507
 
453
508
  **[2] `attributes.vital.value`**:CLS 单位为 `1`,FCP、INP、LCP、TTFB 单位为 `ms`。
454
509
 
455
- **[3] `attributes.vital.cls.largest_shift_value` & `attributes.vital.cls.largest_shift_target`**:Aegis & DataDog 无采集,废弃。
510
+ **[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
511
 
457
512
  **[4] `attributes.vital.inp.interaction_type`**:交互类型。
458
513
 
@@ -460,8 +515,8 @@ View B █████████████
460
515
  | ------------- | ------------- |
461
516
  | `keyup` | 松开按键 |
462
517
  | `keydown` | 按下按键 |
463
- | `pointerdown` | 点击(鼠标、触屏、触控笔) |
464
- | `pointerup` | 松开 |
518
+ | `pointerdown` | 指针按下 |
519
+ | `pointerup` | 指针松开 |
465
520
 
466
521
  **[5] `attributes.vital.inp.processing_duration`**:事件处理回调(如 click handler)本身的执行时间,JS 逻辑过重时这个值会变大。
467
522
 
@@ -471,7 +526,7 @@ View B █████████████
471
526
 
472
527
  **[8] `attributes.vital.lcp.resource_load_duration`**:LCP 元素依赖的外部资源(图片/字体等)从请求到下载完成的时间(如果 LCP 是纯文本节点,此项可能缺失),用于排查 CDN/网络/资源体积问题。
473
528
 
474
- **[9] `attributes.vital.lcp.time_to_first_byte`**:从导航开始到收到首字节的耗时(LCP ≈ `time_to_first_byte` + `resource_load_duration` + `element_render_delay`)。
529
+ **[9] `attributes.vital.lcp.time_to_first_byte`**:当前浏览器 SDK 不上报该字段。LCP 归因只包含 url / target / element_render_delay / resource_load_duration。
475
530
 
476
531
  **[10] `attributes.vital.ttfb.waiting_duration`**:页面激活到浏览器开始 Fetch,或开始启动 Service Worker 前的时间(包含重定向及浏览器前置处理)。
477
532
 
@@ -488,8 +543,8 @@ View B █████████████
488
543
  | 字段 | 状态 | 类型 | 描述 | 备注 |
489
544
  | ---------------------------------------------- | --------------------------------------------------------------- | ------- | ---------------- | ------------------ |
490
545
  | `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_sample_count` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | int | 空白采样数 | |
546
+ | `attributes.blank_screen.empty_ratio` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | 空白点占比 | 回退根节点检测时仍为实际比例,不是固定 `1`。 |
547
+ | `attributes.blank_screen.empty_sample_count` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | int | 空白点数量 | |
493
548
 
494
549
  **[1] `attributes.blank_screen.reason`**:
495
550
 
@@ -503,10 +558,10 @@ View B █████████████
503
558
  | 字段 | 状态 | 类型 | 描述 |
504
559
  | ----------------------------------------------- | --------------------------------------------------------------- | ------ | -------------------- |
505
560
  | `attributes.long_task.id` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 长任务或长动画帧 ID |
506
- | `attributes.long_task.name` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | Performance Entry 名称 |
507
- | `attributes.long_task.entry_type` *[1]* | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 采集条目类型 |
561
+ | `attributes.long_task.name` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 长任务名称 |
562
+ | `attributes.long_task.entry_type` *[1]* | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | str | 长任务类型 |
508
563
  | `attributes.long_task.blocking_duration` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | 主线程阻塞耗时(ms) |
509
- | `attributes.long_task.first_ui_event_timestamp` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | 首个 UI 事件时间(ms) |
564
+ | `attributes.long_task.first_ui_event_timestamp` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | 首个交互事件时间(ms) |
510
565
  | `attributes.long_task.render_start` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | 渲染阶段开始时间(ms) |
511
566
  | `attributes.long_task.style_and_layout_start` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | number | 样式与布局阶段开始时间(ms) |
512
567
 
@@ -519,149 +574,4 @@ View B █████████████
519
574
 
520
575
  ### n. custom
521
576
 
522
- `reportCustomEvent()` 产生 `custom.<name>` Span。
523
-
524
- ## 0x04 资源加载时序
525
-
526
- 本文对照 W3C Resource Timing、Chrome Timing 和 web.dev,并给出 Resource Span 请求时序的产品划分。适用范围是单次 `resource` Span 的阶段图,不含 View 导航瀑布。
527
-
528
- 阶段名称靠近 Chrome Timing。每段时长用已上报的 `attributes.resource.*.duration` 计算,起点用对应的 `start`,单位为毫秒,相对该资源 `startTime`。
529
-
530
- 产品按 `attributes.view.id` 关联 View 生命周期快照后,可用 `attributes.view.previous_url_template` 展示资源所属页面的前序路由。该字段与 `attributes.view.url_template` 使用同一模板规则,初始 View 为空字符串;它不参与 Resource Timing 阶段计算,也不直接写入 `resource` Span。
531
-
532
- ### a. W3C Resource Timing
533
-
534
- 规范记录的是时间点,不是产品上的色块。浏览器按发生顺序打点,产品上的每一段是相邻两点相减。来源:[Resource Timing](https://www.w3.org/TR/resource-timing/)。
535
-
536
- | 顺序 | 时间点 | 含义 |
537
- | --- | --- | --- |
538
- | 1 | `startTime` | 这次取资源开始 |
539
- | 2 | `redirectStart` | 第一次重定向开始 |
540
- | 3 | `redirectEnd` | 最后一次重定向的响应收完 |
541
- | 4 | `workerStart` | Service Worker 开始处理这次请求;未拦截时为 `0` |
542
- | 5 | `fetchStart` | 重定向结束后,开始取最终资源 |
543
- | 6 | `domainLookupStart` | DNS 查询开始 |
544
- | 7 | `domainLookupEnd` | DNS 查询结束 |
545
- | 8 | `connectStart` | 开始建立到服务器的连接 |
546
- | 9 | `secureConnectionStart` | TLS 握手开始;非 HTTPS 时为 `0` |
547
- | 10 | `connectEnd` | 连接建立完成,TLS 包含在内 |
548
- | 11 | `requestStart` | 开始向服务器发请求 |
549
- | 12 | `firstInterimResponseStart` | 中间响应,例如 `103` |
550
- | 13 | `responseStart` | 收到响应首字节 |
551
- | 14 | `finalResponseHeadersStart` | 最终响应头开始,通常是 `200` |
552
- | 15 | `responseEnd` | 最后一字节到达,或连接先关闭 |
553
-
554
- 用上面的时间点可以算出这些耗时:
555
-
556
- | 顺序 | 耗时 | 计算 |
557
- | --- | --- | --- |
558
- | 1 | 重定向 | `redirectEnd − redirectStart` |
559
- | 2 | Service Worker | `fetchStart − workerStart` |
560
- | 3 | DNS | `domainLookupEnd − domainLookupStart` |
561
- | 4 | 建立连接(含 TLS) | `connectEnd − connectStart` |
562
- | 5 | TLS 握手 | `connectEnd − secureConnectionStart` |
563
- | 6 | 发出请求后等到首字节 | `responseStart − requestStart` |
564
- | 7 | 接收响应体 | `responseEnd − responseStart` |
565
-
566
- 规范没有排队时间点。跨源资源未通过 Timing-Allow-Origin 检查时,重定向到 `responseStart` 之间的点会变成 `0`,只保留 `startTime`、`fetchStart`、总时长和 `responseEnd`。
567
-
568
- `workerStart` 是重定向链最终请求上的 Service Worker 开始时间。`fetchStart` 是 post-redirect start。产品上的「后台脚本处理」发生在重定向之后。
569
-
570
- ### b. Chrome Timing
571
-
572
- Chrome 开发者工具中,点开一条请求的 Timing。一次请求不会出现全部标签。来源:[Chrome 网络面板说明](https://developer.chrome.com/docs/devtools/network/reference)。
573
-
574
- | 顺序 | Timing 标签 | 含义 |
575
- | --- | --- | --- |
576
- | 1 | Queueing | 连接开始前排队:存在更高优先级请求,或同一源连接数已满 |
577
- | 2 | Stalled | 连接开始后仍可能因 Queueing 同类原因停滞 |
578
- | 3 | DNS Lookup | 解析域名对应的 IP |
579
- | 4 | Initial connection | 建立连接,包含 TCP 握手和 TLS,不单列 TLS |
580
- | 5 | Proxy negotiation | 与代理协商,仅走代理时出现 |
581
- | 6 | Request sent | 正在发送 HTTP 请求 |
582
- | 7 | ServiceWorker Preparation | 正在启动 Service Worker |
583
- | 8 | Request to ServiceWorker | 请求交给 Service Worker |
584
- | 9 | Waiting (TTFB) | 请求已发出,等待响应首字节。包含一趟网络往返和服务器准备时间 |
585
- | 10 | Content Download | 读取响应体 |
586
-
587
- 常见可见:Queueing、DNS Lookup、Initial connection、Waiting (TTFB)、Content Download。连接已建立时,DNS Lookup 和 Initial connection 经常为 `0`。
588
-
589
- Chrome 不单独上报 Queueing / Stalled / Request sent 到 RUM 字段,产品阶段图无法画出这三行。
590
-
591
- ### c. Google web.dev
592
-
593
- web.dev 讲解同一组时间点,并单独定义页面导航的 Time to First Byte(TTFB,首字节时间)。来源:[Navigation and Resource Timing](https://web.dev/articles/navigation-and-resource-timing)、[TTFB](https://web.dev/articles/ttfb)。
594
-
595
- | 顺序 | 阶段 | 时间点 | 含义 |
596
- | --- | --- | --- | --- |
597
- | 1 | Redirects | `redirectStart` / `redirectEnd` | HTTP 重定向 |
598
- | 2 | DNS lookup | `domainLookupStart` / `domainLookupEnd` | DNS 解析 |
599
- | 3 | Connection | `connectStart` → `secureConnectionStart` → `connectEnd` | 建连,HTTPS 时包含 TLS,TLS 耗时 = `connectEnd − secureConnectionStart` |
600
- | 4 | Fetch | `fetchStart` | 开始取资源,会先查缓存 |
601
- | 5 | Service worker | `workerStart` | 未拦截时为 `0` |
602
- | 6 | Request | `requestStart` → `responseStart` | 发出请求到收到首字节 |
603
- | 7 | Response | `responseStart` → `responseEnd` | 接收响应体 |
604
-
605
- web.dev 的页面 TTFB 从导航开始算到 `responseStart`,包含:
606
-
607
- 1. 重定向
608
- 2. Service Worker 启动(如有)
609
- 3. DNS
610
- 4. 建连和 TLS
611
- 5. 发出请求直到首字节到达
612
-
613
- Chrome Timing 的 Waiting (TTFB) 只等于第 `5` 项。Resource Span 的「等待首字节」跟 Chrome 这一行走,对应 `resource.first_byte.duration`。
614
-
615
- ### d. 产品建议
616
-
617
- 始终展示查找服务器、建立连接、加密连接、等待首字节、下载内容。上报了重定向或 Service Worker 时,再增加对应行。
618
-
619
- 时长用 `resource.*.duration`。建立连接需要减去 TLS:W3C 的 `connectEnd − connectStart` 已包含加密,直接展示完整 `connect.duration` 会和下一行重复。无 `ssl` 时使用完整 `connect.duration`。
620
-
621
- #### 阶段与计算
622
-
623
- | 顺序 | 产品名 | 出现条件 | 时长 |
624
- | --- | --- | --- | --- |
625
- | 1 | 重定向 | 上报了 `redirect.start` 和 `redirect.duration` | `attributes.resource.redirect.duration` |
626
- | 2 | 后台脚本处理 | 上报了 `worker.start` 和 `worker.duration` | `attributes.resource.worker.duration` |
627
- | 3 | 查找服务器 | 始终占一行 | `attributes.resource.dns.duration` |
628
- | 4 | 建立连接 | 始终占一行 | 有 `ssl` 时:`connect.duration − ssl.duration`<br />无 `ssl` 时:`connect.duration` |
629
- | 5 | 加密连接 | 始终占一行 | `attributes.resource.ssl.duration` |
630
- | 6 | 等待首字节 | 始终占一行 | `attributes.resource.first_byte.duration` |
631
- | 7 | 下载内容 | 始终占一行 | `attributes.resource.download.duration` |
632
-
633
- 时间轴上,建立连接画到 `ssl.start`,加密连接从 `ssl.start` 画到 `connect` 结束。
634
-
635
- #### 说明与用户感受
636
-
637
- | 阶段 | 说明 | 用户感受 |
638
- | --- | --- | --- |
639
- | 重定向 | [a] 浏览器跟随 HTTP 重定向,直到拿到最终 URL,对应 `redirectEnd − redirectStart`<br />[b] 未发生重定向时不上报 | [a] 目标内容还没开始出现<br />[b] 文档请求会延长白屏,接口请求会延长 loading |
640
- | 后台脚本处理 | [a] Service Worker 拦截并处理该请求的时间,对应 `fetchStart − workerStart`<br />[b] 记录的是重定向之后、最终请求上的处理<br />[c] 未拦截时不上报 | [a] 界面没有单独提示<br />[b] 用户只会感到这次加载更慢,无法分辨是后台脚本在处理 |
641
- | 查找服务器 | [a] DNS 解析耗时,对应 `domainLookupEnd − domainLookupStart`<br />[b] 解析结果已缓存、或未发起新查询时,可能为 `0` 或不上报 | [a] 内容尚未出现<br />[b] 用户无法感知「正在解析域名」,体验上仍是点了之后没有反应 |
642
- | 建立连接 | [a] 传输连接中扣除 TLS 后的部分<br />[b] W3C 的 `connectEnd − connectStart` 包含加密,产品用 `connect.duration − ssl.duration`,避免与下一行重复 | [a] 内容尚未出现<br />[b] 用户无法感知「正在建连」,体验上仍是在等待 |
643
- | 加密连接 | [a] TLS 握手,对应 `connectEnd − secureConnectionStart`<br />[b] 非 HTTPS、或未发生新握手时不上报 | [a] 内容尚未出现<br />[b] 用户无法感知「正在握手」,体验上仍是在等待 |
644
- | 等待首字节 | [a] 从发出请求到收到响应首字节,对应 `responseStart − requestStart`<br />[b] 包含网络往返和服务端处理,浏览器时序无法拆分<br />[c] 对齐 Chrome Waiting (TTFB),范围小于 web.dev 的页面 TTFB | [a] 本次请求的内容仍不可见:页面持续白屏或停留在旧画面,接口持续 loading<br />[b] 过慢时会感觉页面没有反应 |
645
- | 下载内容 | [a] 从收到首字节到响应体接收完毕,对应 `responseEnd − responseStart`<br />[b] 耗时主要随体积和带宽变化 | [a] 内容开始出现并逐渐完整,图片、脚本会陆续可见<br />[b] 接口数据会从空状态填到页面上<br />[c] 体积大时,用户能感到「出来了但还在加载」 |
646
-
647
- 等待首字节拆不开服务端处理与网络往返。要判断服务端是否慢,需要结合 Trace 或 [Server Timing](https://www.w3.org/TR/server-timing/)。
648
-
649
- #### 缺数字段
650
-
651
- | 上报情况 | 页面展示 |
652
- | --- | --- |
653
- | `start` 与 `duration` 都在,且 `duration > 0` | 显示毫秒数,画出色块 |
654
- | `start` 与 `duration` 都在,且 `duration = 0` | 写 `0` 毫秒。该步骤没有额外耗时 |
655
- | 缺少 `start` 或 `duration` | 写「没有上报」,不画色块。可能该步骤未发生,也可能跨源不允许查看细分时间 |
656
-
657
- 查找服务器、建立连接、加密连接都没有上报时,写「没有上报」。跨源资源未暴露细分时间时表现相同,不能据此判断连接复用。
658
-
659
- ### e. 参考资料
660
-
661
- | 来源 | 文档 |
662
- | --- | --- |
663
- | W3C | [Resource Timing](https://www.w3.org/TR/resource-timing/) |
664
- | W3C | [Server Timing](https://www.w3.org/TR/server-timing/) |
665
- | Chrome | [网络面板 · Timing](https://developer.chrome.com/docs/devtools/network/reference) |
666
- | web.dev | [Navigation and Resource Timing](https://web.dev/articles/navigation-and-resource-timing) |
667
- | web.dev | [Time to First Byte](https://web.dev/articles/ttfb) |
577
+ `reportCustomEvent()` 产生 `custom.<name>` Span。`reportAction({ name })` 产生以 `action.name` 命名的 Span,且 `action.type=custom`。