@be-link/cls-logger 1.0.1-beta.22 → 1.0.1-beta.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 +161 -11
- package/dist/ClsLoggerCore.d.ts +5 -2
- package/dist/ClsLoggerCore.d.ts.map +1 -1
- package/dist/index.esm.js +53 -22
- package/dist/index.js +53 -22
- package/dist/index.umd.js +53 -22
- package/dist/mini.esm.js +53 -22
- package/dist/mini.js +53 -22
- package/dist/types.d.ts +10 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/web.esm.js +53 -22
- package/dist/web.js +53 -22
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -144,6 +144,7 @@ clsLogger.init({
|
|
|
144
144
|
maxSize: 50, // 队列达到此数量立即发送,默认 20
|
|
145
145
|
intervalMs: 1000, // 定时批量发送间隔,默认 500ms
|
|
146
146
|
startupDelayMs: 3000, // 启动合并窗口,默认 0(不启用)
|
|
147
|
+
startupMaxSize: 500, // 启动窗口内的队列阈值,默认 maxSize*10(至少200)
|
|
147
148
|
useIdleCallback: true, // 使用浏览器空闲时间上报,默认 false
|
|
148
149
|
idleTimeout: 3000, // 空闲回调超时时间,默认 3000ms
|
|
149
150
|
},
|
|
@@ -224,13 +225,19 @@ clsLogger.init({
|
|
|
224
225
|
clsLogger.init({
|
|
225
226
|
batch: {
|
|
226
227
|
startupDelayMs: 3000, // 启动 3 秒内的日志合并发送
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
228
|
+
startupMaxSize: 500, // 启动窗口内允许更多日志积累(防止 maxSize 打断合并)
|
|
229
|
+
maxSize: 100, // 正常运行时的队列阈值
|
|
230
|
+
intervalMs: 2000, // 延长批量间隔
|
|
231
|
+
useIdleCallback: true, // 等待延迟后在浏览器空闲时上报
|
|
232
|
+
},
|
|
233
|
+
performanceMonitor: {
|
|
234
|
+
resourceTiming: false, // 关闭资源监控(首屏资源多时可大幅减少日志量)
|
|
230
235
|
},
|
|
231
236
|
});
|
|
232
237
|
```
|
|
233
238
|
|
|
239
|
+
> **说明**:`startupMaxSize` 确保启动窗口内不会因为 `maxSize` 被频繁触发而打断日志合并。
|
|
240
|
+
|
|
234
241
|
### 场景 2:减少资源监控日志
|
|
235
242
|
|
|
236
243
|
开发环境资源过多,或生产环境不需要全量资源监控:
|
|
@@ -274,31 +281,174 @@ clsLogger.init({
|
|
|
274
281
|
```ts
|
|
275
282
|
clsLogger.init({
|
|
276
283
|
batch: {
|
|
277
|
-
|
|
284
|
+
startupDelayMs: 3000, // 启动合并窗口
|
|
285
|
+
startupMaxSize: 500, // 启动窗口内的队列阈值
|
|
286
|
+
maxSize: 100, // 正常运行时的队列阈值
|
|
278
287
|
intervalMs: 2000, // 延长批量间隔
|
|
279
|
-
useIdleCallback: true, //
|
|
288
|
+
useIdleCallback: true, // 空闲时上报,不阻塞主线程
|
|
280
289
|
},
|
|
281
290
|
performanceMonitor: {
|
|
282
291
|
resourceTiming: false, // 关闭资源监控
|
|
283
292
|
},
|
|
284
293
|
requestMonitor: {
|
|
285
294
|
sampleRate: 0.1, // 请求监控采样 10%
|
|
295
|
+
ignoreUrls: [
|
|
296
|
+
/heartbeat/i, // 忽略心跳请求
|
|
297
|
+
/polling/i, // 忽略轮询请求
|
|
298
|
+
/danmaku|barrage/i, // 忽略弹幕请求
|
|
299
|
+
],
|
|
286
300
|
},
|
|
287
301
|
});
|
|
288
302
|
```
|
|
289
303
|
|
|
304
|
+
## 最佳实践
|
|
305
|
+
|
|
306
|
+
### 1. 批量配置的正确姿势
|
|
307
|
+
|
|
308
|
+
#### `useIdleCallback` 工作原理
|
|
309
|
+
|
|
310
|
+
当 `useIdleCallback: true` 时,SDK 的上报流程为:
|
|
311
|
+
|
|
312
|
+
```
|
|
313
|
+
日志产生 → 等待 intervalMs/startupDelayMs → 等待浏览器空闲 → 发送
|
|
314
|
+
↑ 最小等待时间 ↑ 在 idleTimeout 内空闲就发送
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
**关键点**:
|
|
318
|
+
|
|
319
|
+
- `intervalMs`/`startupDelayMs` 是**最小等待时间**,保证不会过早发送
|
|
320
|
+
- `idleTimeout` 是空闲等待的**最大时间**,防止浏览器一直繁忙导致日志积压
|
|
321
|
+
|
|
322
|
+
#### `startupMaxSize` 的作用
|
|
323
|
+
|
|
324
|
+
启动阶段(`startupDelayMs` 窗口内)日志量通常很大(性能指标、资源加载等),如果 `maxSize` 设置过小,会频繁触发队列满而打断合并:
|
|
325
|
+
|
|
326
|
+
```ts
|
|
327
|
+
// ❌ 错误配置:startupDelayMs 会被 maxSize 打断
|
|
328
|
+
batch: {
|
|
329
|
+
startupDelayMs: 3000,
|
|
330
|
+
maxSize: 20, // 启动期间可能产生 200+ 条日志,会触发 10+ 次发送
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// ✅ 正确配置:使用 startupMaxSize 保护启动窗口
|
|
334
|
+
batch: {
|
|
335
|
+
startupDelayMs: 3000,
|
|
336
|
+
maxSize: 20,
|
|
337
|
+
startupMaxSize: 500, // 启动期间允许更多日志积累
|
|
338
|
+
}
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
### 2. 推荐配置模板
|
|
342
|
+
|
|
343
|
+
#### 普通 H5 页面
|
|
344
|
+
|
|
345
|
+
```ts
|
|
346
|
+
clsLogger.init({
|
|
347
|
+
batch: {
|
|
348
|
+
startupDelayMs: 2000,
|
|
349
|
+
startupMaxSize: 300,
|
|
350
|
+
maxSize: 50,
|
|
351
|
+
intervalMs: 1000,
|
|
352
|
+
useIdleCallback: true,
|
|
353
|
+
idleTimeout: 3000,
|
|
354
|
+
},
|
|
355
|
+
});
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
#### 首屏性能敏感(电商、活动页)
|
|
359
|
+
|
|
360
|
+
```ts
|
|
361
|
+
clsLogger.init({
|
|
362
|
+
batch: {
|
|
363
|
+
startupDelayMs: 5000, // 更长的启动窗口
|
|
364
|
+
startupMaxSize: 500,
|
|
365
|
+
maxSize: 100,
|
|
366
|
+
intervalMs: 2000,
|
|
367
|
+
useIdleCallback: true,
|
|
368
|
+
idleTimeout: 5000,
|
|
369
|
+
},
|
|
370
|
+
performanceMonitor: {
|
|
371
|
+
sampleRate: 0.3, // 性能监控采样
|
|
372
|
+
},
|
|
373
|
+
});
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
#### 高频交互页面(直播间、游戏)
|
|
377
|
+
|
|
378
|
+
```ts
|
|
379
|
+
clsLogger.init({
|
|
380
|
+
batch: {
|
|
381
|
+
startupDelayMs: 3000,
|
|
382
|
+
startupMaxSize: 500,
|
|
383
|
+
maxSize: 100,
|
|
384
|
+
intervalMs: 3000, // 更长的间隔
|
|
385
|
+
useIdleCallback: true,
|
|
386
|
+
idleTimeout: 5000,
|
|
387
|
+
},
|
|
388
|
+
performanceMonitor: {
|
|
389
|
+
resourceTiming: false, // 关闭资源监控
|
|
390
|
+
},
|
|
391
|
+
requestMonitor: {
|
|
392
|
+
sampleRate: 0.1, // 请求监控采样
|
|
393
|
+
ignoreUrls: [/heartbeat/, /polling/, /danmaku/],
|
|
394
|
+
},
|
|
395
|
+
});
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
### 3. 日志量优化技巧
|
|
399
|
+
|
|
400
|
+
| 优化项 | 配置 | 效果 |
|
|
401
|
+
| ------------ | ------------------------------------------ | ------------------------- |
|
|
402
|
+
| 关闭资源监控 | `performanceMonitor.resourceTiming: false` | 减少 80%+ 的 perf 日志 |
|
|
403
|
+
| 性能采样 | `performanceMonitor.sampleRate: 0.1` | 只采集 10% 用户的性能数据 |
|
|
404
|
+
| 请求采样 | `requestMonitor.sampleRate: 0.1` | 只采集 10% 的请求日志 |
|
|
405
|
+
| 忽略高频请求 | `requestMonitor.ignoreUrls: [/heartbeat/]` | 过滤心跳等高频请求 |
|
|
406
|
+
| 错误去重 | `errorMonitor.dedupeWindowMs: 5000` | 5秒内相同错误只上报一次 |
|
|
407
|
+
|
|
408
|
+
### 4. 常见问题排查
|
|
409
|
+
|
|
410
|
+
#### 问题:首屏请求过多
|
|
411
|
+
|
|
412
|
+
**现象**:页面加载时发出 10+ 个日志请求
|
|
413
|
+
|
|
414
|
+
**排查步骤**:
|
|
415
|
+
|
|
416
|
+
1. 检查 `startupDelayMs` 是否配置
|
|
417
|
+
2. 检查 `startupMaxSize` 是否足够大(建议 300-500)
|
|
418
|
+
3. 检查 `resourceTiming` 是否需要开启(这是日志量最大的来源)
|
|
419
|
+
|
|
420
|
+
#### 问题:日志延迟过高
|
|
421
|
+
|
|
422
|
+
**现象**:日志上报明显滞后
|
|
423
|
+
|
|
424
|
+
**排查步骤**:
|
|
425
|
+
|
|
426
|
+
1. 检查 `useIdleCallback` + `idleTimeout` 配置
|
|
427
|
+
2. 检查 `intervalMs` 是否设置过大
|
|
428
|
+
3. 考虑关闭 `useIdleCallback` 或减小 `idleTimeout`
|
|
429
|
+
|
|
430
|
+
#### 问题:页面关闭丢日志
|
|
431
|
+
|
|
432
|
+
**现象**:用户快速关闭页面时日志丢失
|
|
433
|
+
|
|
434
|
+
**说明**:SDK 已通过 `sendBeacon` 处理此场景,如仍有丢失:
|
|
435
|
+
|
|
436
|
+
1. 检查日志是否在 `memoryQueue` 中(可能还未触发 flush)
|
|
437
|
+
2. 考虑对关键日志使用 `{ immediate: true }`
|
|
438
|
+
|
|
290
439
|
## 功能说明
|
|
291
440
|
|
|
292
441
|
### 1. 批量上报机制
|
|
293
442
|
|
|
294
443
|
SDK 使用内存队列批量上报日志:
|
|
295
444
|
|
|
296
|
-
| 触发条件 | 说明
|
|
297
|
-
| -------- |
|
|
298
|
-
| 队列满 | 达到 `maxSize`
|
|
299
|
-
| 定时器 | 每 `intervalMs` 毫秒发送一次
|
|
300
|
-
| 页面关闭 | `visibilitychange` + `pagehide` 触发 `sendBeacon` 发送
|
|
301
|
-
| 启动合并 | `startupDelayMs` 窗口内的日志尽量合并
|
|
445
|
+
| 触发条件 | 说明 |
|
|
446
|
+
| -------- | ------------------------------------------------------- |
|
|
447
|
+
| 队列满 | 达到 `maxSize`(启动窗口内为 `startupMaxSize`)立即发送 |
|
|
448
|
+
| 定时器 | 每 `intervalMs` 毫秒发送一次 |
|
|
449
|
+
| 页面关闭 | `visibilitychange` + `pagehide` 触发 `sendBeacon` 发送 |
|
|
450
|
+
| 启动合并 | `startupDelayMs` 窗口内的日志尽量合并 |
|
|
451
|
+
| 空闲上报 | `useIdleCallback` 开启时,等待浏览器空闲后发送 |
|
|
302
452
|
|
|
303
453
|
### 2. 即时上报 vs 批量上报
|
|
304
454
|
|
package/dist/ClsLoggerCore.d.ts
CHANGED
|
@@ -38,8 +38,10 @@ export declare abstract class ClsLoggerCore {
|
|
|
38
38
|
protected batchTimerDueAt: number | null;
|
|
39
39
|
protected initTs: number;
|
|
40
40
|
protected startupDelayMs: number;
|
|
41
|
+
protected startupMaxSize: number;
|
|
41
42
|
protected useIdleCallback: boolean;
|
|
42
43
|
protected idleTimeout: number;
|
|
44
|
+
protected pendingIdleCallback: number | null;
|
|
43
45
|
protected visibilityCleanup: (() => void) | null;
|
|
44
46
|
protected failedCacheKey: string;
|
|
45
47
|
protected failedCacheMax: number;
|
|
@@ -142,12 +144,13 @@ export declare abstract class ClsLoggerCore {
|
|
|
142
144
|
report(log: ReportLog): void;
|
|
143
145
|
/**
|
|
144
146
|
* 调度批量发送
|
|
145
|
-
* -
|
|
146
|
-
* -
|
|
147
|
+
* - 先使用 setTimeout 保证最小延迟(desiredDelay)
|
|
148
|
+
* - 若开启 useIdleCallback,在延迟结束后等待浏览器空闲再执行
|
|
147
149
|
*/
|
|
148
150
|
private scheduleFlush;
|
|
149
151
|
/**
|
|
150
152
|
* 取消已调度的批量发送
|
|
153
|
+
* - 同时清理 setTimeout 和可能的 requestIdleCallback
|
|
151
154
|
*/
|
|
152
155
|
private cancelScheduledFlush;
|
|
153
156
|
private getDesiredBatchFlushDueAt;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ClsLoggerCore.d.ts","sourceRoot":"","sources":["../src/ClsLoggerCore.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"ClsLoggerCore.d.ts","sourceRoot":"","sources":["../src/ClsLoggerCore.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,oBAAoB,EACpB,mBAAmB,EACnB,OAAO,EACP,UAAU,EACV,yBAAyB,EACzB,UAAU,EACV,SAAS,EACT,SAAS,EACT,aAAa,EACb,qBAAqB,EACrB,sBAAsB,EACtB,iBAAiB,EAClB,MAAM,SAAS,CAAC;AAWjB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAYlD;;;;;GAKG;AACH,8BAAsB,aAAa;IACjC,SAAS,CAAC,GAAG,EAAE,YAAY,GAAG,IAAI,CAAQ;IAC1C,SAAS,CAAC,UAAU,EAAE,OAAO,CAAC,YAAY,CAAC,GAAG,IAAI,CAAQ;IAC1D,SAAS,CAAC,WAAW,EAAE,OAAO,GAAG,IAAI,CAAQ;IAC7C,SAAS,CAAC,iBAAiB,EAAE,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,IAAI,CAAQ;IAC9E,SAAS,CAAC,MAAM,EAAE;QAAE,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAA;KAAE,GAAG,IAAI,CAAQ;IAC9F,SAAS,CAAC,aAAa,EAAE,OAAO,CAAC;QAAE,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAA;KAAE,CAAC,GAAG,IAAI,CAAQ;IAC9G,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAA0C;IAC1E,SAAS,CAAC,QAAQ,SAA2C;IAC7D,SAAS,CAAC,UAAU,SAAK;IACzB,SAAS,CAAC,MAAM,SAAe;IAC/B,SAAS,CAAC,OAAO,UAAQ;IAEzB,SAAS,CAAC,SAAS,SAAM;IACzB,SAAS,CAAC,WAAW,SAAM;IAC3B,SAAS,CAAC,KAAK,SAAM;IACrB,SAAS,CAAC,UAAU,SAAM;IAC1B,SAAS,CAAC,OAAO,EAAE,OAAO,CAAa;IACvC,SAAS,CAAC,sBAAsB,EAAE,CAAC,MAAM,UAAU,CAAC,GAAG,IAAI,CAAQ;IACnE,SAAS,CAAC,sBAAsB,EAAE,CAAC,MAAM,UAAU,CAAC,GAAG,IAAI,CAAQ;IACnE,SAAS,CAAC,UAAU,SAAiB;IACrC,SAAS,CAAC,SAAS,SAAM;IAGzB,SAAS,CAAC,WAAW,EAAE,SAAS,EAAE,CAAM;IACxC,SAAS,CAAC,YAAY,SAAM;IAC5B,SAAS,CAAC,eAAe,SAAO;IAChC,SAAS,CAAC,UAAU,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,GAAG,IAAI,CAAQ;IAClE,SAAS,CAAC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAQ;IAChD,SAAS,CAAC,MAAM,EAAE,MAAM,CAAK;IAC7B,SAAS,CAAC,cAAc,EAAE,MAAM,CAAK;IACrC,SAAS,CAAC,cAAc,EAAE,MAAM,CAAK;IACrC,SAAS,CAAC,eAAe,EAAE,OAAO,CAAS;IAC3C,SAAS,CAAC,WAAW,EAAE,MAAM,CAAQ;IACrC,SAAS,CAAC,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAQ;IACpD,SAAS,CAAC,iBAAiB,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,CAAQ;IAGxD,SAAS,CAAC,cAAc,SAAqB;IAC7C,SAAS,CAAC,cAAc,SAAO;IAC/B,SAAS,CAAC,qBAAqB,UAAS;IACxC,SAAS,CAAC,mBAAmB,UAAS;IACtC,SAAS,CAAC,yBAAyB,UAAS;IAC5C,SAAS,CAAC,sBAAsB,UAAS;IACzC,SAAS,CAAC,sBAAsB,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,CAAQ;IAC7D,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;IAElB;;OAEG;IACH,SAAS,CAAC,QAAQ,CAAC,OAAO,IAAI,OAAO,CAAC,YAAY,CAAC;IAEnD;;OAEG;IACH,SAAS,CAAC,QAAQ,CAAC,qBAAqB,CACtC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,KAAK,IAAI,EAChD,OAAO,EAAE,qBAAqB,GAC7B,IAAI;IAEP;;OAEG;IACH,SAAS,CAAC,QAAQ,CAAC,mBAAmB,CACpC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,KAAK,IAAI,EAChD,OAAO,EAAE,OAAO,GAAG,mBAAmB,GACrC,IAAI;IAEP;;OAEG;IACH,SAAS,CAAC,QAAQ,CAAC,yBAAyB,CAC1C,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,KAAK,IAAI,EAChD,OAAO,EAAE,OAAO,GAAG,yBAAyB,GAC3C,IAAI;IAEP;;OAEG;IACH,SAAS,CAAC,QAAQ,CAAC,sBAAsB,CACvC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,KAAK,IAAI,EAChD,OAAO,EAAE,OAAO,GAAG,sBAAsB,GACxC,MAAM,IAAI;IAEb;;OAEG;IACH,SAAS,CAAC,QAAQ,CAAC,0BAA0B,CAC3C,OAAO,EAAE,OAAO,GAAG,iBAAiB,GAAG,SAAS,GAC/C,CAAC,MAAM,UAAU,CAAC,GAAG,IAAI;IAE5B;;OAEG;IACH,SAAS,CAAC,aAAa,IAAI,OAAO;IAclC,IAAI,CAAC,OAAO,EAAE,oBAAoB,GAAG,IAAI;IAkFzC,OAAO,CAAC,aAAa;IAwBrB;;;;OAIG;IACH,OAAO,CAAC,uBAAuB;IAgC/B;;;;OAIG;IACH,OAAO,CAAC,cAAc;IAyCtB;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAQ9B,OAAO,CAAC,mBAAmB;IAsB3B,OAAO,CAAC,iBAAiB;IASzB,OAAO,CAAC,uBAAuB;IAS/B,OAAO,CAAC,oBAAoB;IAW5B;;;OAGG;IACH,mBAAmB,IAAI,IAAI;IAU3B;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC;QAAE,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAA;KAAE,CAAC;IAsB3F;;;;OAIG;IACH,GAAG,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,GAAE,UAAe,GAAG,IAAI;YA2BzC,QAAQ;IAyBtB;;OAEG;IACH,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,YAAY,SAAS,EAAE,OAAO,GAAE,UAAe,GAAG,IAAI;IAK7E;;;OAGG;IACH,OAAO,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,GAAE,UAAe,GAAG,IAAI;IA8B3D;;OAEG;IACH,KAAK,IAAI,IAAI;IAOb;;OAEG;IACH,QAAQ,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,IAAI;YAcpB,aAAa;IA6B3B;;OAEG;IACH,MAAM,CAAC,GAAG,EAAE,SAAS,GAAG,IAAI;IAyC5B;;;;OAIG;IACH,OAAO,CAAC,aAAa;IAoBrB;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IAqB5B,OAAO,CAAC,yBAAyB;IAUjC,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,KAAK,EAAE,IAAI,GAAE,UAAe,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,IAAI;IA6BnF,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,KAAK,EAAE,IAAI,GAAE,UAAe,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,IAAI;IA6BnF,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,KAAK,EAAE,IAAI,GAAE,UAAe,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,IAAI;IA8BpF,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,GAAE,UAAe,GAAG,IAAI;IASrD;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAejC,OAAO,CAAC,iBAAiB;YAmBX,cAAc;IAgC5B,OAAO,CAAC,mBAAmB;IAgB3B,OAAO,CAAC,qBAAqB;IAc7B,WAAW,IAAI,IAAI;IA2BnB;;OAEG;IACH,IAAI,CAAC,KAAK,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,GAAG,IAAI;CAcjG"}
|
package/dist/index.esm.js
CHANGED
|
@@ -178,8 +178,10 @@ class ClsLoggerCore {
|
|
|
178
178
|
this.batchTimerDueAt = null;
|
|
179
179
|
this.initTs = 0;
|
|
180
180
|
this.startupDelayMs = 0;
|
|
181
|
+
this.startupMaxSize = 0; // 启动窗口内的 maxSize,0 表示使用默认计算值
|
|
181
182
|
this.useIdleCallback = false;
|
|
182
183
|
this.idleTimeout = 3000;
|
|
184
|
+
this.pendingIdleCallback = null; // requestIdleCallback 的 id
|
|
183
185
|
this.visibilityCleanup = null;
|
|
184
186
|
// 参考文档:失败缓存 + 重试
|
|
185
187
|
this.failedCacheKey = 'cls_failed_logs';
|
|
@@ -252,6 +254,8 @@ class ClsLoggerCore {
|
|
|
252
254
|
this.startupDelayMs = options.batch?.startupDelayMs ?? this.startupDelayMs;
|
|
253
255
|
this.useIdleCallback = options.batch?.useIdleCallback ?? this.useIdleCallback;
|
|
254
256
|
this.idleTimeout = options.batch?.idleTimeout ?? this.idleTimeout;
|
|
257
|
+
// startupMaxSize:启动窗口内的队列阈值,默认为 maxSize * 10(至少 200)
|
|
258
|
+
this.startupMaxSize = options.batch?.startupMaxSize ?? Math.max(this.batchMaxSize * 10, 200);
|
|
255
259
|
this.failedCacheKey = options.failedCacheKey ?? this.failedCacheKey;
|
|
256
260
|
this.failedCacheMax = options.failedCacheMax ?? this.failedCacheMax;
|
|
257
261
|
// 预热(避免首条日志触发 import/初始化开销)
|
|
@@ -312,10 +316,17 @@ class ClsLoggerCore {
|
|
|
312
316
|
return;
|
|
313
317
|
const handleVisibilityChange = () => {
|
|
314
318
|
if (document.visibilityState === 'hidden') {
|
|
315
|
-
|
|
319
|
+
// 使用微任务延迟 flush,确保 web-vitals 等第三方库的 visibilitychange 回调先执行
|
|
320
|
+
// 这样 LCP/CLS/INP 等指标能先入队,再被 flush 发送
|
|
321
|
+
// 注意:queueMicrotask 比 setTimeout(0) 更可靠,不会被延迟太久
|
|
322
|
+
queueMicrotask(() => {
|
|
323
|
+
this.flushBatchSync();
|
|
324
|
+
});
|
|
316
325
|
}
|
|
317
326
|
};
|
|
318
327
|
const handlePageHide = () => {
|
|
328
|
+
// pagehide 不能延迟,因为浏览器可能立即关闭页面
|
|
329
|
+
// 但 pagehide 通常在 visibilitychange 之后触发,此时队列应该已经包含 web-vitals 指标
|
|
319
330
|
this.flushBatchSync();
|
|
320
331
|
};
|
|
321
332
|
document.addEventListener('visibilitychange', handleVisibilityChange);
|
|
@@ -627,11 +638,15 @@ class ClsLoggerCore {
|
|
|
627
638
|
return;
|
|
628
639
|
}
|
|
629
640
|
this.memoryQueue.push(log);
|
|
630
|
-
|
|
641
|
+
const now = Date.now();
|
|
642
|
+
// 判断是否在启动合并窗口内
|
|
643
|
+
const inStartupWindow = this.startupDelayMs > 0 && now - this.initTs < this.startupDelayMs;
|
|
644
|
+
// 启动窗口内使用 startupMaxSize,正常情况使用 batchMaxSize
|
|
645
|
+
const effectiveMaxSize = inStartupWindow ? this.startupMaxSize : this.batchMaxSize;
|
|
646
|
+
if (this.memoryQueue.length >= effectiveMaxSize) {
|
|
631
647
|
void this.flushBatch();
|
|
632
648
|
return;
|
|
633
649
|
}
|
|
634
|
-
const now = Date.now();
|
|
635
650
|
const desiredDueAt = this.getDesiredBatchFlushDueAt(now);
|
|
636
651
|
const desiredDelay = Math.max(0, desiredDueAt - now);
|
|
637
652
|
if (!this.batchTimer) {
|
|
@@ -648,17 +663,19 @@ class ClsLoggerCore {
|
|
|
648
663
|
}
|
|
649
664
|
/**
|
|
650
665
|
* 调度批量发送
|
|
651
|
-
* -
|
|
652
|
-
* -
|
|
666
|
+
* - 先使用 setTimeout 保证最小延迟(desiredDelay)
|
|
667
|
+
* - 若开启 useIdleCallback,在延迟结束后等待浏览器空闲再执行
|
|
653
668
|
*/
|
|
654
669
|
scheduleFlush(desiredDelay) {
|
|
655
670
|
if (this.useIdleCallback && typeof requestIdleCallback !== 'undefined') {
|
|
656
|
-
//
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
671
|
+
// 先 setTimeout 保证最小延迟,再 requestIdleCallback 在空闲时执行
|
|
672
|
+
this.batchTimer = setTimeout(() => {
|
|
673
|
+
const idleId = requestIdleCallback(() => {
|
|
674
|
+
this.pendingIdleCallback = null;
|
|
675
|
+
void this.flushBatch();
|
|
676
|
+
}, { timeout: this.idleTimeout });
|
|
677
|
+
this.pendingIdleCallback = idleId;
|
|
678
|
+
}, desiredDelay);
|
|
662
679
|
}
|
|
663
680
|
else {
|
|
664
681
|
this.batchTimer = setTimeout(() => {
|
|
@@ -668,22 +685,29 @@ class ClsLoggerCore {
|
|
|
668
685
|
}
|
|
669
686
|
/**
|
|
670
687
|
* 取消已调度的批量发送
|
|
688
|
+
* - 同时清理 setTimeout 和可能的 requestIdleCallback
|
|
671
689
|
*/
|
|
672
690
|
cancelScheduledFlush() {
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
if (this.useIdleCallback && typeof cancelIdleCallback !== 'undefined') {
|
|
677
|
-
cancelIdleCallback(this.batchTimer);
|
|
678
|
-
}
|
|
679
|
-
else {
|
|
691
|
+
// 清理 setTimeout
|
|
692
|
+
if (this.batchTimer) {
|
|
693
|
+
try {
|
|
680
694
|
clearTimeout(this.batchTimer);
|
|
681
695
|
}
|
|
696
|
+
catch {
|
|
697
|
+
// ignore
|
|
698
|
+
}
|
|
699
|
+
this.batchTimer = null;
|
|
682
700
|
}
|
|
683
|
-
|
|
684
|
-
|
|
701
|
+
// 清理可能的 pendingIdleCallback
|
|
702
|
+
if (this.pendingIdleCallback !== null && typeof cancelIdleCallback !== 'undefined') {
|
|
703
|
+
try {
|
|
704
|
+
cancelIdleCallback(this.pendingIdleCallback);
|
|
705
|
+
}
|
|
706
|
+
catch {
|
|
707
|
+
// ignore
|
|
708
|
+
}
|
|
709
|
+
this.pendingIdleCallback = null;
|
|
685
710
|
}
|
|
686
|
-
this.batchTimer = null;
|
|
687
711
|
}
|
|
688
712
|
getDesiredBatchFlushDueAt(nowTs) {
|
|
689
713
|
const start = this.initTs || nowTs;
|
|
@@ -896,7 +920,14 @@ class ClsLoggerCore {
|
|
|
896
920
|
// 先清空,再尝试发送
|
|
897
921
|
writeStringStorage(this.failedCacheKey, JSON.stringify([]));
|
|
898
922
|
this.memoryQueue.unshift(...logs);
|
|
899
|
-
|
|
923
|
+
// 触发定时器而非直接 flush,以尊重 startupDelayMs 配置
|
|
924
|
+
if (!this.batchTimer) {
|
|
925
|
+
const now = Date.now();
|
|
926
|
+
const desiredDueAt = this.getDesiredBatchFlushDueAt(now);
|
|
927
|
+
const desiredDelay = Math.max(0, desiredDueAt - now);
|
|
928
|
+
this.batchTimerDueAt = desiredDueAt;
|
|
929
|
+
this.scheduleFlush(desiredDelay);
|
|
930
|
+
}
|
|
900
931
|
}
|
|
901
932
|
/**
|
|
902
933
|
* 统计/计数类日志:按字段展开上报(若 data 为空默认 1)
|
package/dist/index.js
CHANGED
|
@@ -182,8 +182,10 @@ class ClsLoggerCore {
|
|
|
182
182
|
this.batchTimerDueAt = null;
|
|
183
183
|
this.initTs = 0;
|
|
184
184
|
this.startupDelayMs = 0;
|
|
185
|
+
this.startupMaxSize = 0; // 启动窗口内的 maxSize,0 表示使用默认计算值
|
|
185
186
|
this.useIdleCallback = false;
|
|
186
187
|
this.idleTimeout = 3000;
|
|
188
|
+
this.pendingIdleCallback = null; // requestIdleCallback 的 id
|
|
187
189
|
this.visibilityCleanup = null;
|
|
188
190
|
// 参考文档:失败缓存 + 重试
|
|
189
191
|
this.failedCacheKey = 'cls_failed_logs';
|
|
@@ -256,6 +258,8 @@ class ClsLoggerCore {
|
|
|
256
258
|
this.startupDelayMs = options.batch?.startupDelayMs ?? this.startupDelayMs;
|
|
257
259
|
this.useIdleCallback = options.batch?.useIdleCallback ?? this.useIdleCallback;
|
|
258
260
|
this.idleTimeout = options.batch?.idleTimeout ?? this.idleTimeout;
|
|
261
|
+
// startupMaxSize:启动窗口内的队列阈值,默认为 maxSize * 10(至少 200)
|
|
262
|
+
this.startupMaxSize = options.batch?.startupMaxSize ?? Math.max(this.batchMaxSize * 10, 200);
|
|
259
263
|
this.failedCacheKey = options.failedCacheKey ?? this.failedCacheKey;
|
|
260
264
|
this.failedCacheMax = options.failedCacheMax ?? this.failedCacheMax;
|
|
261
265
|
// 预热(避免首条日志触发 import/初始化开销)
|
|
@@ -316,10 +320,17 @@ class ClsLoggerCore {
|
|
|
316
320
|
return;
|
|
317
321
|
const handleVisibilityChange = () => {
|
|
318
322
|
if (document.visibilityState === 'hidden') {
|
|
319
|
-
|
|
323
|
+
// 使用微任务延迟 flush,确保 web-vitals 等第三方库的 visibilitychange 回调先执行
|
|
324
|
+
// 这样 LCP/CLS/INP 等指标能先入队,再被 flush 发送
|
|
325
|
+
// 注意:queueMicrotask 比 setTimeout(0) 更可靠,不会被延迟太久
|
|
326
|
+
queueMicrotask(() => {
|
|
327
|
+
this.flushBatchSync();
|
|
328
|
+
});
|
|
320
329
|
}
|
|
321
330
|
};
|
|
322
331
|
const handlePageHide = () => {
|
|
332
|
+
// pagehide 不能延迟,因为浏览器可能立即关闭页面
|
|
333
|
+
// 但 pagehide 通常在 visibilitychange 之后触发,此时队列应该已经包含 web-vitals 指标
|
|
323
334
|
this.flushBatchSync();
|
|
324
335
|
};
|
|
325
336
|
document.addEventListener('visibilitychange', handleVisibilityChange);
|
|
@@ -631,11 +642,15 @@ class ClsLoggerCore {
|
|
|
631
642
|
return;
|
|
632
643
|
}
|
|
633
644
|
this.memoryQueue.push(log);
|
|
634
|
-
|
|
645
|
+
const now = Date.now();
|
|
646
|
+
// 判断是否在启动合并窗口内
|
|
647
|
+
const inStartupWindow = this.startupDelayMs > 0 && now - this.initTs < this.startupDelayMs;
|
|
648
|
+
// 启动窗口内使用 startupMaxSize,正常情况使用 batchMaxSize
|
|
649
|
+
const effectiveMaxSize = inStartupWindow ? this.startupMaxSize : this.batchMaxSize;
|
|
650
|
+
if (this.memoryQueue.length >= effectiveMaxSize) {
|
|
635
651
|
void this.flushBatch();
|
|
636
652
|
return;
|
|
637
653
|
}
|
|
638
|
-
const now = Date.now();
|
|
639
654
|
const desiredDueAt = this.getDesiredBatchFlushDueAt(now);
|
|
640
655
|
const desiredDelay = Math.max(0, desiredDueAt - now);
|
|
641
656
|
if (!this.batchTimer) {
|
|
@@ -652,17 +667,19 @@ class ClsLoggerCore {
|
|
|
652
667
|
}
|
|
653
668
|
/**
|
|
654
669
|
* 调度批量发送
|
|
655
|
-
* -
|
|
656
|
-
* -
|
|
670
|
+
* - 先使用 setTimeout 保证最小延迟(desiredDelay)
|
|
671
|
+
* - 若开启 useIdleCallback,在延迟结束后等待浏览器空闲再执行
|
|
657
672
|
*/
|
|
658
673
|
scheduleFlush(desiredDelay) {
|
|
659
674
|
if (this.useIdleCallback && typeof requestIdleCallback !== 'undefined') {
|
|
660
|
-
//
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
675
|
+
// 先 setTimeout 保证最小延迟,再 requestIdleCallback 在空闲时执行
|
|
676
|
+
this.batchTimer = setTimeout(() => {
|
|
677
|
+
const idleId = requestIdleCallback(() => {
|
|
678
|
+
this.pendingIdleCallback = null;
|
|
679
|
+
void this.flushBatch();
|
|
680
|
+
}, { timeout: this.idleTimeout });
|
|
681
|
+
this.pendingIdleCallback = idleId;
|
|
682
|
+
}, desiredDelay);
|
|
666
683
|
}
|
|
667
684
|
else {
|
|
668
685
|
this.batchTimer = setTimeout(() => {
|
|
@@ -672,22 +689,29 @@ class ClsLoggerCore {
|
|
|
672
689
|
}
|
|
673
690
|
/**
|
|
674
691
|
* 取消已调度的批量发送
|
|
692
|
+
* - 同时清理 setTimeout 和可能的 requestIdleCallback
|
|
675
693
|
*/
|
|
676
694
|
cancelScheduledFlush() {
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
if (this.useIdleCallback && typeof cancelIdleCallback !== 'undefined') {
|
|
681
|
-
cancelIdleCallback(this.batchTimer);
|
|
682
|
-
}
|
|
683
|
-
else {
|
|
695
|
+
// 清理 setTimeout
|
|
696
|
+
if (this.batchTimer) {
|
|
697
|
+
try {
|
|
684
698
|
clearTimeout(this.batchTimer);
|
|
685
699
|
}
|
|
700
|
+
catch {
|
|
701
|
+
// ignore
|
|
702
|
+
}
|
|
703
|
+
this.batchTimer = null;
|
|
686
704
|
}
|
|
687
|
-
|
|
688
|
-
|
|
705
|
+
// 清理可能的 pendingIdleCallback
|
|
706
|
+
if (this.pendingIdleCallback !== null && typeof cancelIdleCallback !== 'undefined') {
|
|
707
|
+
try {
|
|
708
|
+
cancelIdleCallback(this.pendingIdleCallback);
|
|
709
|
+
}
|
|
710
|
+
catch {
|
|
711
|
+
// ignore
|
|
712
|
+
}
|
|
713
|
+
this.pendingIdleCallback = null;
|
|
689
714
|
}
|
|
690
|
-
this.batchTimer = null;
|
|
691
715
|
}
|
|
692
716
|
getDesiredBatchFlushDueAt(nowTs) {
|
|
693
717
|
const start = this.initTs || nowTs;
|
|
@@ -900,7 +924,14 @@ class ClsLoggerCore {
|
|
|
900
924
|
// 先清空,再尝试发送
|
|
901
925
|
writeStringStorage(this.failedCacheKey, JSON.stringify([]));
|
|
902
926
|
this.memoryQueue.unshift(...logs);
|
|
903
|
-
|
|
927
|
+
// 触发定时器而非直接 flush,以尊重 startupDelayMs 配置
|
|
928
|
+
if (!this.batchTimer) {
|
|
929
|
+
const now = Date.now();
|
|
930
|
+
const desiredDueAt = this.getDesiredBatchFlushDueAt(now);
|
|
931
|
+
const desiredDelay = Math.max(0, desiredDueAt - now);
|
|
932
|
+
this.batchTimerDueAt = desiredDueAt;
|
|
933
|
+
this.scheduleFlush(desiredDelay);
|
|
934
|
+
}
|
|
904
935
|
}
|
|
905
936
|
/**
|
|
906
937
|
* 统计/计数类日志:按字段展开上报(若 data 为空默认 1)
|
package/dist/index.umd.js
CHANGED
|
@@ -182,8 +182,10 @@
|
|
|
182
182
|
this.batchTimerDueAt = null;
|
|
183
183
|
this.initTs = 0;
|
|
184
184
|
this.startupDelayMs = 0;
|
|
185
|
+
this.startupMaxSize = 0; // 启动窗口内的 maxSize,0 表示使用默认计算值
|
|
185
186
|
this.useIdleCallback = false;
|
|
186
187
|
this.idleTimeout = 3000;
|
|
188
|
+
this.pendingIdleCallback = null; // requestIdleCallback 的 id
|
|
187
189
|
this.visibilityCleanup = null;
|
|
188
190
|
// 参考文档:失败缓存 + 重试
|
|
189
191
|
this.failedCacheKey = 'cls_failed_logs';
|
|
@@ -256,6 +258,8 @@
|
|
|
256
258
|
this.startupDelayMs = options.batch?.startupDelayMs ?? this.startupDelayMs;
|
|
257
259
|
this.useIdleCallback = options.batch?.useIdleCallback ?? this.useIdleCallback;
|
|
258
260
|
this.idleTimeout = options.batch?.idleTimeout ?? this.idleTimeout;
|
|
261
|
+
// startupMaxSize:启动窗口内的队列阈值,默认为 maxSize * 10(至少 200)
|
|
262
|
+
this.startupMaxSize = options.batch?.startupMaxSize ?? Math.max(this.batchMaxSize * 10, 200);
|
|
259
263
|
this.failedCacheKey = options.failedCacheKey ?? this.failedCacheKey;
|
|
260
264
|
this.failedCacheMax = options.failedCacheMax ?? this.failedCacheMax;
|
|
261
265
|
// 预热(避免首条日志触发 import/初始化开销)
|
|
@@ -316,10 +320,17 @@
|
|
|
316
320
|
return;
|
|
317
321
|
const handleVisibilityChange = () => {
|
|
318
322
|
if (document.visibilityState === 'hidden') {
|
|
319
|
-
|
|
323
|
+
// 使用微任务延迟 flush,确保 web-vitals 等第三方库的 visibilitychange 回调先执行
|
|
324
|
+
// 这样 LCP/CLS/INP 等指标能先入队,再被 flush 发送
|
|
325
|
+
// 注意:queueMicrotask 比 setTimeout(0) 更可靠,不会被延迟太久
|
|
326
|
+
queueMicrotask(() => {
|
|
327
|
+
this.flushBatchSync();
|
|
328
|
+
});
|
|
320
329
|
}
|
|
321
330
|
};
|
|
322
331
|
const handlePageHide = () => {
|
|
332
|
+
// pagehide 不能延迟,因为浏览器可能立即关闭页面
|
|
333
|
+
// 但 pagehide 通常在 visibilitychange 之后触发,此时队列应该已经包含 web-vitals 指标
|
|
323
334
|
this.flushBatchSync();
|
|
324
335
|
};
|
|
325
336
|
document.addEventListener('visibilitychange', handleVisibilityChange);
|
|
@@ -631,11 +642,15 @@
|
|
|
631
642
|
return;
|
|
632
643
|
}
|
|
633
644
|
this.memoryQueue.push(log);
|
|
634
|
-
|
|
645
|
+
const now = Date.now();
|
|
646
|
+
// 判断是否在启动合并窗口内
|
|
647
|
+
const inStartupWindow = this.startupDelayMs > 0 && now - this.initTs < this.startupDelayMs;
|
|
648
|
+
// 启动窗口内使用 startupMaxSize,正常情况使用 batchMaxSize
|
|
649
|
+
const effectiveMaxSize = inStartupWindow ? this.startupMaxSize : this.batchMaxSize;
|
|
650
|
+
if (this.memoryQueue.length >= effectiveMaxSize) {
|
|
635
651
|
void this.flushBatch();
|
|
636
652
|
return;
|
|
637
653
|
}
|
|
638
|
-
const now = Date.now();
|
|
639
654
|
const desiredDueAt = this.getDesiredBatchFlushDueAt(now);
|
|
640
655
|
const desiredDelay = Math.max(0, desiredDueAt - now);
|
|
641
656
|
if (!this.batchTimer) {
|
|
@@ -652,17 +667,19 @@
|
|
|
652
667
|
}
|
|
653
668
|
/**
|
|
654
669
|
* 调度批量发送
|
|
655
|
-
* -
|
|
656
|
-
* -
|
|
670
|
+
* - 先使用 setTimeout 保证最小延迟(desiredDelay)
|
|
671
|
+
* - 若开启 useIdleCallback,在延迟结束后等待浏览器空闲再执行
|
|
657
672
|
*/
|
|
658
673
|
scheduleFlush(desiredDelay) {
|
|
659
674
|
if (this.useIdleCallback && typeof requestIdleCallback !== 'undefined') {
|
|
660
|
-
//
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
675
|
+
// 先 setTimeout 保证最小延迟,再 requestIdleCallback 在空闲时执行
|
|
676
|
+
this.batchTimer = setTimeout(() => {
|
|
677
|
+
const idleId = requestIdleCallback(() => {
|
|
678
|
+
this.pendingIdleCallback = null;
|
|
679
|
+
void this.flushBatch();
|
|
680
|
+
}, { timeout: this.idleTimeout });
|
|
681
|
+
this.pendingIdleCallback = idleId;
|
|
682
|
+
}, desiredDelay);
|
|
666
683
|
}
|
|
667
684
|
else {
|
|
668
685
|
this.batchTimer = setTimeout(() => {
|
|
@@ -672,22 +689,29 @@
|
|
|
672
689
|
}
|
|
673
690
|
/**
|
|
674
691
|
* 取消已调度的批量发送
|
|
692
|
+
* - 同时清理 setTimeout 和可能的 requestIdleCallback
|
|
675
693
|
*/
|
|
676
694
|
cancelScheduledFlush() {
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
if (this.useIdleCallback && typeof cancelIdleCallback !== 'undefined') {
|
|
681
|
-
cancelIdleCallback(this.batchTimer);
|
|
682
|
-
}
|
|
683
|
-
else {
|
|
695
|
+
// 清理 setTimeout
|
|
696
|
+
if (this.batchTimer) {
|
|
697
|
+
try {
|
|
684
698
|
clearTimeout(this.batchTimer);
|
|
685
699
|
}
|
|
700
|
+
catch {
|
|
701
|
+
// ignore
|
|
702
|
+
}
|
|
703
|
+
this.batchTimer = null;
|
|
686
704
|
}
|
|
687
|
-
|
|
688
|
-
|
|
705
|
+
// 清理可能的 pendingIdleCallback
|
|
706
|
+
if (this.pendingIdleCallback !== null && typeof cancelIdleCallback !== 'undefined') {
|
|
707
|
+
try {
|
|
708
|
+
cancelIdleCallback(this.pendingIdleCallback);
|
|
709
|
+
}
|
|
710
|
+
catch {
|
|
711
|
+
// ignore
|
|
712
|
+
}
|
|
713
|
+
this.pendingIdleCallback = null;
|
|
689
714
|
}
|
|
690
|
-
this.batchTimer = null;
|
|
691
715
|
}
|
|
692
716
|
getDesiredBatchFlushDueAt(nowTs) {
|
|
693
717
|
const start = this.initTs || nowTs;
|
|
@@ -900,7 +924,14 @@
|
|
|
900
924
|
// 先清空,再尝试发送
|
|
901
925
|
writeStringStorage(this.failedCacheKey, JSON.stringify([]));
|
|
902
926
|
this.memoryQueue.unshift(...logs);
|
|
903
|
-
|
|
927
|
+
// 触发定时器而非直接 flush,以尊重 startupDelayMs 配置
|
|
928
|
+
if (!this.batchTimer) {
|
|
929
|
+
const now = Date.now();
|
|
930
|
+
const desiredDueAt = this.getDesiredBatchFlushDueAt(now);
|
|
931
|
+
const desiredDelay = Math.max(0, desiredDueAt - now);
|
|
932
|
+
this.batchTimerDueAt = desiredDueAt;
|
|
933
|
+
this.scheduleFlush(desiredDelay);
|
|
934
|
+
}
|
|
904
935
|
}
|
|
905
936
|
/**
|
|
906
937
|
* 统计/计数类日志:按字段展开上报(若 data 为空默认 1)
|
package/dist/mini.esm.js
CHANGED
|
@@ -178,8 +178,10 @@ class ClsLoggerCore {
|
|
|
178
178
|
this.batchTimerDueAt = null;
|
|
179
179
|
this.initTs = 0;
|
|
180
180
|
this.startupDelayMs = 0;
|
|
181
|
+
this.startupMaxSize = 0; // 启动窗口内的 maxSize,0 表示使用默认计算值
|
|
181
182
|
this.useIdleCallback = false;
|
|
182
183
|
this.idleTimeout = 3000;
|
|
184
|
+
this.pendingIdleCallback = null; // requestIdleCallback 的 id
|
|
183
185
|
this.visibilityCleanup = null;
|
|
184
186
|
// 参考文档:失败缓存 + 重试
|
|
185
187
|
this.failedCacheKey = 'cls_failed_logs';
|
|
@@ -252,6 +254,8 @@ class ClsLoggerCore {
|
|
|
252
254
|
this.startupDelayMs = options.batch?.startupDelayMs ?? this.startupDelayMs;
|
|
253
255
|
this.useIdleCallback = options.batch?.useIdleCallback ?? this.useIdleCallback;
|
|
254
256
|
this.idleTimeout = options.batch?.idleTimeout ?? this.idleTimeout;
|
|
257
|
+
// startupMaxSize:启动窗口内的队列阈值,默认为 maxSize * 10(至少 200)
|
|
258
|
+
this.startupMaxSize = options.batch?.startupMaxSize ?? Math.max(this.batchMaxSize * 10, 200);
|
|
255
259
|
this.failedCacheKey = options.failedCacheKey ?? this.failedCacheKey;
|
|
256
260
|
this.failedCacheMax = options.failedCacheMax ?? this.failedCacheMax;
|
|
257
261
|
// 预热(避免首条日志触发 import/初始化开销)
|
|
@@ -312,10 +316,17 @@ class ClsLoggerCore {
|
|
|
312
316
|
return;
|
|
313
317
|
const handleVisibilityChange = () => {
|
|
314
318
|
if (document.visibilityState === 'hidden') {
|
|
315
|
-
|
|
319
|
+
// 使用微任务延迟 flush,确保 web-vitals 等第三方库的 visibilitychange 回调先执行
|
|
320
|
+
// 这样 LCP/CLS/INP 等指标能先入队,再被 flush 发送
|
|
321
|
+
// 注意:queueMicrotask 比 setTimeout(0) 更可靠,不会被延迟太久
|
|
322
|
+
queueMicrotask(() => {
|
|
323
|
+
this.flushBatchSync();
|
|
324
|
+
});
|
|
316
325
|
}
|
|
317
326
|
};
|
|
318
327
|
const handlePageHide = () => {
|
|
328
|
+
// pagehide 不能延迟,因为浏览器可能立即关闭页面
|
|
329
|
+
// 但 pagehide 通常在 visibilitychange 之后触发,此时队列应该已经包含 web-vitals 指标
|
|
319
330
|
this.flushBatchSync();
|
|
320
331
|
};
|
|
321
332
|
document.addEventListener('visibilitychange', handleVisibilityChange);
|
|
@@ -627,11 +638,15 @@ class ClsLoggerCore {
|
|
|
627
638
|
return;
|
|
628
639
|
}
|
|
629
640
|
this.memoryQueue.push(log);
|
|
630
|
-
|
|
641
|
+
const now = Date.now();
|
|
642
|
+
// 判断是否在启动合并窗口内
|
|
643
|
+
const inStartupWindow = this.startupDelayMs > 0 && now - this.initTs < this.startupDelayMs;
|
|
644
|
+
// 启动窗口内使用 startupMaxSize,正常情况使用 batchMaxSize
|
|
645
|
+
const effectiveMaxSize = inStartupWindow ? this.startupMaxSize : this.batchMaxSize;
|
|
646
|
+
if (this.memoryQueue.length >= effectiveMaxSize) {
|
|
631
647
|
void this.flushBatch();
|
|
632
648
|
return;
|
|
633
649
|
}
|
|
634
|
-
const now = Date.now();
|
|
635
650
|
const desiredDueAt = this.getDesiredBatchFlushDueAt(now);
|
|
636
651
|
const desiredDelay = Math.max(0, desiredDueAt - now);
|
|
637
652
|
if (!this.batchTimer) {
|
|
@@ -648,17 +663,19 @@ class ClsLoggerCore {
|
|
|
648
663
|
}
|
|
649
664
|
/**
|
|
650
665
|
* 调度批量发送
|
|
651
|
-
* -
|
|
652
|
-
* -
|
|
666
|
+
* - 先使用 setTimeout 保证最小延迟(desiredDelay)
|
|
667
|
+
* - 若开启 useIdleCallback,在延迟结束后等待浏览器空闲再执行
|
|
653
668
|
*/
|
|
654
669
|
scheduleFlush(desiredDelay) {
|
|
655
670
|
if (this.useIdleCallback && typeof requestIdleCallback !== 'undefined') {
|
|
656
|
-
//
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
671
|
+
// 先 setTimeout 保证最小延迟,再 requestIdleCallback 在空闲时执行
|
|
672
|
+
this.batchTimer = setTimeout(() => {
|
|
673
|
+
const idleId = requestIdleCallback(() => {
|
|
674
|
+
this.pendingIdleCallback = null;
|
|
675
|
+
void this.flushBatch();
|
|
676
|
+
}, { timeout: this.idleTimeout });
|
|
677
|
+
this.pendingIdleCallback = idleId;
|
|
678
|
+
}, desiredDelay);
|
|
662
679
|
}
|
|
663
680
|
else {
|
|
664
681
|
this.batchTimer = setTimeout(() => {
|
|
@@ -668,22 +685,29 @@ class ClsLoggerCore {
|
|
|
668
685
|
}
|
|
669
686
|
/**
|
|
670
687
|
* 取消已调度的批量发送
|
|
688
|
+
* - 同时清理 setTimeout 和可能的 requestIdleCallback
|
|
671
689
|
*/
|
|
672
690
|
cancelScheduledFlush() {
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
if (this.useIdleCallback && typeof cancelIdleCallback !== 'undefined') {
|
|
677
|
-
cancelIdleCallback(this.batchTimer);
|
|
678
|
-
}
|
|
679
|
-
else {
|
|
691
|
+
// 清理 setTimeout
|
|
692
|
+
if (this.batchTimer) {
|
|
693
|
+
try {
|
|
680
694
|
clearTimeout(this.batchTimer);
|
|
681
695
|
}
|
|
696
|
+
catch {
|
|
697
|
+
// ignore
|
|
698
|
+
}
|
|
699
|
+
this.batchTimer = null;
|
|
682
700
|
}
|
|
683
|
-
|
|
684
|
-
|
|
701
|
+
// 清理可能的 pendingIdleCallback
|
|
702
|
+
if (this.pendingIdleCallback !== null && typeof cancelIdleCallback !== 'undefined') {
|
|
703
|
+
try {
|
|
704
|
+
cancelIdleCallback(this.pendingIdleCallback);
|
|
705
|
+
}
|
|
706
|
+
catch {
|
|
707
|
+
// ignore
|
|
708
|
+
}
|
|
709
|
+
this.pendingIdleCallback = null;
|
|
685
710
|
}
|
|
686
|
-
this.batchTimer = null;
|
|
687
711
|
}
|
|
688
712
|
getDesiredBatchFlushDueAt(nowTs) {
|
|
689
713
|
const start = this.initTs || nowTs;
|
|
@@ -896,7 +920,14 @@ class ClsLoggerCore {
|
|
|
896
920
|
// 先清空,再尝试发送
|
|
897
921
|
writeStringStorage(this.failedCacheKey, JSON.stringify([]));
|
|
898
922
|
this.memoryQueue.unshift(...logs);
|
|
899
|
-
|
|
923
|
+
// 触发定时器而非直接 flush,以尊重 startupDelayMs 配置
|
|
924
|
+
if (!this.batchTimer) {
|
|
925
|
+
const now = Date.now();
|
|
926
|
+
const desiredDueAt = this.getDesiredBatchFlushDueAt(now);
|
|
927
|
+
const desiredDelay = Math.max(0, desiredDueAt - now);
|
|
928
|
+
this.batchTimerDueAt = desiredDueAt;
|
|
929
|
+
this.scheduleFlush(desiredDelay);
|
|
930
|
+
}
|
|
900
931
|
}
|
|
901
932
|
/**
|
|
902
933
|
* 统计/计数类日志:按字段展开上报(若 data 为空默认 1)
|
package/dist/mini.js
CHANGED
|
@@ -201,8 +201,10 @@ class ClsLoggerCore {
|
|
|
201
201
|
this.batchTimerDueAt = null;
|
|
202
202
|
this.initTs = 0;
|
|
203
203
|
this.startupDelayMs = 0;
|
|
204
|
+
this.startupMaxSize = 0; // 启动窗口内的 maxSize,0 表示使用默认计算值
|
|
204
205
|
this.useIdleCallback = false;
|
|
205
206
|
this.idleTimeout = 3000;
|
|
207
|
+
this.pendingIdleCallback = null; // requestIdleCallback 的 id
|
|
206
208
|
this.visibilityCleanup = null;
|
|
207
209
|
// 参考文档:失败缓存 + 重试
|
|
208
210
|
this.failedCacheKey = 'cls_failed_logs';
|
|
@@ -275,6 +277,8 @@ class ClsLoggerCore {
|
|
|
275
277
|
this.startupDelayMs = options.batch?.startupDelayMs ?? this.startupDelayMs;
|
|
276
278
|
this.useIdleCallback = options.batch?.useIdleCallback ?? this.useIdleCallback;
|
|
277
279
|
this.idleTimeout = options.batch?.idleTimeout ?? this.idleTimeout;
|
|
280
|
+
// startupMaxSize:启动窗口内的队列阈值,默认为 maxSize * 10(至少 200)
|
|
281
|
+
this.startupMaxSize = options.batch?.startupMaxSize ?? Math.max(this.batchMaxSize * 10, 200);
|
|
278
282
|
this.failedCacheKey = options.failedCacheKey ?? this.failedCacheKey;
|
|
279
283
|
this.failedCacheMax = options.failedCacheMax ?? this.failedCacheMax;
|
|
280
284
|
// 预热(避免首条日志触发 import/初始化开销)
|
|
@@ -335,10 +339,17 @@ class ClsLoggerCore {
|
|
|
335
339
|
return;
|
|
336
340
|
const handleVisibilityChange = () => {
|
|
337
341
|
if (document.visibilityState === 'hidden') {
|
|
338
|
-
|
|
342
|
+
// 使用微任务延迟 flush,确保 web-vitals 等第三方库的 visibilitychange 回调先执行
|
|
343
|
+
// 这样 LCP/CLS/INP 等指标能先入队,再被 flush 发送
|
|
344
|
+
// 注意:queueMicrotask 比 setTimeout(0) 更可靠,不会被延迟太久
|
|
345
|
+
queueMicrotask(() => {
|
|
346
|
+
this.flushBatchSync();
|
|
347
|
+
});
|
|
339
348
|
}
|
|
340
349
|
};
|
|
341
350
|
const handlePageHide = () => {
|
|
351
|
+
// pagehide 不能延迟,因为浏览器可能立即关闭页面
|
|
352
|
+
// 但 pagehide 通常在 visibilitychange 之后触发,此时队列应该已经包含 web-vitals 指标
|
|
342
353
|
this.flushBatchSync();
|
|
343
354
|
};
|
|
344
355
|
document.addEventListener('visibilitychange', handleVisibilityChange);
|
|
@@ -650,11 +661,15 @@ class ClsLoggerCore {
|
|
|
650
661
|
return;
|
|
651
662
|
}
|
|
652
663
|
this.memoryQueue.push(log);
|
|
653
|
-
|
|
664
|
+
const now = Date.now();
|
|
665
|
+
// 判断是否在启动合并窗口内
|
|
666
|
+
const inStartupWindow = this.startupDelayMs > 0 && now - this.initTs < this.startupDelayMs;
|
|
667
|
+
// 启动窗口内使用 startupMaxSize,正常情况使用 batchMaxSize
|
|
668
|
+
const effectiveMaxSize = inStartupWindow ? this.startupMaxSize : this.batchMaxSize;
|
|
669
|
+
if (this.memoryQueue.length >= effectiveMaxSize) {
|
|
654
670
|
void this.flushBatch();
|
|
655
671
|
return;
|
|
656
672
|
}
|
|
657
|
-
const now = Date.now();
|
|
658
673
|
const desiredDueAt = this.getDesiredBatchFlushDueAt(now);
|
|
659
674
|
const desiredDelay = Math.max(0, desiredDueAt - now);
|
|
660
675
|
if (!this.batchTimer) {
|
|
@@ -671,17 +686,19 @@ class ClsLoggerCore {
|
|
|
671
686
|
}
|
|
672
687
|
/**
|
|
673
688
|
* 调度批量发送
|
|
674
|
-
* -
|
|
675
|
-
* -
|
|
689
|
+
* - 先使用 setTimeout 保证最小延迟(desiredDelay)
|
|
690
|
+
* - 若开启 useIdleCallback,在延迟结束后等待浏览器空闲再执行
|
|
676
691
|
*/
|
|
677
692
|
scheduleFlush(desiredDelay) {
|
|
678
693
|
if (this.useIdleCallback && typeof requestIdleCallback !== 'undefined') {
|
|
679
|
-
//
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
694
|
+
// 先 setTimeout 保证最小延迟,再 requestIdleCallback 在空闲时执行
|
|
695
|
+
this.batchTimer = setTimeout(() => {
|
|
696
|
+
const idleId = requestIdleCallback(() => {
|
|
697
|
+
this.pendingIdleCallback = null;
|
|
698
|
+
void this.flushBatch();
|
|
699
|
+
}, { timeout: this.idleTimeout });
|
|
700
|
+
this.pendingIdleCallback = idleId;
|
|
701
|
+
}, desiredDelay);
|
|
685
702
|
}
|
|
686
703
|
else {
|
|
687
704
|
this.batchTimer = setTimeout(() => {
|
|
@@ -691,22 +708,29 @@ class ClsLoggerCore {
|
|
|
691
708
|
}
|
|
692
709
|
/**
|
|
693
710
|
* 取消已调度的批量发送
|
|
711
|
+
* - 同时清理 setTimeout 和可能的 requestIdleCallback
|
|
694
712
|
*/
|
|
695
713
|
cancelScheduledFlush() {
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
if (this.useIdleCallback && typeof cancelIdleCallback !== 'undefined') {
|
|
700
|
-
cancelIdleCallback(this.batchTimer);
|
|
701
|
-
}
|
|
702
|
-
else {
|
|
714
|
+
// 清理 setTimeout
|
|
715
|
+
if (this.batchTimer) {
|
|
716
|
+
try {
|
|
703
717
|
clearTimeout(this.batchTimer);
|
|
704
718
|
}
|
|
719
|
+
catch {
|
|
720
|
+
// ignore
|
|
721
|
+
}
|
|
722
|
+
this.batchTimer = null;
|
|
705
723
|
}
|
|
706
|
-
|
|
707
|
-
|
|
724
|
+
// 清理可能的 pendingIdleCallback
|
|
725
|
+
if (this.pendingIdleCallback !== null && typeof cancelIdleCallback !== 'undefined') {
|
|
726
|
+
try {
|
|
727
|
+
cancelIdleCallback(this.pendingIdleCallback);
|
|
728
|
+
}
|
|
729
|
+
catch {
|
|
730
|
+
// ignore
|
|
731
|
+
}
|
|
732
|
+
this.pendingIdleCallback = null;
|
|
708
733
|
}
|
|
709
|
-
this.batchTimer = null;
|
|
710
734
|
}
|
|
711
735
|
getDesiredBatchFlushDueAt(nowTs) {
|
|
712
736
|
const start = this.initTs || nowTs;
|
|
@@ -919,7 +943,14 @@ class ClsLoggerCore {
|
|
|
919
943
|
// 先清空,再尝试发送
|
|
920
944
|
writeStringStorage(this.failedCacheKey, JSON.stringify([]));
|
|
921
945
|
this.memoryQueue.unshift(...logs);
|
|
922
|
-
|
|
946
|
+
// 触发定时器而非直接 flush,以尊重 startupDelayMs 配置
|
|
947
|
+
if (!this.batchTimer) {
|
|
948
|
+
const now = Date.now();
|
|
949
|
+
const desiredDueAt = this.getDesiredBatchFlushDueAt(now);
|
|
950
|
+
const desiredDelay = Math.max(0, desiredDueAt - now);
|
|
951
|
+
this.batchTimerDueAt = desiredDueAt;
|
|
952
|
+
this.scheduleFlush(desiredDelay);
|
|
953
|
+
}
|
|
923
954
|
}
|
|
924
955
|
/**
|
|
925
956
|
* 统计/计数类日志:按字段展开上报(若 data 为空默认 1)
|
package/dist/types.d.ts
CHANGED
|
@@ -29,13 +29,20 @@ export interface BatchOptions {
|
|
|
29
29
|
/**
|
|
30
30
|
* 启动阶段合并窗口(ms)
|
|
31
31
|
* - 目的:减少初始化/首屏阶段(perf/resource 等日志密集期)被 intervalMs 拆成多次上报
|
|
32
|
-
* - 行为:在该窗口内尽量延迟 flush
|
|
32
|
+
* - 行为:在该窗口内尽量延迟 flush 到窗口结束
|
|
33
33
|
* - 默认:0(不开启)
|
|
34
34
|
*/
|
|
35
35
|
startupDelayMs?: number;
|
|
36
|
+
/**
|
|
37
|
+
* 启动阶段的 maxSize(启动窗口内生效)
|
|
38
|
+
* - 目的:防止启动期间日志密集导致 maxSize 被频繁触发而打断 startupDelayMs
|
|
39
|
+
* - 行为:在 startupDelayMs 窗口内使用此值作为队列阈值
|
|
40
|
+
* - 默认:maxSize * 10(若未设置则为 200)
|
|
41
|
+
*/
|
|
42
|
+
startupMaxSize?: number;
|
|
36
43
|
/**
|
|
37
44
|
* 是否使用浏览器空闲时间上报
|
|
38
|
-
* -
|
|
45
|
+
* - 开启后会先等待 intervalMs/startupDelayMs,然后在浏览器空闲时执行发送
|
|
39
46
|
* - 配合 idleTimeout 使用,保证即使浏览器繁忙也能在超时后发送
|
|
40
47
|
* - 默认:false
|
|
41
48
|
*/
|
|
@@ -43,7 +50,7 @@ export interface BatchOptions {
|
|
|
43
50
|
/**
|
|
44
51
|
* 空闲回调超时时间(ms)
|
|
45
52
|
* - 当 useIdleCallback 为 true 时生效
|
|
46
|
-
* -
|
|
53
|
+
* - 在等待空闲期间,即使浏览器繁忙,也会在此时间后强制执行
|
|
47
54
|
* - 默认:3000
|
|
48
55
|
*/
|
|
49
56
|
idleTimeout?: number;
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;AAC7D,MAAM,MAAM,SAAS,GAAG,aAAa,GAAG,SAAS,EAAE,GAAG;IAAE,CAAC,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAAC;AAEjF;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GAAG,aAAa,GAAG,SAAS,GAAG,MAAM,CAAC;AAEhE,sCAAsC;AACtC,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;AAExD,MAAM,MAAM,UAAU,GAAG,UAAU,CAAC;AAEpC,MAAM,MAAM,OAAO,GAAG,SAAS,GAAG,aAAa,CAAC;AAEhD,MAAM,WAAW,qBAAqB;IACpC,oDAAoD;IACpD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kBAAkB;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,eAAe;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,8BAA8B;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,wBAAwB;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,oBAAoB;IACnC;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW;IACX,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;OAGG;IACH,YAAY,CAAC,EAAE,qBAAqB,CAAC;IAErC;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;;;OAIG;IACH,GAAG,CAAC,EAAE,GAAG,CAAC;IAEV;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAErC;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,UAAU,CAAC;IAEtC,gDAAgD;IAChD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,8BAA8B;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,wCAAwC;IACxC,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,qCAAqC;IACrC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,0BAA0B;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,GAAG,qBAAqB,CAAC;IAEjD;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,GAAG,mBAAmB,CAAC;IAE7C;;;OAGG;IACH,kBAAkB,CAAC,EAAE,OAAO,GAAG,yBAAyB,CAAC;IAEzD;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,GAAG,iBAAiB,CAAC;IAEzC;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,GAAG,sBAAsB,CAAC;CACpD;AAED,MAAM,WAAW,mBAAoB,SAAQ,oBAAoB;IAC/D,yCAAyC;IACzC,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,sBAAsB;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,2BAA4B,SAAQ,oBAAoB;IACvE,8BAA8B;IAC9B,OAAO,EAAE,aAAa,CAAC;IACvB,mBAAmB;IACnB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,MAAM,oBAAoB,GAAG,mBAAmB,GAAG,2BAA2B,CAAC;AAErF,MAAM,WAAW,UAAU;IACzB,iDAAiD;IACjD,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,UAAU,CAAC;CAClB;AAED,MAAM,MAAM,MAAM,GAAG;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,sBAAsB;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uBAAuB;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,WAAW,SAAS;IACxB,+CAA+C;IAC/C,IAAI,EAAE,MAAM,CAAC;IACb,WAAW;IACX,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,yBAAyB;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,qBAAqB;IACpC,mBAAmB;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,+BAA+B;IAC/B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mBAAmB;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,UAAU,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;IACpC,0BAA0B;IAC1B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,yBAAyB;IACzB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,6BAA6B;IAC7B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,8BAA8B;IAC9B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,oCAAoC;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,mBAAmB;IAClC,mBAAmB;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,gCAAgC;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mBAAmB;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mDAAmD;IACnD,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,uCAAuC;IACvC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,cAAc,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;CACzC;AAED,MAAM,WAAW,yBAAyB;IACxC,mBAAmB;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,+BAA+B;IAC/B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mBAAmB;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gCAAgC;IAChC,UAAU,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;IACpC;;;;;;;;OAQG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,2DAA2D;IAC3D,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,+BAA+B;IAC/B,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,iBAAiB;IAChC,qCAAqC;IACrC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,gCAAgC;IAChC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,kDAAkD;IAClD,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,mCAAmC;IACnC,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED,MAAM,WAAW,sBAAsB;IACrC;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,yBAAyB;IACzB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,sBAAsB;IACtB,EAAE,CAAC,EAAE,OAAO,CAAC;IACb,sBAAsB;IACtB,EAAE,CAAC,EAAE,OAAO,CAAC;IACb,qBAAqB;IACrB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,mBAAmB;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,0BAA0B;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,0BAA0B;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,4BAA4B;IAC5B,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAEzC,kCAAkC;IAClC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gDAAgD;IAChD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,2DAA2D;IAC3D,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAEhC;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAE1B;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,wCAAwC;IACxC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gCAAgC;IAChC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAE5B,8DAA8D;IAC9D,WAAW,CAAC,EAAE,MAAM,MAAM,CAAC;IAE3B;;OAEG;IACH,YAAY,CAAC,EAAE;QACb;;;;WAIG;QACH,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;QAC1B,yBAAyB;QACzB,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,CAAC;CACH"}
|
package/dist/web.esm.js
CHANGED
|
@@ -179,8 +179,10 @@ class ClsLoggerCore {
|
|
|
179
179
|
this.batchTimerDueAt = null;
|
|
180
180
|
this.initTs = 0;
|
|
181
181
|
this.startupDelayMs = 0;
|
|
182
|
+
this.startupMaxSize = 0; // 启动窗口内的 maxSize,0 表示使用默认计算值
|
|
182
183
|
this.useIdleCallback = false;
|
|
183
184
|
this.idleTimeout = 3000;
|
|
185
|
+
this.pendingIdleCallback = null; // requestIdleCallback 的 id
|
|
184
186
|
this.visibilityCleanup = null;
|
|
185
187
|
// 参考文档:失败缓存 + 重试
|
|
186
188
|
this.failedCacheKey = 'cls_failed_logs';
|
|
@@ -253,6 +255,8 @@ class ClsLoggerCore {
|
|
|
253
255
|
this.startupDelayMs = options.batch?.startupDelayMs ?? this.startupDelayMs;
|
|
254
256
|
this.useIdleCallback = options.batch?.useIdleCallback ?? this.useIdleCallback;
|
|
255
257
|
this.idleTimeout = options.batch?.idleTimeout ?? this.idleTimeout;
|
|
258
|
+
// startupMaxSize:启动窗口内的队列阈值,默认为 maxSize * 10(至少 200)
|
|
259
|
+
this.startupMaxSize = options.batch?.startupMaxSize ?? Math.max(this.batchMaxSize * 10, 200);
|
|
256
260
|
this.failedCacheKey = options.failedCacheKey ?? this.failedCacheKey;
|
|
257
261
|
this.failedCacheMax = options.failedCacheMax ?? this.failedCacheMax;
|
|
258
262
|
// 预热(避免首条日志触发 import/初始化开销)
|
|
@@ -313,10 +317,17 @@ class ClsLoggerCore {
|
|
|
313
317
|
return;
|
|
314
318
|
const handleVisibilityChange = () => {
|
|
315
319
|
if (document.visibilityState === 'hidden') {
|
|
316
|
-
|
|
320
|
+
// 使用微任务延迟 flush,确保 web-vitals 等第三方库的 visibilitychange 回调先执行
|
|
321
|
+
// 这样 LCP/CLS/INP 等指标能先入队,再被 flush 发送
|
|
322
|
+
// 注意:queueMicrotask 比 setTimeout(0) 更可靠,不会被延迟太久
|
|
323
|
+
queueMicrotask(() => {
|
|
324
|
+
this.flushBatchSync();
|
|
325
|
+
});
|
|
317
326
|
}
|
|
318
327
|
};
|
|
319
328
|
const handlePageHide = () => {
|
|
329
|
+
// pagehide 不能延迟,因为浏览器可能立即关闭页面
|
|
330
|
+
// 但 pagehide 通常在 visibilitychange 之后触发,此时队列应该已经包含 web-vitals 指标
|
|
320
331
|
this.flushBatchSync();
|
|
321
332
|
};
|
|
322
333
|
document.addEventListener('visibilitychange', handleVisibilityChange);
|
|
@@ -628,11 +639,15 @@ class ClsLoggerCore {
|
|
|
628
639
|
return;
|
|
629
640
|
}
|
|
630
641
|
this.memoryQueue.push(log);
|
|
631
|
-
|
|
642
|
+
const now = Date.now();
|
|
643
|
+
// 判断是否在启动合并窗口内
|
|
644
|
+
const inStartupWindow = this.startupDelayMs > 0 && now - this.initTs < this.startupDelayMs;
|
|
645
|
+
// 启动窗口内使用 startupMaxSize,正常情况使用 batchMaxSize
|
|
646
|
+
const effectiveMaxSize = inStartupWindow ? this.startupMaxSize : this.batchMaxSize;
|
|
647
|
+
if (this.memoryQueue.length >= effectiveMaxSize) {
|
|
632
648
|
void this.flushBatch();
|
|
633
649
|
return;
|
|
634
650
|
}
|
|
635
|
-
const now = Date.now();
|
|
636
651
|
const desiredDueAt = this.getDesiredBatchFlushDueAt(now);
|
|
637
652
|
const desiredDelay = Math.max(0, desiredDueAt - now);
|
|
638
653
|
if (!this.batchTimer) {
|
|
@@ -649,17 +664,19 @@ class ClsLoggerCore {
|
|
|
649
664
|
}
|
|
650
665
|
/**
|
|
651
666
|
* 调度批量发送
|
|
652
|
-
* -
|
|
653
|
-
* -
|
|
667
|
+
* - 先使用 setTimeout 保证最小延迟(desiredDelay)
|
|
668
|
+
* - 若开启 useIdleCallback,在延迟结束后等待浏览器空闲再执行
|
|
654
669
|
*/
|
|
655
670
|
scheduleFlush(desiredDelay) {
|
|
656
671
|
if (this.useIdleCallback && typeof requestIdleCallback !== 'undefined') {
|
|
657
|
-
//
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
672
|
+
// 先 setTimeout 保证最小延迟,再 requestIdleCallback 在空闲时执行
|
|
673
|
+
this.batchTimer = setTimeout(() => {
|
|
674
|
+
const idleId = requestIdleCallback(() => {
|
|
675
|
+
this.pendingIdleCallback = null;
|
|
676
|
+
void this.flushBatch();
|
|
677
|
+
}, { timeout: this.idleTimeout });
|
|
678
|
+
this.pendingIdleCallback = idleId;
|
|
679
|
+
}, desiredDelay);
|
|
663
680
|
}
|
|
664
681
|
else {
|
|
665
682
|
this.batchTimer = setTimeout(() => {
|
|
@@ -669,22 +686,29 @@ class ClsLoggerCore {
|
|
|
669
686
|
}
|
|
670
687
|
/**
|
|
671
688
|
* 取消已调度的批量发送
|
|
689
|
+
* - 同时清理 setTimeout 和可能的 requestIdleCallback
|
|
672
690
|
*/
|
|
673
691
|
cancelScheduledFlush() {
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
if (this.useIdleCallback && typeof cancelIdleCallback !== 'undefined') {
|
|
678
|
-
cancelIdleCallback(this.batchTimer);
|
|
679
|
-
}
|
|
680
|
-
else {
|
|
692
|
+
// 清理 setTimeout
|
|
693
|
+
if (this.batchTimer) {
|
|
694
|
+
try {
|
|
681
695
|
clearTimeout(this.batchTimer);
|
|
682
696
|
}
|
|
697
|
+
catch {
|
|
698
|
+
// ignore
|
|
699
|
+
}
|
|
700
|
+
this.batchTimer = null;
|
|
683
701
|
}
|
|
684
|
-
|
|
685
|
-
|
|
702
|
+
// 清理可能的 pendingIdleCallback
|
|
703
|
+
if (this.pendingIdleCallback !== null && typeof cancelIdleCallback !== 'undefined') {
|
|
704
|
+
try {
|
|
705
|
+
cancelIdleCallback(this.pendingIdleCallback);
|
|
706
|
+
}
|
|
707
|
+
catch {
|
|
708
|
+
// ignore
|
|
709
|
+
}
|
|
710
|
+
this.pendingIdleCallback = null;
|
|
686
711
|
}
|
|
687
|
-
this.batchTimer = null;
|
|
688
712
|
}
|
|
689
713
|
getDesiredBatchFlushDueAt(nowTs) {
|
|
690
714
|
const start = this.initTs || nowTs;
|
|
@@ -897,7 +921,14 @@ class ClsLoggerCore {
|
|
|
897
921
|
// 先清空,再尝试发送
|
|
898
922
|
writeStringStorage(this.failedCacheKey, JSON.stringify([]));
|
|
899
923
|
this.memoryQueue.unshift(...logs);
|
|
900
|
-
|
|
924
|
+
// 触发定时器而非直接 flush,以尊重 startupDelayMs 配置
|
|
925
|
+
if (!this.batchTimer) {
|
|
926
|
+
const now = Date.now();
|
|
927
|
+
const desiredDueAt = this.getDesiredBatchFlushDueAt(now);
|
|
928
|
+
const desiredDelay = Math.max(0, desiredDueAt - now);
|
|
929
|
+
this.batchTimerDueAt = desiredDueAt;
|
|
930
|
+
this.scheduleFlush(desiredDelay);
|
|
931
|
+
}
|
|
901
932
|
}
|
|
902
933
|
/**
|
|
903
934
|
* 统计/计数类日志:按字段展开上报(若 data 为空默认 1)
|
package/dist/web.js
CHANGED
|
@@ -202,8 +202,10 @@ class ClsLoggerCore {
|
|
|
202
202
|
this.batchTimerDueAt = null;
|
|
203
203
|
this.initTs = 0;
|
|
204
204
|
this.startupDelayMs = 0;
|
|
205
|
+
this.startupMaxSize = 0; // 启动窗口内的 maxSize,0 表示使用默认计算值
|
|
205
206
|
this.useIdleCallback = false;
|
|
206
207
|
this.idleTimeout = 3000;
|
|
208
|
+
this.pendingIdleCallback = null; // requestIdleCallback 的 id
|
|
207
209
|
this.visibilityCleanup = null;
|
|
208
210
|
// 参考文档:失败缓存 + 重试
|
|
209
211
|
this.failedCacheKey = 'cls_failed_logs';
|
|
@@ -276,6 +278,8 @@ class ClsLoggerCore {
|
|
|
276
278
|
this.startupDelayMs = options.batch?.startupDelayMs ?? this.startupDelayMs;
|
|
277
279
|
this.useIdleCallback = options.batch?.useIdleCallback ?? this.useIdleCallback;
|
|
278
280
|
this.idleTimeout = options.batch?.idleTimeout ?? this.idleTimeout;
|
|
281
|
+
// startupMaxSize:启动窗口内的队列阈值,默认为 maxSize * 10(至少 200)
|
|
282
|
+
this.startupMaxSize = options.batch?.startupMaxSize ?? Math.max(this.batchMaxSize * 10, 200);
|
|
279
283
|
this.failedCacheKey = options.failedCacheKey ?? this.failedCacheKey;
|
|
280
284
|
this.failedCacheMax = options.failedCacheMax ?? this.failedCacheMax;
|
|
281
285
|
// 预热(避免首条日志触发 import/初始化开销)
|
|
@@ -336,10 +340,17 @@ class ClsLoggerCore {
|
|
|
336
340
|
return;
|
|
337
341
|
const handleVisibilityChange = () => {
|
|
338
342
|
if (document.visibilityState === 'hidden') {
|
|
339
|
-
|
|
343
|
+
// 使用微任务延迟 flush,确保 web-vitals 等第三方库的 visibilitychange 回调先执行
|
|
344
|
+
// 这样 LCP/CLS/INP 等指标能先入队,再被 flush 发送
|
|
345
|
+
// 注意:queueMicrotask 比 setTimeout(0) 更可靠,不会被延迟太久
|
|
346
|
+
queueMicrotask(() => {
|
|
347
|
+
this.flushBatchSync();
|
|
348
|
+
});
|
|
340
349
|
}
|
|
341
350
|
};
|
|
342
351
|
const handlePageHide = () => {
|
|
352
|
+
// pagehide 不能延迟,因为浏览器可能立即关闭页面
|
|
353
|
+
// 但 pagehide 通常在 visibilitychange 之后触发,此时队列应该已经包含 web-vitals 指标
|
|
343
354
|
this.flushBatchSync();
|
|
344
355
|
};
|
|
345
356
|
document.addEventListener('visibilitychange', handleVisibilityChange);
|
|
@@ -651,11 +662,15 @@ class ClsLoggerCore {
|
|
|
651
662
|
return;
|
|
652
663
|
}
|
|
653
664
|
this.memoryQueue.push(log);
|
|
654
|
-
|
|
665
|
+
const now = Date.now();
|
|
666
|
+
// 判断是否在启动合并窗口内
|
|
667
|
+
const inStartupWindow = this.startupDelayMs > 0 && now - this.initTs < this.startupDelayMs;
|
|
668
|
+
// 启动窗口内使用 startupMaxSize,正常情况使用 batchMaxSize
|
|
669
|
+
const effectiveMaxSize = inStartupWindow ? this.startupMaxSize : this.batchMaxSize;
|
|
670
|
+
if (this.memoryQueue.length >= effectiveMaxSize) {
|
|
655
671
|
void this.flushBatch();
|
|
656
672
|
return;
|
|
657
673
|
}
|
|
658
|
-
const now = Date.now();
|
|
659
674
|
const desiredDueAt = this.getDesiredBatchFlushDueAt(now);
|
|
660
675
|
const desiredDelay = Math.max(0, desiredDueAt - now);
|
|
661
676
|
if (!this.batchTimer) {
|
|
@@ -672,17 +687,19 @@ class ClsLoggerCore {
|
|
|
672
687
|
}
|
|
673
688
|
/**
|
|
674
689
|
* 调度批量发送
|
|
675
|
-
* -
|
|
676
|
-
* -
|
|
690
|
+
* - 先使用 setTimeout 保证最小延迟(desiredDelay)
|
|
691
|
+
* - 若开启 useIdleCallback,在延迟结束后等待浏览器空闲再执行
|
|
677
692
|
*/
|
|
678
693
|
scheduleFlush(desiredDelay) {
|
|
679
694
|
if (this.useIdleCallback && typeof requestIdleCallback !== 'undefined') {
|
|
680
|
-
//
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
695
|
+
// 先 setTimeout 保证最小延迟,再 requestIdleCallback 在空闲时执行
|
|
696
|
+
this.batchTimer = setTimeout(() => {
|
|
697
|
+
const idleId = requestIdleCallback(() => {
|
|
698
|
+
this.pendingIdleCallback = null;
|
|
699
|
+
void this.flushBatch();
|
|
700
|
+
}, { timeout: this.idleTimeout });
|
|
701
|
+
this.pendingIdleCallback = idleId;
|
|
702
|
+
}, desiredDelay);
|
|
686
703
|
}
|
|
687
704
|
else {
|
|
688
705
|
this.batchTimer = setTimeout(() => {
|
|
@@ -692,22 +709,29 @@ class ClsLoggerCore {
|
|
|
692
709
|
}
|
|
693
710
|
/**
|
|
694
711
|
* 取消已调度的批量发送
|
|
712
|
+
* - 同时清理 setTimeout 和可能的 requestIdleCallback
|
|
695
713
|
*/
|
|
696
714
|
cancelScheduledFlush() {
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
if (this.useIdleCallback && typeof cancelIdleCallback !== 'undefined') {
|
|
701
|
-
cancelIdleCallback(this.batchTimer);
|
|
702
|
-
}
|
|
703
|
-
else {
|
|
715
|
+
// 清理 setTimeout
|
|
716
|
+
if (this.batchTimer) {
|
|
717
|
+
try {
|
|
704
718
|
clearTimeout(this.batchTimer);
|
|
705
719
|
}
|
|
720
|
+
catch {
|
|
721
|
+
// ignore
|
|
722
|
+
}
|
|
723
|
+
this.batchTimer = null;
|
|
706
724
|
}
|
|
707
|
-
|
|
708
|
-
|
|
725
|
+
// 清理可能的 pendingIdleCallback
|
|
726
|
+
if (this.pendingIdleCallback !== null && typeof cancelIdleCallback !== 'undefined') {
|
|
727
|
+
try {
|
|
728
|
+
cancelIdleCallback(this.pendingIdleCallback);
|
|
729
|
+
}
|
|
730
|
+
catch {
|
|
731
|
+
// ignore
|
|
732
|
+
}
|
|
733
|
+
this.pendingIdleCallback = null;
|
|
709
734
|
}
|
|
710
|
-
this.batchTimer = null;
|
|
711
735
|
}
|
|
712
736
|
getDesiredBatchFlushDueAt(nowTs) {
|
|
713
737
|
const start = this.initTs || nowTs;
|
|
@@ -920,7 +944,14 @@ class ClsLoggerCore {
|
|
|
920
944
|
// 先清空,再尝试发送
|
|
921
945
|
writeStringStorage(this.failedCacheKey, JSON.stringify([]));
|
|
922
946
|
this.memoryQueue.unshift(...logs);
|
|
923
|
-
|
|
947
|
+
// 触发定时器而非直接 flush,以尊重 startupDelayMs 配置
|
|
948
|
+
if (!this.batchTimer) {
|
|
949
|
+
const now = Date.now();
|
|
950
|
+
const desiredDueAt = this.getDesiredBatchFlushDueAt(now);
|
|
951
|
+
const desiredDelay = Math.max(0, desiredDueAt - now);
|
|
952
|
+
this.batchTimerDueAt = desiredDueAt;
|
|
953
|
+
this.scheduleFlush(desiredDelay);
|
|
954
|
+
}
|
|
924
955
|
}
|
|
925
956
|
/**
|
|
926
957
|
* 统计/计数类日志:按字段展开上报(若 data 为空默认 1)
|