@blueking/open-telemetry 0.0.22 → 0.0.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +256 -70
- package/dist/bk-rum.global.js +2 -2
- package/dist/bk-rum.global.js.map +1 -1
- package/dist/core/config.d.ts +4 -2
- package/dist/core/processor.d.ts +2 -2
- package/dist/core/runtime-context.d.ts +4 -0
- package/dist/core/sampling.d.ts +0 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +395 -477
- package/dist/index.js.map +1 -1
- package/dist/plugins/blank-screen.d.ts +1 -1
- package/dist/plugins/common.d.ts +3 -4
- package/dist/plugins/web-vitals.d.ts +0 -5
- package/dist/schema/attributes.d.ts +6 -8
- package/dist/schema/span.d.ts +24 -15
- package/package.json +1 -1
- package/skills/bk-rum-otele/SKILL.md +26 -23
- package/skills/bk-rum-otele/references/actions.md +166 -0
- package/skills/bk-rum-otele/references/api.md +248 -215
- package/skills/bk-rum-otele/references/architecture.md +139 -37
- package/skills/bk-rum-otele/references/blank-screen.md +190 -0
- package/skills/bk-rum-otele/references/capabilities.md +175 -83
- package/skills/bk-rum-otele/references/configuration.md +226 -151
- package/skills/bk-rum-otele/references/data-model.md +156 -24
- package/skills/bk-rum-otele/references/errors-security.md +168 -0
- package/skills/bk-rum-otele/references/extensions.md +236 -0
- package/skills/bk-rum-otele/references/framework-integration.md +318 -75
- package/skills/bk-rum-otele/references/getting-started.md +186 -70
- package/skills/bk-rum-otele/references/network.md +258 -0
- package/skills/bk-rum-otele/references/performance.md +197 -0
- package/skills/bk-rum-otele/references/privacy.md +236 -67
- package/skills/bk-rum-otele/references/production.md +264 -0
- package/skills/bk-rum-otele/references/session-context.md +185 -0
- package/skills/bk-rum-otele/references/telemetry-reference.md +479 -51
- package/skills/bk-rum-otele/references/transport-sampling.md +318 -32
- package/skills/bk-rum-otele/references/troubleshooting.md +341 -151
- package/skills/bk-rum-otele/references/views-routing.md +211 -0
- package/skills/bk-rum-otele/references/concepts.md +0 -60
- package/skills/bk-rum-otele/references/custom-plugin.md +0 -170
- package/skills/bk-rum-otele/references/plugins-basic.md +0 -135
- package/skills/bk-rum-otele/references/plugins-error-security.md +0 -83
- package/skills/bk-rum-otele/references/plugins-experience.md +0 -124
- package/skills/bk-rum-otele/references/plugins-network.md +0 -32
- package/skills/bk-rum-otele/references/plugins.md +0 -62
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
<!-- 本文件由 packages/open-telemetry/scripts/gen-skill-references.mjs 从 wikis 自动生成,请勿手改。 -->
|
|
2
|
+
|
|
3
|
+
# Web Vitals 与 Long Task
|
|
4
|
+
|
|
5
|
+
性能能力把浏览器提供的核心体验指标和主线程阻塞转换为原始 Span。`webVitals` 默认开启;`longTask` 默认关闭。SDK 不创建 Histogram,分布和分位数由服务端从原始值派生。
|
|
6
|
+
|
|
7
|
+
## Web Vitals 总览
|
|
8
|
+
|
|
9
|
+
`browser.web_vital` 覆盖:
|
|
10
|
+
|
|
11
|
+
| 指标 | `vital.metric` | 单位 | 当前采集方式 |
|
|
12
|
+
| --- | --- | --- | --- |
|
|
13
|
+
| Cumulative Layout Shift | `cls` | 无量纲 | 每个 SDK View 独立计算 session window |
|
|
14
|
+
| First Contentful Paint | `fcp` | ms | `web-vitals/attribution` |
|
|
15
|
+
| Interaction to Next Paint | `inp` | ms | 每个 SDK View 维护交互候选近似 p98 |
|
|
16
|
+
| Largest Contentful Paint | `lcp` | ms | `web-vitals/attribution` |
|
|
17
|
+
| Time to First Byte | `ttfb` | ms | `web-vitals/attribution` |
|
|
18
|
+
|
|
19
|
+
插件通过动态 import 加载 `web-vitals/attribution`。加载失败只禁用该插件;`debug=true` 时输出 warning,不影响其他采集。
|
|
20
|
+
|
|
21
|
+
## 公共 Vital 字段
|
|
22
|
+
|
|
23
|
+
每条 Vital Span 包含:
|
|
24
|
+
|
|
25
|
+
- `vital.metric`
|
|
26
|
+
- `vital.value`
|
|
27
|
+
- `vital.rating=good|needs-improvement|poor`
|
|
28
|
+
- `vital.id`
|
|
29
|
+
- `span_type=vital`
|
|
30
|
+
- `outcome.type=success`,仅 rating=good
|
|
31
|
+
- `outcome.type=warning`,其他 rating
|
|
32
|
+
|
|
33
|
+
Vital Span 是瞬时 INTERNAL Span。非法 `NaN`、`Infinity` 不上报。
|
|
34
|
+
|
|
35
|
+
## Navigation Type
|
|
36
|
+
|
|
37
|
+
优先使用 web-vitals metric 的 `navigationType`;否则读取首个 `PerformanceNavigationTiming.type`,并把 `back_forward` 规范为 `back-forward`。可能值包括:
|
|
38
|
+
|
|
39
|
+
- `navigate`
|
|
40
|
+
- `reload`
|
|
41
|
+
- `back-forward`
|
|
42
|
+
- `back-forward-cache`
|
|
43
|
+
- `prerender`
|
|
44
|
+
- `restore`
|
|
45
|
+
- `unknown`
|
|
46
|
+
|
|
47
|
+
## FCP、LCP 与 TTFB 的 View 归属
|
|
48
|
+
|
|
49
|
+
这三类指标使用第一个 Performance Entry 的真实发生时间回查历史 Runtime Context。因此即使路由已经切换,较晚回调的初始导航指标仍会归到发生时的旧 View,而不是当前 View。
|
|
50
|
+
|
|
51
|
+
Attribution 字段:
|
|
52
|
+
|
|
53
|
+
| 指标 | 字段 |
|
|
54
|
+
| --- | --- |
|
|
55
|
+
| LCP | `vital.lcp.url`、`target`、`element_render_delay`、`resource_load_duration`、`time_to_first_byte` |
|
|
56
|
+
| FCP | `vital.fcp.time_to_first_byte`、`load_state` |
|
|
57
|
+
| TTFB | `vital.ttfb.waiting_duration`、`dns_duration`、`connection_duration`、`request_duration` |
|
|
58
|
+
|
|
59
|
+
LCP URL 经过 `redactUrl`;DOM target 等字段可能包含高基数或业务语义,应纳入隐私审查。
|
|
60
|
+
|
|
61
|
+
## 每个 View 的 CLS
|
|
62
|
+
|
|
63
|
+
SDK 不直接使用整页生命周期累计的 web-vitals CLS,而是按 SDK View 重新计算:
|
|
64
|
+
|
|
65
|
+
- 忽略 `hadRecentInput=true` 的 Layout Shift;
|
|
66
|
+
- 相邻 Shift 间隔小于 1 秒且窗口总长小于 5 秒时进入同一 session window;
|
|
67
|
+
- 取所有 session window 中累计值最大的一个;
|
|
68
|
+
- 数值保留四位小数;
|
|
69
|
+
- 记录该窗口中最大的单次 Shift 及目标元素;
|
|
70
|
+
- View 结束时发送一条 CLS Span,即使值为 0。
|
|
71
|
+
|
|
72
|
+
Rating 阈值:
|
|
73
|
+
|
|
74
|
+
- `<= 0.1`:good
|
|
75
|
+
- `<= 0.25`:needs-improvement
|
|
76
|
+
- `> 0.25`:poor
|
|
77
|
+
|
|
78
|
+
Attribution:`vital.cls.largest_shift_target`、`vital.cls.largest_shift_value`。通过 web-vitals 原生指标产生时还可能带 `vital.cls.load_state`。
|
|
79
|
+
|
|
80
|
+
## 每个 View 的 INP
|
|
81
|
+
|
|
82
|
+
SDK 使用 Event Timing Observer,`durationThreshold=40`:
|
|
83
|
+
|
|
84
|
+
1. 按 interactionId 合并同一次交互,只保留 duration 最大 Entry。
|
|
85
|
+
2. duration 上限保护为 60,000ms。
|
|
86
|
+
3. 保留当前 View 最慢的 10 个候选。
|
|
87
|
+
4. 优先使用浏览器 `performance.interactionCount` 估算总交互数。
|
|
88
|
+
5. 浏览器不提供时,根据 interactionId 范围近似交互数。
|
|
89
|
+
6. 取排序候选中接近 p98 的位置,而不是简单最大值。
|
|
90
|
+
7. View 没有有效 Interaction 时不发送 INP Span。
|
|
91
|
+
|
|
92
|
+
Rating 阈值:
|
|
93
|
+
|
|
94
|
+
- `<= 200ms`:good
|
|
95
|
+
- `<= 500ms`:needs-improvement
|
|
96
|
+
- `> 500ms`:poor
|
|
97
|
+
|
|
98
|
+
Attribution:
|
|
99
|
+
|
|
100
|
+
- `vital.inp.interaction_target`
|
|
101
|
+
- `vital.inp.interaction_type`
|
|
102
|
+
- `vital.inp.input_delay`
|
|
103
|
+
- `vital.inp.processing_duration`
|
|
104
|
+
- `vital.inp.presentation_delay`
|
|
105
|
+
|
|
106
|
+
Target 优先读取 `data-bk-action-name`、`aria-label`、`name`,否则使用标签名。
|
|
107
|
+
|
|
108
|
+
## SPA 语义
|
|
109
|
+
|
|
110
|
+
FCP、LCP、TTFB 本质上仍主要来自文档导航;CLS 和 INP 按 SDK View 划分,适合 SPA 路由。View 名称变化只更新当前 Vital Runtime Attributes,不会清空当前 CLS/INP;创建新 View 才会 finalize 旧值并重置。
|
|
111
|
+
|
|
112
|
+
## Long Task 启用
|
|
113
|
+
|
|
114
|
+
```ts
|
|
115
|
+
tracking: {
|
|
116
|
+
longTask: {
|
|
117
|
+
threshold: 50,
|
|
118
|
+
},
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
阈值默认为 50ms;有效的非负有限值生效,非法值回退 50。浏览器 Performance API 通常本身只产生超过 50ms 的 Entry,把阈值设得更低不保证获得更多数据。
|
|
123
|
+
|
|
124
|
+
## API 选择与降级
|
|
125
|
+
|
|
126
|
+
插件按优先级只观察一种 Entry:
|
|
127
|
+
|
|
128
|
+
1. 浏览器支持 `long-animation-frame`:使用 LoAF。
|
|
129
|
+
2. 否则支持 `longtask`:使用 Legacy Long Tasks。
|
|
130
|
+
3. 都不支持:跳过插件,不抛业务异常。
|
|
131
|
+
|
|
132
|
+
不会同时观察两种 API,因此不产生重复 Span。初始化时 buffered entries 先合并到下一个宏任务处理,等待 Session/View 上下文就绪。
|
|
133
|
+
|
|
134
|
+
## Long Task Span
|
|
135
|
+
|
|
136
|
+
每条合格 Entry 生成 `browser.long_task` INTERNAL Span,Span 持续时间等于浏览器 Entry duration:
|
|
137
|
+
|
|
138
|
+
- `long_task.id`
|
|
139
|
+
- `long_task.entry_type=long-animation-frame|long-task`
|
|
140
|
+
- `long_task.duration`
|
|
141
|
+
- `long_task.name`
|
|
142
|
+
- LoAF 的 `long_task.start_time`
|
|
143
|
+
- LoAF 原生 `long_task.blocking_duration`
|
|
144
|
+
- LoAF 的 `long_task.first_ui_event_timestamp`
|
|
145
|
+
- LoAF 的 `long_task.render_start`
|
|
146
|
+
- LoAF 的 `long_task.style_and_layout_start`
|
|
147
|
+
|
|
148
|
+
Span 开始时间是 `performance.timeOrigin + entry.startTime`。该 Span 也会通知 Action Activity,使活动中的 Action 能关联此 Long Task。
|
|
149
|
+
|
|
150
|
+
## LoAF 脚本 Event
|
|
151
|
+
|
|
152
|
+
LoAF 每个脚本写为 `long_task.script` Span Event:
|
|
153
|
+
|
|
154
|
+
- `long_task.script.duration`
|
|
155
|
+
- `long_task.script.execution_start`
|
|
156
|
+
- `long_task.script.forced_style_and_layout_duration`
|
|
157
|
+
- `long_task.script.invoker`
|
|
158
|
+
- `long_task.script.invoker_type`
|
|
159
|
+
- `long_task.script.pause_duration`
|
|
160
|
+
- `long_task.script.source_char_position`
|
|
161
|
+
- `long_task.script.source_function_name`
|
|
162
|
+
- `long_task.script.source_url`,经过 URL 脱敏
|
|
163
|
+
- `long_task.script.start_time`
|
|
164
|
+
- `long_task.script.window_attribution`
|
|
165
|
+
|
|
166
|
+
Event 时间使用脚本自己的 `performance.timeOrigin + startTime`。
|
|
167
|
+
|
|
168
|
+
## Legacy Attribution Event
|
|
169
|
+
|
|
170
|
+
Legacy Long Tasks 的每条 attribution 写为 `long_task.attribution` Span Event:
|
|
171
|
+
|
|
172
|
+
- `long_task.attribution.index`
|
|
173
|
+
- `long_task.attribution.container_id`
|
|
174
|
+
- `long_task.attribution.container_name`
|
|
175
|
+
- `long_task.attribution.container_src`,经过 URL 脱敏
|
|
176
|
+
- `long_task.attribution.container_type`
|
|
177
|
+
- `long_task.attribution.name`
|
|
178
|
+
|
|
179
|
+
Legacy API 不提供可靠 blocking duration,SDK不会从总 duration 猜测。
|
|
180
|
+
|
|
181
|
+
## 成本与隐私
|
|
182
|
+
|
|
183
|
+
Long Task 会为每个 Entry 建 Span,并可能附带多个脚本 Event;在主线程问题严重的应用中数据量可观。建议结合 Session 采样开启。
|
|
184
|
+
|
|
185
|
+
脚本 URL、函数名、Invoker 和 DOM target 可能暴露内部路径或业务命名。URL 会走 `redactUrl`,所有 Long Task Event attributes 在写入前还会经过 `redactAttributes`。`tracking.beforeSend` 只暴露 Span 顶层 attributes,不能修改这些 Span Event。
|
|
186
|
+
|
|
187
|
+
## 常见问题
|
|
188
|
+
|
|
189
|
+
### 没有 Long Task 数据
|
|
190
|
+
|
|
191
|
+
在控制台检查 `PerformanceObserver.supportedEntryTypes`。不支持两种 API时是正常降级;还要确认插件已开启、Session 被采样、存在有效 View、Entry duration 达到阈值。
|
|
192
|
+
|
|
193
|
+
### CLS/INP 只在切换页面后出现
|
|
194
|
+
|
|
195
|
+
CLS/INP 在 View finalize 时发送;当前 View 通常在下一次路由、pagehide、Session 过期或 shutdown 时结束。需要调试最终值时可以触发一次 View 切换或 `await bkOT.shutdown()`。
|
|
196
|
+
|
|
197
|
+
完整字段见[遥测字段字典](./telemetry-reference.md#web-vitals-字段)和[Long Task 字段](./telemetry-reference.md#long-task-字段)。
|
|
@@ -1,109 +1,278 @@
|
|
|
1
1
|
<!-- 本文件由 packages/open-telemetry/scripts/gen-skill-references.mjs 从 wikis 自动生成,请勿手改。 -->
|
|
2
2
|
|
|
3
|
-
#
|
|
3
|
+
# 隐私、Consent 与字段治理
|
|
4
4
|
|
|
5
|
-
RUM
|
|
5
|
+
浏览器 RUM 天然接触 URL、错误信息、页面文本、用户标识和业务属性。SDK 提供采集授权与脱敏入口,但不会替业务判断哪些数据属于敏感信息,也不会替代服务端权限、留存和删除机制。
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## 默认采集边界
|
|
8
|
+
|
|
9
|
+
当前 SDK:
|
|
10
|
+
|
|
11
|
+
- 不采集 Fetch/XHR 请求体和响应体;
|
|
12
|
+
- 不采集普通业务请求/响应 header;
|
|
13
|
+
- 不采集 WebSocket message 内容;
|
|
14
|
+
- 默认 Action 命名模式不读取 DOM 文本,只读取显式 `data-bk-action-name`,否则使用标签名;
|
|
15
|
+
- 默认 URL 清洗会移除 username、password、query 和 fragment;
|
|
16
|
+
- 会采集错误 message/stack、资源 URL、View URL、CSP 原始策略、显式业务属性;
|
|
17
|
+
- `user.id` 由业务提供,SDK 原样透传;
|
|
18
|
+
- 可选 `persistentId: true` 会把匿名设备 ID 写入 localStorage。
|
|
19
|
+
|
|
20
|
+
“默认不采集 Body”不代表所有字段都安全。URL path、错误堆栈、DOM 显式名称和自定义属性仍可能包含个人信息或密钥。
|
|
21
|
+
|
|
22
|
+
## Consent 生命周期
|
|
23
|
+
|
|
24
|
+
需要先授权后采集时:
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
const rum = new BkOpenTelemetry({
|
|
28
|
+
application: { name: 'portal' },
|
|
29
|
+
transport: { endpoint: 'https://rum.example.com' },
|
|
30
|
+
privacy: { trackingConsent: 'not-granted' },
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// 用户同意后
|
|
34
|
+
await rum.setTrackingConsent('granted');
|
|
35
|
+
|
|
36
|
+
// 用户撤回后
|
|
37
|
+
await rum.setTrackingConsent('not-granted');
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
`not-granted` 状态下不会安装内置插件、自定义插件或 instrumentation。撤销时 SDK 会:
|
|
41
|
+
|
|
42
|
+
1. 关闭插件并解除监听/patch;
|
|
43
|
+
2. 关闭注册的 instrumentation;
|
|
44
|
+
3. 清除运行时 Session 关联;
|
|
45
|
+
4. flush 撤销前已经结束、仍在队列中的数据;
|
|
46
|
+
5. 释放本实例持有的全局 OTel context/propagator 引用。
|
|
47
|
+
|
|
48
|
+
注意:
|
|
49
|
+
|
|
50
|
+
- 撤销不是“删除已上报数据”;删除和留存策略必须由服务端实现;
|
|
51
|
+
- 撤销时会尽力发送队列中的既有数据。如果你的合规策略要求撤销时直接丢弃未发送数据,当前 SDK 不提供该选项;
|
|
52
|
+
- SDK 不提供 Consent UI、Cookie 分类或法律依据记录;
|
|
53
|
+
- 页面中有多个 SDK 实例时,全局 OTel 对象按引用计数释放,但全局监听仍可能重复,因此仍应使用单实例。
|
|
54
|
+
|
|
55
|
+
构造阶段有一个重要边界:`new BkOpenTelemetry()` 会先解析配置并创建 OpenTelemetry Resource。只要 `tracking.device !== false`,这个过程会同步读取 UA/UA-CH、内存、逻辑处理器等 Device Resource 快照,即使初始 Consent 是 `not-granted` 或 `enabled: false`;这些值此时不会发送,Device 插件的 ID/视口/网络监听也不会启动。如果你的合规定义连“读取到内存”都必须晚于授权,应在获得授权后再构造 SDK,而不是只依赖 `trackingConsent`。
|
|
8
56
|
|
|
9
57
|
## URL 脱敏
|
|
10
58
|
|
|
11
|
-
|
|
59
|
+
默认逻辑:
|
|
60
|
+
|
|
61
|
+
```text
|
|
62
|
+
https://user:pass@example.com/orders/123?token=abc#detail
|
|
63
|
+
→ https://example.com/orders/123
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
默认只移除认证信息、query 和 fragment,不改写 path。动态订单号、用户 ID、文件名等仍需要自定义:
|
|
12
67
|
|
|
13
68
|
```ts
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
69
|
+
privacy: {
|
|
70
|
+
redactUrl(rawUrl) {
|
|
71
|
+
const url = new URL(rawUrl, location.href);
|
|
72
|
+
url.username = '';
|
|
73
|
+
url.password = '';
|
|
74
|
+
url.search = '';
|
|
75
|
+
url.hash = '';
|
|
76
|
+
url.pathname = url.pathname
|
|
77
|
+
.replace(/\/orders\/[^/]+/g, '/orders/:id')
|
|
78
|
+
.replace(/\/users\/[^/]+/g, '/users/:id');
|
|
79
|
+
return url.href;
|
|
21
80
|
},
|
|
22
|
-
}
|
|
81
|
+
}
|
|
23
82
|
```
|
|
24
83
|
|
|
25
|
-
|
|
84
|
+
`redactUrl` 覆盖而不是叠加默认实现;自定义时必须自己删除 query、fragment 和认证信息。
|
|
26
85
|
|
|
27
|
-
|
|
86
|
+
SDK 在写入 View、Request、Resource、错误资源、CSP source、Long Task source、referrer 等 URL 字段时调用该 Hook。自定义插件直接写入名为 URL 的任意属性不会被自动识别,插件必须显式调用 `context.config.hooks.redactUrl()`。
|
|
87
|
+
|
|
88
|
+
## 路由分组不是 URL 脱敏
|
|
28
89
|
|
|
29
90
|
```ts
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
delete next['http.request.header.authorization'];
|
|
35
|
-
delete next['biz.mobile'];
|
|
36
|
-
delete next['biz.email'];
|
|
37
|
-
return next;
|
|
91
|
+
tracking: {
|
|
92
|
+
view: {
|
|
93
|
+
getPathGroup(url) {
|
|
94
|
+
return new URL(url, location.href).pathname.replace(/\/orders\/[^/]+/, '/orders/:id');
|
|
38
95
|
},
|
|
39
96
|
},
|
|
40
|
-
}
|
|
97
|
+
}
|
|
41
98
|
```
|
|
42
99
|
|
|
43
|
-
|
|
100
|
+
`getPathGroup` 只生成低基数 `view.url_path_group`,不会改写 `view.url`。要保护原 URL,仍需 `privacy.redactUrl`。建议两者使用相同的动态段规则,但用途不同:
|
|
101
|
+
|
|
102
|
+
- `redactUrl`:隐私和敏感数据边界;
|
|
103
|
+
- `getPathGroup`:聚合维度和查询基数。
|
|
104
|
+
|
|
105
|
+
未配置 `context.attributes.page` 时默认返回空对象,不存在独立的默认页面属性路径。页面地址统一由经过 `redactUrl` 的 `view.url` 表达,聚合维度使用 `view.url_path_group`;业务通过该 Hook 添加的属性仍需自行确保不包含敏感信息。
|
|
106
|
+
|
|
107
|
+
## 属性脱敏
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
privacy: {
|
|
111
|
+
redactAttributes(attributes) {
|
|
112
|
+
const next = { ...attributes };
|
|
113
|
+
delete next['enduser.email'];
|
|
114
|
+
delete next['biz.access_token'];
|
|
115
|
+
|
|
116
|
+
if (typeof next['error.message'] === 'string') {
|
|
117
|
+
next['error.message'] = next['error.message'].replace(/[\w.+-]+@[\w.-]+/g, '<email>');
|
|
118
|
+
}
|
|
119
|
+
return next;
|
|
120
|
+
},
|
|
121
|
+
}
|
|
122
|
+
```
|
|
44
123
|
|
|
45
|
-
|
|
124
|
+
当前调用边界:
|
|
46
125
|
|
|
47
|
-
|
|
126
|
+
- SDK 构造 Resource attributes 时调用一次;
|
|
127
|
+
- `BkOpenTelemetry.startSpan()` 合并运行时与显式属性后调用;
|
|
128
|
+
- 内置/自定义插件也可以显式调用 `context.applyRedact()`;
|
|
129
|
+
- Filter processor 不会在 `tracking.beforeSend` 之后再次调用脱敏 Hook。
|
|
48
130
|
|
|
49
|
-
`
|
|
131
|
+
该 Hook 的类型只覆盖 Attributes,不会自动处理 Span Event。当前例外是 Long Task 插件在写入自己的 script/attribution Event 前显式调用;`recordException()` 产生的 exception message/stacktrace 不会经过这个 Hook。
|
|
50
132
|
|
|
51
|
-
|
|
52
|
-
- `error`:错误事件补充属性。
|
|
53
|
-
- `custom`:手动自定义事件补充属性。
|
|
133
|
+
因此 `tracking.beforeSend` 如果新增或改写字段,必须保证写入值已经安全:
|
|
54
134
|
|
|
55
135
|
```ts
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
'biz.space_id': window.__BK_SPACE_ID__,
|
|
61
|
-
'biz.project_id': window.__BK_PROJECT_ID__,
|
|
62
|
-
}),
|
|
63
|
-
error: () => ({
|
|
64
|
-
'biz.release_channel': window.__BK_RELEASE_CHANNEL__,
|
|
65
|
-
}),
|
|
66
|
-
custom: () => ({
|
|
67
|
-
'biz.scene': 'checkout',
|
|
68
|
-
}),
|
|
69
|
-
},
|
|
136
|
+
tracking: {
|
|
137
|
+
beforeSend(event) {
|
|
138
|
+
// 这里写入的字段不会再自动走 redactAttributes
|
|
139
|
+
event.attributes['biz.region'] = 'ap-guangzhou';
|
|
70
140
|
},
|
|
71
|
-
}
|
|
141
|
+
}
|
|
72
142
|
```
|
|
73
143
|
|
|
74
|
-
|
|
144
|
+
`redactAttributes` 应满足:
|
|
145
|
+
|
|
146
|
+
- 返回普通的合法 OTel Attributes 对象;
|
|
147
|
+
- 不抛异常;
|
|
148
|
+
- 不执行网络请求或昂贵计算;
|
|
149
|
+
- 不原地依赖第三方对象结构,推荐复制后修改;
|
|
150
|
+
- 同一输入得到稳定结果,避免字段在不同 Span 间随机变化。
|
|
151
|
+
|
|
152
|
+
## Request 局部过滤
|
|
75
153
|
|
|
76
|
-
|
|
154
|
+
只针对 Fetch/XHR:
|
|
77
155
|
|
|
78
156
|
```ts
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
157
|
+
tracking: {
|
|
158
|
+
request: {
|
|
159
|
+
excludedUrls: [/\/auth\/refresh(?:\?|$)/],
|
|
160
|
+
beforeSend(event) {
|
|
161
|
+
delete event.attributes['server.address'];
|
|
162
|
+
if (event.resource.url?.includes('/private/')) return false;
|
|
83
163
|
},
|
|
84
164
|
},
|
|
85
|
-
}
|
|
165
|
+
}
|
|
86
166
|
```
|
|
87
167
|
|
|
88
|
-
|
|
168
|
+
执行边界:
|
|
89
169
|
|
|
90
|
-
|
|
170
|
+
- `excludedUrls` 在建 Span 和注入 trace header 之前判断,适合完全排除;
|
|
171
|
+
- `request.beforeSend` 在请求结束、属性已组装后执行,返回 false 丢弃该请求 Span;
|
|
172
|
+
- `tracking.beforeSend` 是所有 Span 的最终业务过滤;
|
|
173
|
+
- URL 在请求 Hook 前已经经过 `redactUrl`,但 `context.request` / `xhr` 仍是原浏览器对象,Hook 不应把其中原始敏感值重新复制到 attributes。
|
|
91
174
|
|
|
92
|
-
##
|
|
175
|
+
## Action 文本隐私
|
|
93
176
|
|
|
94
|
-
|
|
177
|
+
默认:
|
|
95
178
|
|
|
96
179
|
```ts
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
180
|
+
tracking: {
|
|
181
|
+
action: { nameCollection: 'explicit' },
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
SDK向上查找 `data-bk-action-name`,没有时退回标签名。建议模板只写稳定业务动作:
|
|
186
|
+
|
|
187
|
+
```html
|
|
188
|
+
<button data-bk-action-name="submit-order">提交订单</button>
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
不要写:
|
|
192
|
+
|
|
193
|
+
```html
|
|
194
|
+
<button data-bk-action-name="submit-order-for-user-13800138000">提交订单</button>
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
`nameCollection: 'text'` 会允许读取可访问名称和 DOM 文本,最多 100 字符,但长度限制不是脱敏。输入框内容、用户名、消息文本都有泄露风险,只有经过隐私评审后才应开启。
|
|
198
|
+
|
|
199
|
+
## User ID
|
|
200
|
+
|
|
201
|
+
```ts
|
|
202
|
+
rum.setUser({ id: hashUserId(rawUserId) });
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
SDK 不执行 hash。推荐:
|
|
206
|
+
|
|
207
|
+
- 使用稳定、不可直接还原的内部标识;
|
|
208
|
+
- 不发送手机号、邮箱、身份证、昵称;
|
|
209
|
+
- hash 时使用服务端管理的盐或 HMAC 方案,避免简单 SHA 对小空间标识可枚举;
|
|
210
|
+
- 登出时 `setUser({ id: undefined })`;
|
|
211
|
+
- 服务端限制谁能查询 `user.id`。
|
|
212
|
+
|
|
213
|
+
## Device ID 与存储
|
|
214
|
+
|
|
215
|
+
默认 `tracking.device` 开启,但 `persistentId` 默认 false:设备 ID 只活在当前 Device 插件的采集周期;撤销再授权会重新初始化插件并生成新 ID。启用持久化:
|
|
216
|
+
|
|
217
|
+
```ts
|
|
218
|
+
tracking: {
|
|
219
|
+
device: {
|
|
220
|
+
persistentId: true,
|
|
221
|
+
storageKey: 'MY_APP_RUM_DEVICE_ID',
|
|
107
222
|
},
|
|
108
|
-
}
|
|
223
|
+
}
|
|
109
224
|
```
|
|
225
|
+
|
|
226
|
+
这会写入 localStorage,可能受 Cookie/本地存储合规策略约束。SDK 不提供自动过期或删除 API;业务需在撤销授权时按自身策略清理指定 key。Session 也会使用 localStorage 维持跨 Tab 状态,撤销 Consent 不会自动删除该 Record;重新授权可能在 Record 有效时复用 Session ID。
|
|
227
|
+
|
|
228
|
+
## 错误与 CSP 数据
|
|
229
|
+
|
|
230
|
+
错误可能包含:
|
|
231
|
+
|
|
232
|
+
- `error.message`;
|
|
233
|
+
- exception message、type 和 stacktrace;这些 Span Event 不对 `tracking.beforeSend` 暴露;
|
|
234
|
+
- 源文件 URL、行列号;
|
|
235
|
+
- Promise rejection 的字符串化内容;
|
|
236
|
+
- 资源加载失败 URL。
|
|
237
|
+
|
|
238
|
+
CSP Span 可能包含 `blocked_uri`、`source_file`、`original_policy` 等字段。`original_policy` 可能暴露内部域名或安全策略结构。开启 `cspViolation` 前应确认服务端访问控制,并用 `redactAttributes` 删除不应出浏览器的数据。
|
|
239
|
+
|
|
240
|
+
## 自定义属性规范
|
|
241
|
+
|
|
242
|
+
推荐命名:`biz.<domain>.<field>`,例如 `biz.order.channel`。值只允许 OTel Attribute 支持的标量或同类型数组。
|
|
243
|
+
|
|
244
|
+
不要上报:
|
|
245
|
+
|
|
246
|
+
- token、cookie、密码、密钥;
|
|
247
|
+
- 完整请求/响应 Body;
|
|
248
|
+
- 未经处理的邮箱、手机号、身份证;
|
|
249
|
+
- 用户自由输入文本;
|
|
250
|
+
- 每条事件都唯一但无法用于诊断的高基数值;
|
|
251
|
+
- 超大字符串、序列化 DOM、整段应用状态。
|
|
252
|
+
|
|
253
|
+
## 服务端责任
|
|
254
|
+
|
|
255
|
+
SDK 不实现以下能力:
|
|
256
|
+
|
|
257
|
+
- IP 到地理位置的派生与脱敏;
|
|
258
|
+
- 错误指纹、聚合计数、分位数;
|
|
259
|
+
- 字段级访问控制;
|
|
260
|
+
- 租户鉴权和 token 轮换;
|
|
261
|
+
- 数据保留、删除、导出;
|
|
262
|
+
- Consent 法律依据存档;
|
|
263
|
+
- 敏感字段二次扫描。
|
|
264
|
+
|
|
265
|
+
生产链路应在 Collector/处理管道再做一次 allowlist 或脱敏,不能只依赖浏览器 Hook。
|
|
266
|
+
|
|
267
|
+
## 上线评审清单
|
|
268
|
+
|
|
269
|
+
1. `redactUrl` 覆盖 path 中的动态 ID,且仍删除 query/fragment/认证信息;
|
|
270
|
+
2. `view.url_path_group` 是低基数模板;
|
|
271
|
+
3. `user.id` 已按公司规范处理;
|
|
272
|
+
4. Action 未开启不必要的文本采集;
|
|
273
|
+
5. CSP、错误 stack、自定义字段经过字段评审;
|
|
274
|
+
6. 浏览器 token 是最小权限且可轮换;
|
|
275
|
+
7. `persistentId` 和 Session localStorage 满足存储授权要求;
|
|
276
|
+
8. Collector 有二次校验、权限、留存和删除能力;
|
|
277
|
+
9. 撤销 Consent 后的“flush 既有队列”行为符合产品政策;
|
|
278
|
+
10. 用真实页面抓包检查最终 OTLP JSON,而不仅是配置代码审查。
|