@antglobal/rlog-sdk 0.0.1755855517-dev.17 → 0.0.1755855517-dev.18
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 +203 -483
- package/dist/esm/index.d.ts +1 -2
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +36 -18
- package/dist/esm/lib/config.js +1 -1
- package/dist/esm/lib/error-trigger.d.ts.map +1 -1
- package/dist/esm/lib/error-trigger.js +5 -4
- package/dist/esm/lib/error.d.ts.map +1 -1
- package/dist/esm/lib/error.js +61 -50
- package/dist/esm/lib/init.d.ts.map +1 -1
- package/dist/esm/lib/init.js +43 -15
- package/dist/esm/lib/net.d.ts.map +1 -1
- package/dist/esm/lib/net.js +195 -160
- package/dist/esm/lib/router-monitor.d.ts.map +1 -1
- package/dist/esm/lib/router-monitor.js +18 -11
- package/dist/esm/lib/rrweb.d.ts.map +1 -1
- package/dist/esm/lib/rrweb.js +12 -8
- package/dist/esm/lib/upload-worker-manager.d.ts.map +1 -1
- package/dist/esm/lib/upload-worker-manager.js +59 -42
- package/dist/esm/lib/uploader.d.ts.map +1 -1
- package/dist/esm/lib/uploader.js +54 -50
- package/dist/lib/index.d.ts +1 -2
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js +41 -17
- package/dist/lib/lib/config.js +1 -1
- package/dist/lib/lib/error-trigger.d.ts.map +1 -1
- package/dist/lib/lib/error-trigger.js +5 -4
- package/dist/lib/lib/error.d.ts.map +1 -1
- package/dist/lib/lib/error.js +61 -50
- package/dist/lib/lib/init.d.ts.map +1 -1
- package/dist/lib/lib/init.js +43 -15
- package/dist/lib/lib/net.d.ts.map +1 -1
- package/dist/lib/lib/net.js +195 -160
- package/dist/lib/lib/router-monitor.d.ts.map +1 -1
- package/dist/lib/lib/router-monitor.js +18 -11
- package/dist/lib/lib/rrweb.d.ts.map +1 -1
- package/dist/lib/lib/rrweb.js +12 -8
- package/dist/lib/lib/upload-worker-manager.d.ts.map +1 -1
- package/dist/lib/lib/upload-worker-manager.js +59 -42
- package/dist/lib/lib/uploader.d.ts.map +1 -1
- package/dist/lib/lib/uploader.js +54 -50
- package/dist/rlog-sdk.min.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,525 +1,245 @@
|
|
|
1
|
-
# rlog-sdk
|
|
1
|
+
# @antglobal/rlog-sdk
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
基于 [rrweb](https://github.com/rrweb-io/rrweb) 的用户行为录制与上报 SDK,用于 Web 应用的会话回放、错误追踪和行为分析。
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
SDK 现在支持从 CDN 获取动态配置,可以实时调整以下参数:
|
|
7
|
-
- 采集总开关
|
|
8
|
-
- Canvas 录制采样率
|
|
9
|
-
- 数据上传时间间隔
|
|
5
|
+
## 特性
|
|
10
6
|
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
- **全量录制** — 持续录制用户操作,定时上传
|
|
8
|
+
- **错误模式录制** — 仅在发生错误时上传前后时间窗口内的录制数据
|
|
9
|
+
- **多 Tab 隔离** — 每个标签页独立 Session,分布式锁保证数据安全
|
|
10
|
+
- **存储自动降级** — IndexedDB > LocalStorage > Memory
|
|
11
|
+
- **网络监控** — 拦截 XHR/Fetch,记录请求响应,支持黑白名单过滤
|
|
12
|
+
- **路由监控** — 监听 History API 和 Hash 变化,记录页面停留时间
|
|
13
|
+
- **Console 录制** — 可选的 console 输出捕获(rrweb 插件)
|
|
14
|
+
- **上传重试** — 失败自动重试,超过阈值停止录制并派发事件
|
|
15
|
+
- **Web Worker 上传** — 后台线程上传,不阻塞主线程
|
|
16
|
+
- **只采集不上报** — 可动态切换的 consume-only 模式
|
|
17
|
+
- **自定义事件** — 支持注入业务自定义事件到录制流
|
|
18
|
+
- **错误上报** — 全局 JS 错误和资源加载错误自动捕获
|
|
13
19
|
|
|
14
|
-
|
|
15
|
-
```json
|
|
16
|
-
{
|
|
17
|
-
"enable": true, // 采集总开关
|
|
18
|
-
"uploadInterval": 2000, // 数据上传时间间隔 (毫秒)
|
|
19
|
-
"configUpdateInterval": 300000, // 配置更新时间间隔 (毫秒),默认 5 分钟
|
|
20
|
-
"checkoutEveryNms": 5000, // 每N毫秒做一次全量快照,默认 5 秒
|
|
21
|
-
"captureMode": "full", // 采集模式: 'full' 全量采集(默认) | 'error' 错误采集
|
|
22
|
-
"packEvents": false, // 是否使用rrweb的packFn压缩事件数据,默认false
|
|
23
|
-
"clearStorageBeforeRecord": true, // 是否在开始录制前清空存储数据,默认true
|
|
24
|
-
"http": false, // 是否开启接口请求抓取,默认false
|
|
25
|
-
"maxRetryCount": 3, // 上传失败最大重试次数,默认3次
|
|
26
|
-
"consumeOnly": false, // 只采集不上报模式,默认false
|
|
27
|
-
"urlParamsToCache": [], // 需要从URL中缓存和上报的参数名称列表
|
|
28
|
-
|
|
29
|
-
// 事件采样配置
|
|
30
|
-
"sampling": {
|
|
31
|
-
"canvas": 15, // Canvas 采样率 (帧/秒),默认 15
|
|
32
|
-
"mousemove": 50, // 鼠标移动事件采样间隔 (毫秒),false表示不录制
|
|
33
|
-
"scroll": 100, // 滚动事件防抖间隔 (毫秒)
|
|
34
|
-
"input": "last" // 输入事件录制时机: 'all' 全部 | 'last' 只记录最终值
|
|
35
|
-
},
|
|
36
|
-
|
|
37
|
-
// rrweb 相关 CSS 类名配置
|
|
38
|
-
"ignoreClass": "rr-ignore", // 忽略采集的CSS类名
|
|
39
|
-
"blockClass": "rr-block", // 阻塞采集的CSS类名
|
|
40
|
-
"maskTextClass": "rr-mask", // 文本遮罩的CSS类名
|
|
41
|
-
|
|
42
|
-
// 错误采集模式配置 (仅在 captureMode='error' 时生效)
|
|
43
|
-
"errorCapture": {
|
|
44
|
-
"beforeDuration": 30000, // 错误发生前录制时长 (毫秒),默认 30000
|
|
45
|
-
"afterDuration": 30000, // 错误发生后继续录制时长 (毫秒),默认 30000
|
|
46
|
-
"httpErrorThreshold": 400 // HTTP 错误状态码阈值,>= 该值视为错误,默认 400
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
```
|
|
20
|
+
## 安装
|
|
50
21
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
{
|
|
54
|
-
"console": {
|
|
55
|
-
"enable": true, // 是否启用console录制,默认false
|
|
56
|
-
"level": ["log", "warn", "error", "info"], // 需要录制的console方法,默认['log', 'warn', 'error', 'info']
|
|
57
|
-
"lengthThreshold": 1000, // 最大录制条数,默认1000
|
|
58
|
-
"stringifyOptions": {
|
|
59
|
-
"stringLengthLimit": 500, // 单个值能转化的最大字符串长度,默认不限制
|
|
60
|
-
"numOfKeysLimit": 50, // 被序列化的js对象能够包含的最大key数量,默认50
|
|
61
|
-
"depthOfLimit": 4 // 对象能够拥有的最大深度(层数),默认4
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
22
|
+
```bash
|
|
23
|
+
npm install @antglobal/rlog-sdk
|
|
65
24
|
```
|
|
66
25
|
|
|
67
|
-
|
|
68
|
-
```json
|
|
69
|
-
{
|
|
70
|
-
"routerMonitor": {
|
|
71
|
-
"enable": false, // 是否启用路由监控,默认false
|
|
72
|
-
"trackHashChange": true, // 是否监听hash变化,默认true
|
|
73
|
-
"trackHistoryChange": true, // 是否监听History API变化,默认true
|
|
74
|
-
"trackPageStayTime": true, // 是否计算页面停留时间,默认true
|
|
75
|
-
"ignoreRoutes": [] // 需要忽略的路由列表
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
```
|
|
26
|
+
## 快速开始
|
|
79
27
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
- `debug`, `dir`, `dirxml`, `error`
|
|
83
|
-
- `group`, `groupCollapsed`, `groupEnd`, `info`
|
|
84
|
-
- `log`, `table`, `time`, `timeEnd`, `timeLog`, `trace`, `warn`
|
|
28
|
+
```typescript
|
|
29
|
+
import { init } from '@antglobal/rlog-sdk';
|
|
85
30
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
// 初始化时传入 CDN 配置 URL
|
|
89
|
-
Rlog.init({
|
|
31
|
+
const rlog = init({
|
|
32
|
+
serv: 'https://your-server.com/api/rlog/record',
|
|
90
33
|
appId: 'your-app-id',
|
|
91
|
-
|
|
92
|
-
cdnConfigUrl: 'https://your-cdn.com/rlog-config.json'
|
|
34
|
+
cdnConfigUrl: 'https://your-cdn.com/rlog-config.json', // 可选,远程动态配置
|
|
93
35
|
});
|
|
94
|
-
```
|
|
95
36
|
|
|
96
|
-
|
|
37
|
+
// 停止录制
|
|
38
|
+
rlog.cancel();
|
|
97
39
|
|
|
98
|
-
|
|
99
|
-
|
|
40
|
+
// 停止录制并清除本地数据
|
|
41
|
+
rlog.cancel({ clearData: true });
|
|
42
|
+
```
|
|
100
43
|
|
|
101
|
-
|
|
102
|
-
```javascript
|
|
103
|
-
// 设置全局自定义请求头
|
|
104
|
-
Rlog.setCustomHeaders({
|
|
105
|
-
'Authorization': 'Bearer your-token',
|
|
106
|
-
'X-Request-Id': 'unique-request-id',
|
|
107
|
-
'X-Client-Version': '1.0.0'
|
|
108
|
-
});
|
|
44
|
+
## API
|
|
109
45
|
|
|
110
|
-
|
|
111
|
-
Rlog.setWhiteListUrls([
|
|
112
|
-
'https://api.your-app.com',
|
|
113
|
-
'https://analytics.your-app.com'
|
|
114
|
-
]);
|
|
115
|
-
```
|
|
46
|
+
### 核心
|
|
116
47
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
## 代码结构优化
|
|
124
|
-
|
|
125
|
-
### 1. 入口文件精简
|
|
126
|
-
入口文件 [`src/index.ts`](src/index.ts) 已经优化得更加精简,只包含必要的导入和导出:
|
|
127
|
-
- 导入核心初始化函数
|
|
128
|
-
- 导出初始化接口
|
|
129
|
-
- 导出自定义事件接口
|
|
130
|
-
|
|
131
|
-
### 2. 模块化设计
|
|
132
|
-
项目现在采用更好的模块化设计,按功能域清晰划分:
|
|
133
|
-
|
|
134
|
-
#### 核心模块
|
|
135
|
-
- [`src/lib/init.ts`](src/lib/init.ts): 初始化逻辑,负责SDK的启动和配置
|
|
136
|
-
- [`src/lib/config.ts`](src/lib/config.ts): 配置管理,支持CDN动态配置
|
|
137
|
-
- [`src/lib/rrweb.ts`](src/lib/rrweb.ts): 录制功能,集成rrweb录制器
|
|
138
|
-
|
|
139
|
-
#### 数据管理模块
|
|
140
|
-
- [`src/lib/storage-manager.ts`](src/lib/storage-manager.ts): 存储管理,统一处理数据存储
|
|
141
|
-
- [`src/lib/drive/`](src/lib/drive/): 存储驱动层
|
|
142
|
-
- [`indexeddb-adapt.ts`](src/lib/drive/indexeddb-adapt.ts): IndexedDB存储适配器
|
|
143
|
-
- [`localstorage-adapt.ts`](src/lib/drive/localstorage-adapt.ts): LocalStorage存储适配器
|
|
144
|
-
- [`memory-adapt.ts`](src/lib/drive/memory-adapt.ts): 内存存储适配器
|
|
145
|
-
- [`storage-interface.ts`](src/lib/drive/storage-interface.ts): 存储接口定义
|
|
146
|
-
|
|
147
|
-
#### 网络通信模块
|
|
148
|
-
- [`src/lib/uploader.ts`](src/lib/uploader.ts): 数据上传逻辑,包含重试机制
|
|
149
|
-
- [`src/lib/request.ts`](src/lib/request.ts): 网络请求工具,封装HTTP请求
|
|
150
|
-
- [`src/lib/net.ts`](src/lib/net.ts): 网络监控,拦截和记录网络请求
|
|
151
|
-
|
|
152
|
-
#### 功能扩展模块
|
|
153
|
-
- [`src/lib/router-monitor.ts`](src/lib/router-monitor.ts): 路由监控,跟踪页面路由变化
|
|
154
|
-
- [`src/lib/api.ts`](src/lib/api.ts): API接口,提供外部调用接口
|
|
155
|
-
- [`src/lib/utils.ts`](src/lib/utils.ts): 工具函数,通用工具方法
|
|
156
|
-
- [`src/lib/error.ts`](src/lib/error.ts): 错误处理,统一的错误捕获和处理
|
|
157
|
-
|
|
158
|
-
#### 架构特点
|
|
159
|
-
- **分层架构**: 驱动层、服务层、接口层清晰分离
|
|
160
|
-
- **插件化设计**: 各模块可独立启用/禁用
|
|
161
|
-
- **高内聚低耦合**: 模块间通过明确定义的接口交互
|
|
162
|
-
- **可扩展性**: 易于添加新的存储驱动或功能模块
|
|
163
|
-
|
|
164
|
-
## 请求工具
|
|
165
|
-
|
|
166
|
-
### 1. 工具说明
|
|
167
|
-
项目中封装了兼容性更好的请求工具 [`src/lib/request.ts`](src/lib/request.ts),提供以下功能:
|
|
168
|
-
- 兼容性更好的 HTTP 请求(基于 XMLHttpRequest)
|
|
169
|
-
- 支持 GET/POST 方法
|
|
170
|
-
- 支持超时设置
|
|
171
|
-
- 统一的错误处理
|
|
172
|
-
|
|
173
|
-
### 2. 使用方式
|
|
174
|
-
```typescript
|
|
175
|
-
import { get, post, request } from './lib/request';
|
|
48
|
+
| 方法 | 说明 |
|
|
49
|
+
|------|------|
|
|
50
|
+
| `init({ serv, appId, cdnConfigUrl? })` | 初始化 SDK,返回 `{ cancel }` |
|
|
51
|
+
| `cancelRecord(options?)` | 停止录制(推荐使用 `rlog.cancel()`) |
|
|
52
|
+
| `addCustomEvent(tag, payload)` | 添加自定义事件到录制流 |
|
|
53
|
+
| `reportError(error)` | 手动上报错误(Error 对象或字符串) |
|
|
176
54
|
|
|
177
|
-
|
|
178
|
-
const response = await get('https://api.example.com/data');
|
|
55
|
+
### 网络配置
|
|
179
56
|
|
|
180
|
-
|
|
181
|
-
|
|
57
|
+
| 方法 | 说明 |
|
|
58
|
+
|------|------|
|
|
59
|
+
| `setCustomHeaders(headers)` | 设置全局自定义请求头(注入到 XHR/Fetch) |
|
|
60
|
+
| `getCustomHeaders()` | 获取当前自定义请求头 |
|
|
61
|
+
| `setWhiteListUrls(urls)` | 设置白名单 URL(仅监控这些请求) |
|
|
62
|
+
| `getWhiteListUrls()` | 获取白名单 |
|
|
63
|
+
| `setBlackListUrls(urls)` | 设置黑名单 URL(排除这些请求) |
|
|
64
|
+
| `getBlackListUrls()` | 获取黑名单 |
|
|
182
65
|
|
|
183
|
-
|
|
184
|
-
const response = await request('https://api.example.com/data', {
|
|
185
|
-
method: 'POST',
|
|
186
|
-
headers: { 'Content-Type': 'application/json' },
|
|
187
|
-
body: JSON.stringify({ key: 'value' }),
|
|
188
|
-
timeout: 5000
|
|
189
|
-
});
|
|
190
|
-
```
|
|
66
|
+
> 黑名单优先于白名单。白名单为空时监控所有非黑名单请求。
|
|
191
67
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
### 1. Canvas录制动态采样
|
|
195
|
-
**自适应采样算法**:
|
|
196
|
-
```ts
|
|
197
|
-
class SamplingController {
|
|
198
|
-
// 设备性能评估
|
|
199
|
-
static getSamplingRate(): number {
|
|
200
|
-
const isMobile = /Mobi/.test(navigator.userAgent);
|
|
201
|
-
const deviceMemory = (navigator as any).deviceMemory || 4;
|
|
202
|
-
|
|
203
|
-
// 动态计算采样率:
|
|
204
|
-
// 1. 根据设备类型选择基准值
|
|
205
|
-
// 2. 根据内存大小动态调整(4GB基准,每增加1GB降低1帧)
|
|
206
|
-
// 3. 最低不低于5帧保证录制可用性
|
|
207
|
-
const baseRate = isMobile ? 8 : 15;
|
|
208
|
-
const memoryFactor = Math.max(0, 4 - (deviceMemory - 4));
|
|
209
|
-
|
|
210
|
-
return Math.max(5, baseRate - memoryFactor);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// 实时性能监控(每5秒检测一次)
|
|
214
|
-
static startPerformanceMonitor() {
|
|
215
|
-
setInterval(() => {
|
|
216
|
-
const fps = this.calculateFPS();
|
|
217
|
-
if (fps < 15) { // 低性能预警
|
|
218
|
-
this.throttleRecording();
|
|
219
|
-
} else if (fps > 25) { // 高性能提升
|
|
220
|
-
this.boostSampling();
|
|
221
|
-
}
|
|
222
|
-
}, 5000);
|
|
223
|
-
}
|
|
224
|
-
}
|
|
68
|
+
### 路由监控
|
|
225
69
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
```
|
|
70
|
+
| 方法 | 说明 |
|
|
71
|
+
|------|------|
|
|
72
|
+
| `updateRouterConfig(config)` | 更新路由监控配置 |
|
|
73
|
+
| `getCurrentRouteInfo()` | 获取当前路由信息 |
|
|
74
|
+
| `manualRouteChange(newRoute, prevRoute?)` | 手动触发路由变化事件 |
|
|
75
|
+
| `stopRouterMonitor()` | 停止路由监控 |
|
|
233
76
|
|
|
234
|
-
|
|
235
|
-
| 指标 | 目标值 | 监控方式 |
|
|
236
|
-
|------|-------|---------|
|
|
237
|
-
| FPS | ≥15 | requestAnimationFrame |
|
|
238
|
-
| 内存占用 | ≤15MB | performance.memory |
|
|
239
|
-
| CPU使用率 | ≤30% | navigator.hardwareConcurrency |
|
|
240
|
-
|
|
241
|
-
### 2. 存储性能优化
|
|
242
|
-
**批量写入策略**:
|
|
243
|
-
```ts
|
|
244
|
-
// IndexedDBAdapter增强
|
|
245
|
-
async push(deviceId: string, data: any): Promise<void> {
|
|
246
|
-
// 实现批量缓冲
|
|
247
|
-
if (!this.writeBuffer) {
|
|
248
|
-
this.writeBuffer = [];
|
|
249
|
-
// 500ms批量提交
|
|
250
|
-
setTimeout(() => this.flushBuffer(), 500);
|
|
251
|
-
}
|
|
252
|
-
this.writeBuffer.push({ deviceId, data });
|
|
253
|
-
}
|
|
77
|
+
### Consume-Only 模式
|
|
254
78
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
// 使用游标批量更新
|
|
262
|
-
const cursor = await store.openCursor();
|
|
263
|
-
while (cursor) {
|
|
264
|
-
const updated = this.writeBuffer.find(
|
|
265
|
-
item => item.deviceId === cursor.key
|
|
266
|
-
);
|
|
267
|
-
if (updated) {
|
|
268
|
-
await cursor.update({
|
|
269
|
-
...cursor.value,
|
|
270
|
-
data: [...cursor.value.data, ...updated.data]
|
|
271
|
-
});
|
|
272
|
-
}
|
|
273
|
-
await cursor.continue();
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
this.writeBuffer = null;
|
|
277
|
-
}
|
|
278
|
-
```
|
|
79
|
+
| 方法 | 说明 |
|
|
80
|
+
|------|------|
|
|
81
|
+
| `setConsumeOnlyMode(enable)` | 开启/关闭只采集不上报 |
|
|
82
|
+
| `isConsumeOnlyMode()` | 查询当前是否为只采集模式 |
|
|
83
|
+
| `toggleConsumeOnlyMode()` | 切换模式,返回新状态 |
|
|
279
84
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
### 1. 加密架构设计
|
|
283
|
-
```mermaid
|
|
284
|
-
sequenceDiagram
|
|
285
|
-
participant SDK
|
|
286
|
-
participant Crypto
|
|
287
|
-
participant Storage
|
|
288
|
-
|
|
289
|
-
SDK->>Crypto: setEncryptionKey() 设置密钥
|
|
290
|
-
SDK->>Crypto: encryptData() 请求加密
|
|
291
|
-
Crypto->>Crypto: 生成IV (12字节)
|
|
292
|
-
Crypto->>Crypto: 使用AES-GCM加密
|
|
293
|
-
Crypto-->>SDK: 返回Base64加密数据
|
|
294
|
-
SDK->>Storage: 存储加密数据
|
|
295
|
-
```
|
|
85
|
+
### 调试工具
|
|
296
86
|
|
|
297
|
-
|
|
298
|
-
```ts
|
|
299
|
-
// 加密配置接口
|
|
300
|
-
interface EncryptionConfig {
|
|
301
|
-
enabled: boolean; // 加密开关
|
|
302
|
-
algorithm: 'AES-GCM' | 'AES-CBC'; // 算法选择
|
|
303
|
-
keyLength: 128 | 192 | 256; // 密钥长度
|
|
304
|
-
autoRotate: boolean; // 密钥自动轮换
|
|
305
|
-
rotationInterval: number; // 轮换间隔(小时)
|
|
306
|
-
}
|
|
87
|
+
通过 `debug` 对象访问:
|
|
307
88
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
this.key = key;
|
|
321
|
-
if (this.config.autoRotate) {
|
|
322
|
-
this.scheduleKeyRotation();
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
async encrypt(data: any): Promise<string> {
|
|
327
|
-
if (!this.config.enabled || !this.key) {
|
|
328
|
-
return JSON.stringify(data);
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
332
|
-
const encoder = new TextEncoder();
|
|
333
|
-
const encrypted = await crypto.subtle.encrypt(
|
|
334
|
-
{
|
|
335
|
-
name: this.config.algorithm,
|
|
336
|
-
iv
|
|
337
|
-
},
|
|
338
|
-
this.key,
|
|
339
|
-
encoder.encode(JSON.stringify(data))
|
|
340
|
-
);
|
|
341
|
-
|
|
342
|
-
// 返回包含元数据的加密数据
|
|
343
|
-
return JSON.stringify({
|
|
344
|
-
v: 1, // 版本号
|
|
345
|
-
alg: this.config.algorithm,
|
|
346
|
-
iv: arrayBufferToBase64(iv),
|
|
347
|
-
data: arrayBufferToBase64(encrypted)
|
|
348
|
-
});
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
private scheduleKeyRotation(): void {
|
|
352
|
-
setInterval(async () => {
|
|
353
|
-
const newKey = await this.generateKey();
|
|
354
|
-
await this.rotateKey(newKey);
|
|
355
|
-
}, this.config.rotationInterval * 3600000);
|
|
356
|
-
}
|
|
357
|
-
}
|
|
89
|
+
```typescript
|
|
90
|
+
import { debug } from '@antglobal/rlog-sdk';
|
|
91
|
+
|
|
92
|
+
debug.getStorageAdapterType(); // 当前存储类型: 'indexeddb' | 'localstorage' | 'memory'
|
|
93
|
+
debug.getDeviceId(); // 设备 ID
|
|
94
|
+
debug.getSessionId(); // 当前 Tab 的 Session ID
|
|
95
|
+
debug.getStorageKey(); // 存储键 (deviceId_sessionId)
|
|
96
|
+
debug.resetUploadState(); // 重置上传状态
|
|
97
|
+
debug.getStorageState(); // Promise<{ adapterType, eventCount }>
|
|
98
|
+
debug.clearStorage(); // Promise<void> 清空存储
|
|
99
|
+
debug.getConfig(); // 当前 CDN 配置
|
|
100
|
+
debug.shouldRecordUrl(url); // 判断 URL 是否会被网络监控记录
|
|
358
101
|
```
|
|
359
102
|
|
|
360
|
-
|
|
361
|
-
```ts
|
|
362
|
-
// 脱敏配置
|
|
363
|
-
interface SanitizationRule {
|
|
364
|
-
selector: string; // CSS选择器
|
|
365
|
-
type: 'mask' | 'hash' | 'remove'; // 脱敏类型
|
|
366
|
-
hashAlgorithm?: 'SHA-1' | 'SHA-256'; // 哈希算法
|
|
367
|
-
maskChar?: string; // 掩码字符
|
|
368
|
-
maskLength?: number; // 掩码长度
|
|
369
|
-
}
|
|
103
|
+
## CDN 动态配置
|
|
370
104
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
105
|
+
通过 `cdnConfigUrl` 传入远程 JSON 配置,SDK 会定期拉取更新。完整配置项:
|
|
106
|
+
|
|
107
|
+
```json
|
|
108
|
+
{
|
|
109
|
+
"enable": false,
|
|
110
|
+
"uploadInterval": 2000,
|
|
111
|
+
"configUpdateInterval": 300000,
|
|
112
|
+
"captureMode": "full",
|
|
113
|
+
"packEvents": false,
|
|
114
|
+
"clearStorageBeforeRecord": true,
|
|
115
|
+
"http": false,
|
|
116
|
+
"maxRetryCount": 3,
|
|
117
|
+
"consumeOnly": false,
|
|
118
|
+
"urlParamsToCache": [],
|
|
119
|
+
"sampling": {
|
|
120
|
+
"canvas": 15,
|
|
121
|
+
"mousemove": 50,
|
|
122
|
+
"scroll": 100,
|
|
123
|
+
"input": "last"
|
|
124
|
+
},
|
|
125
|
+
"ignoreClass": "rr-ignore",
|
|
126
|
+
"blockClass": "rr-block",
|
|
127
|
+
"maskTextClass": "rr-mask",
|
|
128
|
+
"console": {
|
|
129
|
+
"enable": false,
|
|
130
|
+
"level": ["log", "warn", "error", "info"],
|
|
131
|
+
"lengthThreshold": 1000,
|
|
132
|
+
"stringifyOptions": {
|
|
133
|
+
"stringLengthLimit": 500,
|
|
134
|
+
"numOfKeysLimit": 50,
|
|
135
|
+
"depthOfLimit": 4
|
|
384
136
|
}
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
result.textContent = this.applyMask(
|
|
398
|
-
element.textContent || '',
|
|
399
|
-
rule.maskChar || '*',
|
|
400
|
-
rule.maskLength || 8
|
|
401
|
-
);
|
|
402
|
-
break;
|
|
403
|
-
case 'hash':
|
|
404
|
-
result.textContent = this.hashContent(
|
|
405
|
-
element.textContent || '',
|
|
406
|
-
rule.hashAlgorithm || 'SHA-256'
|
|
407
|
-
);
|
|
408
|
-
break;
|
|
409
|
-
case 'remove':
|
|
410
|
-
result.remove();
|
|
411
|
-
break;
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
});
|
|
415
|
-
});
|
|
416
|
-
|
|
417
|
-
return result;
|
|
137
|
+
},
|
|
138
|
+
"routerMonitor": {
|
|
139
|
+
"enable": false,
|
|
140
|
+
"trackHashChange": true,
|
|
141
|
+
"trackHistoryChange": true,
|
|
142
|
+
"trackPageStayTime": true,
|
|
143
|
+
"ignoreRoutes": []
|
|
144
|
+
},
|
|
145
|
+
"errorCapture": {
|
|
146
|
+
"beforeDuration": 30000,
|
|
147
|
+
"afterDuration": 30000,
|
|
148
|
+
"httpErrorThreshold": 400
|
|
418
149
|
}
|
|
419
150
|
}
|
|
420
151
|
```
|
|
421
152
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
153
|
+
### 配置说明
|
|
154
|
+
|
|
155
|
+
| 字段 | 默认值 | 说明 |
|
|
156
|
+
|------|--------|------|
|
|
157
|
+
| `enable` | `false` | 采集总开关 |
|
|
158
|
+
| `uploadInterval` | `2000` | 上传间隔(ms) |
|
|
159
|
+
| `configUpdateInterval` | `300000` | 配置刷新间隔(ms) |
|
|
160
|
+
| `captureMode` | `"full"` | `"full"` 全量录制 / `"error"` 错误触发录制 |
|
|
161
|
+
| `packEvents` | `false` | 是否压缩事件数据 |
|
|
162
|
+
| `clearStorageBeforeRecord` | `true` | 录制前是否清空存储 |
|
|
163
|
+
| `http` | `false` | 是否开启网络请求监控 |
|
|
164
|
+
| `maxRetryCount` | `3` | 上传失败最大重试次数 |
|
|
165
|
+
| `consumeOnly` | `false` | 只采集不上报 |
|
|
166
|
+
| `sampling.canvas` | `15` | Canvas 采样率(fps) |
|
|
167
|
+
| `sampling.mousemove` | `50` | 鼠标移动采样间隔(ms) |
|
|
168
|
+
| `sampling.scroll` | `100` | 滚动防抖间隔(ms) |
|
|
169
|
+
| `sampling.input` | `"last"` | `"all"` 全部 / `"last"` 仅最终值 |
|
|
170
|
+
| `errorCapture.beforeDuration` | `30000` | 错误前录制时长(ms) |
|
|
171
|
+
| `errorCapture.afterDuration` | `30000` | 错误后继续录制时长(ms) |
|
|
172
|
+
| `errorCapture.httpErrorThreshold` | `400` | HTTP 错误状态码阈值 |
|
|
173
|
+
|
|
174
|
+
## 采集模式
|
|
175
|
+
|
|
176
|
+
### Full 模式(默认)
|
|
177
|
+
|
|
178
|
+
持续录制所有用户操作,按 `uploadInterval` 定时上传,每批最多 100 条事件。
|
|
179
|
+
|
|
180
|
+
### Error 模式
|
|
181
|
+
|
|
182
|
+
事件持续写入本地存储,仅在错误发生时触发上传。上传时间窗口为 `[now - beforeDuration, now + afterDuration]`。若 `afterDuration` 期间再次发生错误,窗口自动延长。
|
|
183
|
+
|
|
184
|
+
## 架构
|
|
185
|
+
|
|
186
|
+
```
|
|
187
|
+
src/
|
|
188
|
+
├── index.ts # 公共 API 导出
|
|
189
|
+
└── lib/
|
|
190
|
+
├── init.ts # SDK 初始化与销毁
|
|
191
|
+
├── config.ts # CDN 配置管理
|
|
192
|
+
├── rrweb.ts # rrweb 录制启动
|
|
193
|
+
├── storage-manager.ts # 存储管理(序列化队列)
|
|
194
|
+
├── uploader.ts # 上传调度(全量/错误模式)
|
|
195
|
+
├── upload-worker-manager.ts # Web Worker 管理
|
|
196
|
+
├── upload-worker.ts # Worker 上传逻辑
|
|
197
|
+
├── net.ts # 网络监控(XHR/Fetch 拦截)
|
|
198
|
+
├── api.ts # 上传接口
|
|
199
|
+
├── request.ts # HTTP 请求工具
|
|
200
|
+
├── router-monitor.ts # 路由监控
|
|
201
|
+
├── error.ts # 错误捕获
|
|
202
|
+
├── error-trigger.ts # 错误模式状态机
|
|
203
|
+
├── utils.ts # 工具函数
|
|
204
|
+
├── logger.ts # 日志输出
|
|
205
|
+
├── constants.ts # 常量定义
|
|
206
|
+
└── drive/
|
|
207
|
+
├── storage-interface.ts # StorageAdapter 接口
|
|
208
|
+
├── indexeddb-adapt.ts # IndexedDB 适配器
|
|
209
|
+
├── localstorage-adapt.ts # LocalStorage 适配器(含分布式锁)
|
|
210
|
+
├── memory-adapt.ts # Memory 适配器
|
|
211
|
+
└── safe-storage.ts # 安全 KV 存储(降级到内存)
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## 多 Tab 并发
|
|
215
|
+
|
|
216
|
+
- 每个标签页分配唯一 `sessionId`,存储键为 `deviceId_sessionId`
|
|
217
|
+
- Session 注册表追踪活跃标签页
|
|
218
|
+
- SDK 初始化时自动清理孤儿 session 的残留数据
|
|
219
|
+
- LocalStorage 操作通过分布式锁(CAS + 超时)保证原子性
|
|
220
|
+
|
|
221
|
+
## 上传重试
|
|
222
|
+
|
|
223
|
+
上传失败时事件回退到队列头部,下个周期重试。连续失败达到 `maxRetryCount` 后:
|
|
224
|
+
1. 派发 `rlog-upload-failure` 自定义事件(window + rrweb)
|
|
225
|
+
2. 停止 rrweb 录制
|
|
226
|
+
3. 停止上传循环
|
|
227
|
+
|
|
228
|
+
监听失败事件:
|
|
428
229
|
|
|
429
|
-
**参数:**
|
|
430
|
-
- `urls`: string[] - URL列表,支持完整URL和相对路径
|
|
431
|
-
|
|
432
|
-
**示例:**
|
|
433
230
|
```javascript
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
'/ebank/api/v1/rlog/record'
|
|
438
|
-
]);
|
|
231
|
+
window.addEventListener('rlog-upload-failure', (e) => {
|
|
232
|
+
console.log('上传失败', e.detail);
|
|
233
|
+
});
|
|
439
234
|
```
|
|
440
235
|
|
|
441
|
-
|
|
442
|
-
设置黑名单URL列表,这些接口将不会被记录和监控。
|
|
443
|
-
|
|
444
|
-
**参数:**
|
|
445
|
-
- `urls`: string[] - URL列表,支持完整URL和相对路径
|
|
236
|
+
## 开发
|
|
446
237
|
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
'https://api.sensitive.com/user-info',
|
|
451
|
-
'/api/auth/token',
|
|
452
|
-
'/ebank/api/v1/rlog/record' // 避免循环上报
|
|
453
|
-
]);
|
|
238
|
+
```bash
|
|
239
|
+
npm run dev # 开发模式
|
|
240
|
+
npm run build # 构建(ESM + CJS)
|
|
454
241
|
```
|
|
455
242
|
|
|
456
|
-
|
|
457
|
-
获取当前白名单URL列表。
|
|
458
|
-
|
|
459
|
-
**返回值:**
|
|
460
|
-
- string[] - 当前白名单URL列表
|
|
461
|
-
|
|
462
|
-
#### getBlackListUrls(): string[]
|
|
463
|
-
获取当前黑名单URL列表。
|
|
464
|
-
|
|
465
|
-
**返回值:**
|
|
466
|
-
- string[] - 当前黑名单URL列表
|
|
467
|
-
|
|
468
|
-
### 2. 配置优先级
|
|
469
|
-
网络接口的过滤优先级如下:
|
|
470
|
-
1. **黑名单优先**:如果URL在黑名单中,无论是否在白名单都不会被记录
|
|
471
|
-
2. **白名单过滤**:只有在白名单中的URL才会被记录(如果不在黑名单中)
|
|
472
|
-
3. **默认行为**:如果白名单为空,则记录所有不在黑名单中的请求
|
|
473
|
-
|
|
474
|
-
### 3. 路径匹配规则
|
|
475
|
-
|
|
476
|
-
#### 完整URL匹配
|
|
477
|
-
```javascript
|
|
478
|
-
// 匹配以指定URL开头的请求
|
|
479
|
-
'https://api.example.com/v1/users' // 匹配 https://api.example.com/v1/users/123
|
|
480
|
-
```
|
|
481
|
-
|
|
482
|
-
#### 相对路径匹配
|
|
483
|
-
```javascript
|
|
484
|
-
// 匹配以指定路径开头的请求
|
|
485
|
-
'/api/v1/' // 匹配 /api/v1/users, /api/v1/data 等
|
|
486
|
-
'/ebank/api/v1/rlog/record' // 精确匹配该路径
|
|
487
|
-
```
|
|
488
|
-
|
|
489
|
-
#### 域名匹配
|
|
490
|
-
```javascript
|
|
491
|
-
// 匹配指定域名下的所有请求
|
|
492
|
-
'api.example.com' // 匹配该域名下的所有接口
|
|
493
|
-
```
|
|
243
|
+
## License
|
|
494
244
|
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
### 1. 路由监控功能
|
|
498
|
-
- [详细使用指南](docs/ROUTER_MONITOR_GUIDE.md) - 包含完整的路由监控配置和使用示例
|
|
499
|
-
- [在线示例](examples/test-router-monitor.html) - 可直接在浏览器中测试路由监控功能
|
|
500
|
-
|
|
501
|
-
### 2. 上传重试机制
|
|
502
|
-
- [重试逻辑指南](docs/RETRY_LOGIC_GUIDE.md) - 上传失败后的重试策略和配置
|
|
503
|
-
- [重试测试示例](examples/test-retry-logic.html) - 测试上传重试功能
|
|
504
|
-
|
|
505
|
-
### 3. 消费模式
|
|
506
|
-
- [消费模式指南](docs/CONSUME_ONLY_MODE_GUIDE.md) - 仅消费数据的轻量级使用模式
|
|
507
|
-
- [消费模式示例](examples/consume-only-mode.html) - 消费模式的实际应用示例
|
|
508
|
-
|
|
509
|
-
### 4. 取消方法
|
|
510
|
-
- [取消方法指南](docs/CANCEL_METHOD_GUIDE.md) - 如何正确停止录制和数据收集
|
|
511
|
-
- [取消方法示例](examples/test-cancel.html) - 演示如何正确停止录制和数据收集
|
|
512
|
-
|
|
513
|
-
## 改进建议实施状态
|
|
514
|
-
| 改进项 | 实施状态 | 完成度 |
|
|
515
|
-
|-------|----------|--------|
|
|
516
|
-
| IndexedDB降级修复 | 已完成 | 100% |
|
|
517
|
-
| 动态采样率调整 | 已完成 | 100% |
|
|
518
|
-
| 路由监控功能 | 已完成 | 100% |
|
|
519
|
-
| 错误处理优化 | 设计中 | 50% |
|
|
520
|
-
| 上传重试机制 | 已完成 | 100% |
|
|
521
|
-
| 消费模式支持 | 已完成 | 100% |
|
|
522
|
-
| 取消方法支持 | 已完成 | 100% |
|
|
523
|
-
| 移动端触控增强 | 未开始 | 0% |
|
|
524
|
-
| 数据加密模块 | 设计完成 | 50% |
|
|
525
|
-
| 数据压缩模块 | 未开始 | 0% |
|
|
245
|
+
MIT
|