@be-link/cls-logger 1.0.11 → 1.0.13
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 +430 -68
- package/dist/ClsLoggerCore.d.ts +38 -5
- package/dist/ClsLoggerCore.d.ts.map +1 -1
- package/dist/index.esm.js +380 -62
- package/dist/index.js +380 -62
- package/dist/index.umd.js +380 -62
- package/dist/mini/ClsLogger.d.ts +6 -0
- package/dist/mini/ClsLogger.d.ts.map +1 -1
- package/dist/mini/behaviorMonitor.d.ts.map +1 -1
- package/dist/mini/deviceInfo.d.ts.map +1 -1
- package/dist/mini/errorMonitor.d.ts.map +1 -1
- package/dist/mini.esm.js +298 -49
- package/dist/mini.js +298 -49
- package/dist/types.d.ts +41 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/web/deviceInfo.d.ts.map +1 -1
- package/dist/web/errorMonitor.d.ts.map +1 -1
- package/dist/web/performanceMonitor.d.ts.map +1 -1
- package/dist/web.esm.js +304 -38
- package/dist/web.js +304 -38
- package/package.json +1 -1
package/dist/mini/ClsLogger.d.ts
CHANGED
|
@@ -9,6 +9,12 @@ export declare class ClsLoggerMini extends ClsLoggerCore {
|
|
|
9
9
|
protected installPerformanceMonitor(report: (type: string, data: FlatFields) => void, options: boolean | PerformanceMonitorOptions): void;
|
|
10
10
|
protected installBehaviorMonitor(report: (type: string, data: FlatFields) => void, options: boolean | BehaviorMonitorOptions): () => void;
|
|
11
11
|
protected createDeviceInfoBaseFields(options: boolean | DeviceInfoOptions | undefined): (() => FlatFields) | null;
|
|
12
|
+
/**
|
|
13
|
+
* 小程序版本的可见性监听
|
|
14
|
+
* - 监听 wx.onAppHide 在应用隐藏时发送日志
|
|
15
|
+
* - 确保用户关闭小程序或切后台时,队列中的日志不会丢失
|
|
16
|
+
*/
|
|
17
|
+
protected setupVisibilityListener(): void;
|
|
12
18
|
private normalize;
|
|
13
19
|
}
|
|
14
20
|
//# sourceMappingURL=ClsLogger.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ClsLogger.d.ts","sourceRoot":"","sources":["../../src/mini/ClsLogger.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAMnD,OAAO,KAAK,EACV,UAAU,EACV,qBAAqB,EACrB,mBAAmB,EACnB,yBAAyB,EACzB,sBAAsB,EACtB,iBAAiB,EAClB,MAAM,UAAU,CAAC;AAIlB,qBAAa,aAAc,SAAQ,aAAa;IAC9C,SAAS,CAAC,OAAO,EAAE,OAAO,UAAU,EAAE,OAAO,CAAiB;cAE9C,OAAO,IAAI,OAAO,CAAC,YAAY,CAAC;IAahD,SAAS,CAAC,qBAAqB,CAC7B,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,KAAK,IAAI,EAChD,OAAO,EAAE,qBAAqB,GAC7B,IAAI;IAIP,SAAS,CAAC,mBAAmB,CAC3B,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,KAAK,IAAI,EAChD,OAAO,EAAE,OAAO,GAAG,mBAAmB,GACrC,IAAI;IAIP,SAAS,CAAC,yBAAyB,CACjC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,KAAK,IAAI,EAChD,OAAO,EAAE,OAAO,GAAG,yBAAyB,GAC3C,IAAI;IAIP,SAAS,CAAC,sBAAsB,CAC9B,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,KAAK,IAAI,EAChD,OAAO,EAAE,OAAO,GAAG,sBAAsB,GACxC,MAAM,IAAI;IAIb,SAAS,CAAC,0BAA0B,CAAC,OAAO,EAAE,OAAO,GAAG,iBAAiB,GAAG,SAAS,GAAG,CAAC,MAAM,UAAU,CAAC,GAAG,IAAI;IAIjH,OAAO,CAAC,SAAS;CAYlB"}
|
|
1
|
+
{"version":3,"file":"ClsLogger.d.ts","sourceRoot":"","sources":["../../src/mini/ClsLogger.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAMnD,OAAO,KAAK,EACV,UAAU,EACV,qBAAqB,EACrB,mBAAmB,EACnB,yBAAyB,EACzB,sBAAsB,EACtB,iBAAiB,EAClB,MAAM,UAAU,CAAC;AAIlB,qBAAa,aAAc,SAAQ,aAAa;IAC9C,SAAS,CAAC,OAAO,EAAE,OAAO,UAAU,EAAE,OAAO,CAAiB;cAE9C,OAAO,IAAI,OAAO,CAAC,YAAY,CAAC;IAahD,SAAS,CAAC,qBAAqB,CAC7B,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,KAAK,IAAI,EAChD,OAAO,EAAE,qBAAqB,GAC7B,IAAI;IAIP,SAAS,CAAC,mBAAmB,CAC3B,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,KAAK,IAAI,EAChD,OAAO,EAAE,OAAO,GAAG,mBAAmB,GACrC,IAAI;IAIP,SAAS,CAAC,yBAAyB,CACjC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,KAAK,IAAI,EAChD,OAAO,EAAE,OAAO,GAAG,yBAAyB,GAC3C,IAAI;IAIP,SAAS,CAAC,sBAAsB,CAC9B,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,KAAK,IAAI,EAChD,OAAO,EAAE,OAAO,GAAG,sBAAsB,GACxC,MAAM,IAAI;IAIb,SAAS,CAAC,0BAA0B,CAAC,OAAO,EAAE,OAAO,GAAG,iBAAiB,GAAG,SAAS,GAAG,CAAC,MAAM,UAAU,CAAC,GAAG,IAAI;IAIjH;;;;OAIG;IACH,SAAS,CAAC,uBAAuB,IAAI,IAAI;IAezC,OAAO,CAAC,SAAS;CAYlB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"behaviorMonitor.d.ts","sourceRoot":"","sources":["../../src/mini/behaviorMonitor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,UAAU,EAAU,MAAM,UAAU,CAAC;AAG3E,KAAK,QAAQ,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,KAAK,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"behaviorMonitor.d.ts","sourceRoot":"","sources":["../../src/mini/behaviorMonitor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,UAAU,EAAU,MAAM,UAAU,CAAC;AAG3E,KAAK,QAAQ,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,KAAK,IAAI,CAAC;AAgFzD,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAE,sBAA2B,GAAG,MAAM,IAAI,CA6N7G"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deviceInfo.d.ts","sourceRoot":"","sources":["../../src/mini/deviceInfo.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"deviceInfo.d.ts","sourceRoot":"","sources":["../../src/mini/deviceInfo.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAuE9D,wBAAgB,8BAA8B,CAC5C,IAAI,EAAE,OAAO,GAAG,iBAAiB,GAAG,SAAS,GAC5C,CAAC,MAAM,UAAU,CAAC,GAAG,IAAI,CAqC3B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errorMonitor.d.ts","sourceRoot":"","sources":["../../src/mini/errorMonitor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAGhE,KAAK,QAAQ,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,KAAK,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"errorMonitor.d.ts","sourceRoot":"","sources":["../../src/mini/errorMonitor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAGhE,KAAK,QAAQ,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,KAAK,IAAI,CAAC;AAkQzD,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,QAAQ,EAAE,IAAI,GAAE,OAAO,GAAG,mBAAmB,GAAG,SAAc,GAAG,IAAI,CAiBpH"}
|
package/dist/mini.esm.js
CHANGED
|
@@ -178,6 +178,11 @@ class ClsLoggerCore {
|
|
|
178
178
|
this.batchTimerDueAt = null;
|
|
179
179
|
this.initTs = 0;
|
|
180
180
|
this.startupDelayMs = 0;
|
|
181
|
+
this.startupMaxSize = 0; // 启动窗口内的 maxSize,0 表示使用默认计算值
|
|
182
|
+
this.useIdleCallback = false;
|
|
183
|
+
this.idleTimeout = 3000;
|
|
184
|
+
this.pendingIdleCallback = null; // requestIdleCallback 的 id
|
|
185
|
+
this.visibilityCleanup = null;
|
|
181
186
|
// 参考文档:失败缓存 + 重试
|
|
182
187
|
this.failedCacheKey = 'cls_failed_logs';
|
|
183
188
|
this.failedCacheMax = 200;
|
|
@@ -201,7 +206,7 @@ class ClsLoggerCore {
|
|
|
201
206
|
}
|
|
202
207
|
return 'browser';
|
|
203
208
|
}
|
|
204
|
-
init(options) {
|
|
209
|
+
async init(options) {
|
|
205
210
|
this.initTs = Date.now();
|
|
206
211
|
const topicId = options?.tencentCloud?.topicID ?? options?.topic_id ?? options?.topicID ?? this.topicId;
|
|
207
212
|
const endpoint = options?.tencentCloud?.endpoint ?? options?.endpoint ?? this.endpoint;
|
|
@@ -210,7 +215,7 @@ class ClsLoggerCore {
|
|
|
210
215
|
if (!topicId) {
|
|
211
216
|
// eslint-disable-next-line no-console
|
|
212
217
|
console.warn('ClsLogger.init 没有传 topicID/topic_id');
|
|
213
|
-
return;
|
|
218
|
+
return false;
|
|
214
219
|
}
|
|
215
220
|
const nextEnvType = options.envType ?? this.detectEnvType();
|
|
216
221
|
// envType/endpoint/retryTimes 变化时:重置 client(以及可能的 sdk)
|
|
@@ -247,15 +252,21 @@ class ClsLoggerCore {
|
|
|
247
252
|
this.batchMaxSize = options.batch?.maxSize ?? this.batchMaxSize;
|
|
248
253
|
this.batchIntervalMs = options.batch?.intervalMs ?? this.batchIntervalMs;
|
|
249
254
|
this.startupDelayMs = options.batch?.startupDelayMs ?? this.startupDelayMs;
|
|
255
|
+
this.useIdleCallback = options.batch?.useIdleCallback ?? this.useIdleCallback;
|
|
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);
|
|
250
259
|
this.failedCacheKey = options.failedCacheKey ?? this.failedCacheKey;
|
|
251
260
|
this.failedCacheMax = options.failedCacheMax ?? this.failedCacheMax;
|
|
252
261
|
// 预热(避免首条日志触发 import/初始化开销)
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
262
|
+
const sdkReadyPromise = this.getInstance()
|
|
263
|
+
.then(() => true)
|
|
264
|
+
.catch(() => false);
|
|
256
265
|
if (this.enabled) {
|
|
257
266
|
// 启动时尝试发送失败缓存
|
|
258
267
|
this.flushFailed();
|
|
268
|
+
// 添加页面可见性监听(确保页面关闭时数据不丢失)
|
|
269
|
+
this.setupVisibilityListener();
|
|
259
270
|
// 初始化后立即启动请求监听
|
|
260
271
|
this.startRequestMonitor(options.requestMonitor);
|
|
261
272
|
// 初始化后立即启动错误监控/性能监控
|
|
@@ -264,6 +275,7 @@ class ClsLoggerCore {
|
|
|
264
275
|
// 初始化后立即启动行为埋点(PV/UV/点击)
|
|
265
276
|
this.startBehaviorMonitor(options.behaviorMonitor);
|
|
266
277
|
}
|
|
278
|
+
return sdkReadyPromise;
|
|
267
279
|
}
|
|
268
280
|
getBaseFields() {
|
|
269
281
|
let auto = undefined;
|
|
@@ -292,6 +304,97 @@ class ClsLoggerCore {
|
|
|
292
304
|
return auto;
|
|
293
305
|
return undefined;
|
|
294
306
|
}
|
|
307
|
+
/**
|
|
308
|
+
* 设置页面可见性监听
|
|
309
|
+
* - visibilitychange: 页面隐藏时使用 sendBeacon 发送队列
|
|
310
|
+
* - pagehide: 作为移动端 fallback
|
|
311
|
+
* - 子类可覆写此方法以实现平台特定的监听(如小程序的 wx.onAppHide)
|
|
312
|
+
*/
|
|
313
|
+
setupVisibilityListener() {
|
|
314
|
+
if (typeof document === 'undefined' || typeof window === 'undefined')
|
|
315
|
+
return;
|
|
316
|
+
// 避免重复监听
|
|
317
|
+
if (this.visibilityCleanup)
|
|
318
|
+
return;
|
|
319
|
+
const handleVisibilityChange = () => {
|
|
320
|
+
if (document.visibilityState === 'hidden') {
|
|
321
|
+
// 使用微任务延迟 flush,确保 web-vitals 等第三方库的 visibilitychange 回调先执行
|
|
322
|
+
// 这样 LCP/CLS/INP 等指标能先入队,再被 flush 发送
|
|
323
|
+
// 注意:queueMicrotask 比 setTimeout(0) 更可靠,不会被延迟太久
|
|
324
|
+
queueMicrotask(() => {
|
|
325
|
+
this.flushBatchSync();
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
const handlePageHide = () => {
|
|
330
|
+
// pagehide 不能延迟,因为浏览器可能立即关闭页面
|
|
331
|
+
// 但 pagehide 通常在 visibilitychange 之后触发,此时队列应该已经包含 web-vitals 指标
|
|
332
|
+
this.flushBatchSync();
|
|
333
|
+
};
|
|
334
|
+
document.addEventListener('visibilitychange', handleVisibilityChange);
|
|
335
|
+
window.addEventListener('pagehide', handlePageHide);
|
|
336
|
+
this.visibilityCleanup = () => {
|
|
337
|
+
document.removeEventListener('visibilitychange', handleVisibilityChange);
|
|
338
|
+
window.removeEventListener('pagehide', handlePageHide);
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* 同步发送内存队列(使用 sendBeacon)
|
|
343
|
+
* - 用于页面关闭时确保数据发送
|
|
344
|
+
* - sendBeacon 不可用时降级为缓存到 localStorage
|
|
345
|
+
*/
|
|
346
|
+
flushBatchSync() {
|
|
347
|
+
if (this.memoryQueue.length === 0)
|
|
348
|
+
return;
|
|
349
|
+
// 清除定时器
|
|
350
|
+
if (this.batchTimer) {
|
|
351
|
+
try {
|
|
352
|
+
if (this.useIdleCallback && typeof cancelIdleCallback !== 'undefined') {
|
|
353
|
+
cancelIdleCallback(this.batchTimer);
|
|
354
|
+
}
|
|
355
|
+
else {
|
|
356
|
+
clearTimeout(this.batchTimer);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
catch {
|
|
360
|
+
// ignore
|
|
361
|
+
}
|
|
362
|
+
this.batchTimer = null;
|
|
363
|
+
}
|
|
364
|
+
this.batchTimerDueAt = null;
|
|
365
|
+
const logs = [...this.memoryQueue];
|
|
366
|
+
this.memoryQueue = [];
|
|
367
|
+
// 优先使用 sendBeacon(页面关闭时可靠发送)
|
|
368
|
+
if (typeof navigator !== 'undefined' && typeof navigator.sendBeacon === 'function') {
|
|
369
|
+
try {
|
|
370
|
+
const payload = this.buildSendBeaconPayload(logs);
|
|
371
|
+
const blob = new Blob([payload], { type: 'application/json' });
|
|
372
|
+
const url = `${this.endpoint}/structuredlog?topic_id=${this.topicId}`;
|
|
373
|
+
const success = navigator.sendBeacon(url, blob);
|
|
374
|
+
if (!success) {
|
|
375
|
+
// sendBeacon 返回 false 时,降级缓存
|
|
376
|
+
this.cacheFailedReportLogs(logs);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
catch {
|
|
380
|
+
this.cacheFailedReportLogs(logs);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
else {
|
|
384
|
+
// 不支持 sendBeacon,降级缓存到 localStorage
|
|
385
|
+
this.cacheFailedReportLogs(logs);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* 构建 sendBeacon 的 payload
|
|
390
|
+
*/
|
|
391
|
+
buildSendBeaconPayload(logs) {
|
|
392
|
+
const logList = logs.map((log) => this.buildReportFields(log));
|
|
393
|
+
return JSON.stringify({
|
|
394
|
+
source: this.source,
|
|
395
|
+
logs: logList,
|
|
396
|
+
});
|
|
397
|
+
}
|
|
295
398
|
startRequestMonitor(requestMonitor) {
|
|
296
399
|
if (this.requestMonitorStarted)
|
|
297
400
|
return;
|
|
@@ -537,32 +640,75 @@ class ClsLoggerCore {
|
|
|
537
640
|
return;
|
|
538
641
|
}
|
|
539
642
|
this.memoryQueue.push(log);
|
|
540
|
-
|
|
643
|
+
const now = Date.now();
|
|
644
|
+
// 判断是否在启动合并窗口内
|
|
645
|
+
const inStartupWindow = this.startupDelayMs > 0 && now - this.initTs < this.startupDelayMs;
|
|
646
|
+
// 启动窗口内使用 startupMaxSize,正常情况使用 batchMaxSize
|
|
647
|
+
const effectiveMaxSize = inStartupWindow ? this.startupMaxSize : this.batchMaxSize;
|
|
648
|
+
if (this.memoryQueue.length >= effectiveMaxSize) {
|
|
541
649
|
void this.flushBatch();
|
|
542
650
|
return;
|
|
543
651
|
}
|
|
544
|
-
const now = Date.now();
|
|
545
652
|
const desiredDueAt = this.getDesiredBatchFlushDueAt(now);
|
|
546
653
|
const desiredDelay = Math.max(0, desiredDueAt - now);
|
|
547
654
|
if (!this.batchTimer) {
|
|
548
655
|
this.batchTimerDueAt = desiredDueAt;
|
|
656
|
+
this.scheduleFlush(desiredDelay);
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
// 启动合并窗口内:如果当前 timer 会"更早"触发,则延后到窗口结束,尽量减少多次发送
|
|
660
|
+
if (this.batchTimerDueAt !== null && this.batchTimerDueAt < desiredDueAt) {
|
|
661
|
+
this.cancelScheduledFlush();
|
|
662
|
+
this.batchTimerDueAt = desiredDueAt;
|
|
663
|
+
this.scheduleFlush(desiredDelay);
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
/**
|
|
667
|
+
* 调度批量发送
|
|
668
|
+
* - 先使用 setTimeout 保证最小延迟(desiredDelay)
|
|
669
|
+
* - 若开启 useIdleCallback,在延迟结束后等待浏览器空闲再执行
|
|
670
|
+
*/
|
|
671
|
+
scheduleFlush(desiredDelay) {
|
|
672
|
+
if (this.useIdleCallback && typeof requestIdleCallback !== 'undefined') {
|
|
673
|
+
// 先 setTimeout 保证最小延迟,再 requestIdleCallback 在空闲时执行
|
|
674
|
+
this.batchTimer = setTimeout(() => {
|
|
675
|
+
const idleId = requestIdleCallback(() => {
|
|
676
|
+
this.pendingIdleCallback = null;
|
|
677
|
+
void this.flushBatch();
|
|
678
|
+
}, { timeout: this.idleTimeout });
|
|
679
|
+
this.pendingIdleCallback = idleId;
|
|
680
|
+
}, desiredDelay);
|
|
681
|
+
}
|
|
682
|
+
else {
|
|
549
683
|
this.batchTimer = setTimeout(() => {
|
|
550
684
|
void this.flushBatch();
|
|
551
685
|
}, desiredDelay);
|
|
552
|
-
return;
|
|
553
686
|
}
|
|
554
|
-
|
|
555
|
-
|
|
687
|
+
}
|
|
688
|
+
/**
|
|
689
|
+
* 取消已调度的批量发送
|
|
690
|
+
* - 同时清理 setTimeout 和可能的 requestIdleCallback
|
|
691
|
+
*/
|
|
692
|
+
cancelScheduledFlush() {
|
|
693
|
+
// 清理 setTimeout
|
|
694
|
+
if (this.batchTimer) {
|
|
556
695
|
try {
|
|
557
696
|
clearTimeout(this.batchTimer);
|
|
558
697
|
}
|
|
559
698
|
catch {
|
|
560
699
|
// ignore
|
|
561
700
|
}
|
|
562
|
-
this.
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
701
|
+
this.batchTimer = null;
|
|
702
|
+
}
|
|
703
|
+
// 清理可能的 pendingIdleCallback
|
|
704
|
+
if (this.pendingIdleCallback !== null && typeof cancelIdleCallback !== 'undefined') {
|
|
705
|
+
try {
|
|
706
|
+
cancelIdleCallback(this.pendingIdleCallback);
|
|
707
|
+
}
|
|
708
|
+
catch {
|
|
709
|
+
// ignore
|
|
710
|
+
}
|
|
711
|
+
this.pendingIdleCallback = null;
|
|
566
712
|
}
|
|
567
713
|
}
|
|
568
714
|
getDesiredBatchFlushDueAt(nowTs) {
|
|
@@ -575,7 +721,7 @@ class ClsLoggerCore {
|
|
|
575
721
|
}
|
|
576
722
|
return nowTs + this.batchIntervalMs;
|
|
577
723
|
}
|
|
578
|
-
info(message, data = {}) {
|
|
724
|
+
info(message, data = {}, options) {
|
|
579
725
|
let msg = '';
|
|
580
726
|
let extra = {};
|
|
581
727
|
if (message instanceof Error) {
|
|
@@ -591,9 +737,18 @@ class ClsLoggerCore {
|
|
|
591
737
|
extra = data;
|
|
592
738
|
}
|
|
593
739
|
const payload = normalizeFlatFields({ message: msg, ...extra }, 'info');
|
|
594
|
-
|
|
740
|
+
const log = { type: 'info', data: payload, timestamp: Date.now() };
|
|
741
|
+
// info 默认走批量队列,支持 immediate 选项立即发送
|
|
742
|
+
if (options?.immediate) {
|
|
743
|
+
void this.sendReportLogs([log]).catch(() => {
|
|
744
|
+
this.cacheFailedReportLogs([log]);
|
|
745
|
+
});
|
|
746
|
+
}
|
|
747
|
+
else {
|
|
748
|
+
this.report(log);
|
|
749
|
+
}
|
|
595
750
|
}
|
|
596
|
-
warn(message, data = {}) {
|
|
751
|
+
warn(message, data = {}, options) {
|
|
597
752
|
let msg = '';
|
|
598
753
|
let extra = {};
|
|
599
754
|
if (message instanceof Error) {
|
|
@@ -609,9 +764,18 @@ class ClsLoggerCore {
|
|
|
609
764
|
extra = data;
|
|
610
765
|
}
|
|
611
766
|
const payload = normalizeFlatFields({ message: msg, ...extra }, 'warn');
|
|
612
|
-
|
|
767
|
+
const log = { type: 'warn', data: payload, timestamp: Date.now() };
|
|
768
|
+
// warn 默认走批量队列,支持 immediate 选项立即发送
|
|
769
|
+
if (options?.immediate) {
|
|
770
|
+
void this.sendReportLogs([log]).catch(() => {
|
|
771
|
+
this.cacheFailedReportLogs([log]);
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
else {
|
|
775
|
+
this.report(log);
|
|
776
|
+
}
|
|
613
777
|
}
|
|
614
|
-
error(message, data = {}) {
|
|
778
|
+
error(message, data = {}, options) {
|
|
615
779
|
let msg = '';
|
|
616
780
|
let extra = {};
|
|
617
781
|
if (message instanceof Error) {
|
|
@@ -627,7 +791,17 @@ class ClsLoggerCore {
|
|
|
627
791
|
extra = data;
|
|
628
792
|
}
|
|
629
793
|
const payload = normalizeFlatFields({ message: msg, ...extra }, 'error');
|
|
630
|
-
|
|
794
|
+
const log = { type: 'error', data: payload, timestamp: Date.now() };
|
|
795
|
+
// error 默认即时上报,除非显式指定 immediate: false
|
|
796
|
+
const immediate = options?.immediate ?? true;
|
|
797
|
+
if (immediate) {
|
|
798
|
+
void this.sendReportLogs([log]).catch(() => {
|
|
799
|
+
this.cacheFailedReportLogs([log]);
|
|
800
|
+
});
|
|
801
|
+
}
|
|
802
|
+
else {
|
|
803
|
+
this.report(log);
|
|
804
|
+
}
|
|
631
805
|
}
|
|
632
806
|
track(trackType, data = {}) {
|
|
633
807
|
if (!trackType)
|
|
@@ -642,10 +816,7 @@ class ClsLoggerCore {
|
|
|
642
816
|
* 立即发送内存队列
|
|
643
817
|
*/
|
|
644
818
|
async flushBatch() {
|
|
645
|
-
|
|
646
|
-
clearTimeout(this.batchTimer);
|
|
647
|
-
this.batchTimer = null;
|
|
648
|
-
}
|
|
819
|
+
this.cancelScheduledFlush();
|
|
649
820
|
this.batchTimerDueAt = null;
|
|
650
821
|
if (this.memoryQueue.length === 0)
|
|
651
822
|
return;
|
|
@@ -751,7 +922,14 @@ class ClsLoggerCore {
|
|
|
751
922
|
// 先清空,再尝试发送
|
|
752
923
|
writeStringStorage(this.failedCacheKey, JSON.stringify([]));
|
|
753
924
|
this.memoryQueue.unshift(...logs);
|
|
754
|
-
|
|
925
|
+
// 触发定时器而非直接 flush,以尊重 startupDelayMs 配置
|
|
926
|
+
if (!this.batchTimer) {
|
|
927
|
+
const now = Date.now();
|
|
928
|
+
const desiredDueAt = this.getDesiredBatchFlushDueAt(now);
|
|
929
|
+
const desiredDelay = Math.max(0, desiredDueAt - now);
|
|
930
|
+
this.batchTimerDueAt = desiredDueAt;
|
|
931
|
+
this.scheduleFlush(desiredDelay);
|
|
932
|
+
}
|
|
755
933
|
}
|
|
756
934
|
/**
|
|
757
935
|
* 统计/计数类日志:按字段展开上报(若 data 为空默认 1)
|
|
@@ -987,6 +1165,13 @@ function installMiniRequestMonitor(report, opts = {}) {
|
|
|
987
1165
|
const DEFAULT_MAX_TEXT = 4000;
|
|
988
1166
|
const DEFAULT_DEDUPE_WINDOW_MS = 3000;
|
|
989
1167
|
const DEFAULT_DEDUPE_MAX_KEYS = 200;
|
|
1168
|
+
/** 默认忽略的无意义错误信息 */
|
|
1169
|
+
const DEFAULT_IGNORE_MESSAGES = [
|
|
1170
|
+
'Script error.',
|
|
1171
|
+
'Script error',
|
|
1172
|
+
/^ResizeObserver loop/,
|
|
1173
|
+
'Permission was denied',
|
|
1174
|
+
];
|
|
990
1175
|
function truncate(s, maxLen) {
|
|
991
1176
|
if (!s)
|
|
992
1177
|
return s;
|
|
@@ -999,12 +1184,33 @@ function sampleHit$1(sampleRate) {
|
|
|
999
1184
|
return false;
|
|
1000
1185
|
return Math.random() < sampleRate;
|
|
1001
1186
|
}
|
|
1187
|
+
/** 检查消息是否应该被忽略 */
|
|
1188
|
+
function shouldIgnoreMessage(message, ignorePatterns) {
|
|
1189
|
+
if (!message || ignorePatterns.length === 0)
|
|
1190
|
+
return false;
|
|
1191
|
+
return ignorePatterns.some((pattern) => {
|
|
1192
|
+
if (typeof pattern === 'string') {
|
|
1193
|
+
return message === pattern || message.includes(pattern);
|
|
1194
|
+
}
|
|
1195
|
+
return pattern.test(message);
|
|
1196
|
+
});
|
|
1197
|
+
}
|
|
1002
1198
|
function getMpPagePath() {
|
|
1003
1199
|
try {
|
|
1004
1200
|
const pages = globalThis.getCurrentPages?.();
|
|
1005
1201
|
if (Array.isArray(pages) && pages.length > 0) {
|
|
1006
1202
|
const page = pages[pages.length - 1];
|
|
1007
|
-
|
|
1203
|
+
const route = page.route || page.__route__;
|
|
1204
|
+
if (typeof route === 'string') {
|
|
1205
|
+
let path = route.startsWith('/') ? route : `/${route}`;
|
|
1206
|
+
const options = page.options || {};
|
|
1207
|
+
const keys = Object.keys(options);
|
|
1208
|
+
if (keys.length > 0) {
|
|
1209
|
+
const qs = keys.map((k) => `${k}=${options[k]}`).join('&');
|
|
1210
|
+
path = `${path}?${qs}`;
|
|
1211
|
+
}
|
|
1212
|
+
return path;
|
|
1213
|
+
}
|
|
1008
1214
|
}
|
|
1009
1215
|
return '';
|
|
1010
1216
|
}
|
|
@@ -1093,6 +1299,9 @@ function installMiniProgramErrorMonitor(report, options) {
|
|
|
1093
1299
|
if (!sampleHit$1(options.sampleRate))
|
|
1094
1300
|
return;
|
|
1095
1301
|
const e = normalizeErrorLike(msg, options.maxTextLength);
|
|
1302
|
+
// 检查是否应该忽略此错误
|
|
1303
|
+
if (shouldIgnoreMessage(e.message, options.ignoreMessages))
|
|
1304
|
+
return;
|
|
1096
1305
|
const payload = {
|
|
1097
1306
|
pagePath: getMpPagePath(),
|
|
1098
1307
|
source: 'wx.onError',
|
|
@@ -1120,6 +1329,9 @@ function installMiniProgramErrorMonitor(report, options) {
|
|
|
1120
1329
|
if (!sampleHit$1(options.sampleRate))
|
|
1121
1330
|
return;
|
|
1122
1331
|
const e = normalizeErrorLike(res?.reason, options.maxTextLength);
|
|
1332
|
+
// 检查是否应该忽略此错误
|
|
1333
|
+
if (shouldIgnoreMessage(e.message, options.ignoreMessages))
|
|
1334
|
+
return;
|
|
1123
1335
|
const payload = {
|
|
1124
1336
|
pagePath: getMpPagePath(),
|
|
1125
1337
|
source: 'wx.onUnhandledRejection',
|
|
@@ -1151,15 +1363,18 @@ function installMiniProgramErrorMonitor(report, options) {
|
|
|
1151
1363
|
try {
|
|
1152
1364
|
if (sampleHit$1(options.sampleRate)) {
|
|
1153
1365
|
const e = normalizeErrorLike(args?.[0], options.maxTextLength);
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1366
|
+
// 检查是否应该忽略此错误
|
|
1367
|
+
if (!shouldIgnoreMessage(e.message, options.ignoreMessages)) {
|
|
1368
|
+
const payload = {
|
|
1369
|
+
pagePath: getMpPagePath(),
|
|
1370
|
+
source: 'App.onError',
|
|
1371
|
+
message: e.message,
|
|
1372
|
+
errorName: e.name,
|
|
1373
|
+
stack: e.stack,
|
|
1374
|
+
};
|
|
1375
|
+
if (shouldReport(buildErrorKey(options.reportType, payload)))
|
|
1376
|
+
report(options.reportType, payload);
|
|
1377
|
+
}
|
|
1163
1378
|
}
|
|
1164
1379
|
}
|
|
1165
1380
|
catch {
|
|
@@ -1175,15 +1390,18 @@ function installMiniProgramErrorMonitor(report, options) {
|
|
|
1175
1390
|
if (sampleHit$1(options.sampleRate)) {
|
|
1176
1391
|
const reason = args?.[0]?.reason ?? args?.[0];
|
|
1177
1392
|
const e = normalizeErrorLike(reason, options.maxTextLength);
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1393
|
+
// 检查是否应该忽略此错误
|
|
1394
|
+
if (!shouldIgnoreMessage(e.message, options.ignoreMessages)) {
|
|
1395
|
+
const payload = {
|
|
1396
|
+
pagePath: getMpPagePath(),
|
|
1397
|
+
source: 'App.onUnhandledRejection',
|
|
1398
|
+
message: e.message,
|
|
1399
|
+
errorName: e.name,
|
|
1400
|
+
stack: e.stack,
|
|
1401
|
+
};
|
|
1402
|
+
if (shouldReport(buildErrorKey(options.reportType, payload)))
|
|
1403
|
+
report(options.reportType, payload);
|
|
1404
|
+
}
|
|
1187
1405
|
}
|
|
1188
1406
|
}
|
|
1189
1407
|
catch {
|
|
@@ -1214,6 +1432,7 @@ function installMiniErrorMonitor(report, opts = {}) {
|
|
|
1214
1432
|
maxTextLength: raw.maxTextLength ?? DEFAULT_MAX_TEXT,
|
|
1215
1433
|
dedupeWindowMs: raw.dedupeWindowMs ?? DEFAULT_DEDUPE_WINDOW_MS,
|
|
1216
1434
|
dedupeMaxKeys: raw.dedupeMaxKeys ?? DEFAULT_DEDUPE_MAX_KEYS,
|
|
1435
|
+
ignoreMessages: raw.ignoreMessages ?? DEFAULT_IGNORE_MESSAGES,
|
|
1217
1436
|
};
|
|
1218
1437
|
installMiniProgramErrorMonitor(report, options);
|
|
1219
1438
|
}
|
|
@@ -1356,7 +1575,16 @@ function getMiniProgramPagePath() {
|
|
|
1356
1575
|
const pages = typeof g.getCurrentPages === 'function' ? g.getCurrentPages() : [];
|
|
1357
1576
|
const last = Array.isArray(pages) ? pages[pages.length - 1] : undefined;
|
|
1358
1577
|
const route = (last?.route || last?.__route__);
|
|
1359
|
-
|
|
1578
|
+
if (typeof route !== 'string')
|
|
1579
|
+
return '';
|
|
1580
|
+
let path = route.startsWith('/') ? route : `/${route}`;
|
|
1581
|
+
const options = last?.options || {};
|
|
1582
|
+
const keys = Object.keys(options);
|
|
1583
|
+
if (keys.length > 0) {
|
|
1584
|
+
const qs = keys.map((k) => `${k}=${options[k]}`).join('&');
|
|
1585
|
+
path = `${path}?${qs}`;
|
|
1586
|
+
}
|
|
1587
|
+
return path;
|
|
1360
1588
|
}
|
|
1361
1589
|
catch {
|
|
1362
1590
|
return '';
|
|
@@ -1619,7 +1847,7 @@ function getMiniProgramDeviceInfo(options) {
|
|
|
1619
1847
|
try {
|
|
1620
1848
|
if (typeof wxAny.getNetworkTypeSync === 'function') {
|
|
1621
1849
|
const n = wxAny.getNetworkTypeSync();
|
|
1622
|
-
out.networkType = n?.networkType ? String(n.networkType) : '';
|
|
1850
|
+
out.networkType = n?.networkType ? String(n.networkType).toUpperCase() : '';
|
|
1623
1851
|
}
|
|
1624
1852
|
else if (typeof wxAny.getNetworkType === 'function') {
|
|
1625
1853
|
// 异步更新:先不阻塞初始化
|
|
@@ -1629,7 +1857,9 @@ function getMiniProgramDeviceInfo(options) {
|
|
|
1629
1857
|
const g = globalThis;
|
|
1630
1858
|
if (!g.__beLinkClsLoggerDeviceInfo__)
|
|
1631
1859
|
g.__beLinkClsLoggerDeviceInfo__ = {};
|
|
1632
|
-
g.__beLinkClsLoggerDeviceInfo__.networkType = res?.networkType
|
|
1860
|
+
g.__beLinkClsLoggerDeviceInfo__.networkType = res?.networkType
|
|
1861
|
+
? String(res.networkType).toUpperCase()
|
|
1862
|
+
: '';
|
|
1633
1863
|
}
|
|
1634
1864
|
catch {
|
|
1635
1865
|
// ignore
|
|
@@ -1648,7 +1878,7 @@ function getMiniProgramDeviceInfo(options) {
|
|
|
1648
1878
|
const g = globalThis;
|
|
1649
1879
|
if (!g.__beLinkClsLoggerDeviceInfo__)
|
|
1650
1880
|
g.__beLinkClsLoggerDeviceInfo__ = {};
|
|
1651
|
-
g.__beLinkClsLoggerDeviceInfo__.networkType = res?.networkType ? String(res.networkType) : '';
|
|
1881
|
+
g.__beLinkClsLoggerDeviceInfo__.networkType = res?.networkType ? String(res.networkType).toUpperCase() : '';
|
|
1652
1882
|
}
|
|
1653
1883
|
catch {
|
|
1654
1884
|
// ignore
|
|
@@ -1689,7 +1919,7 @@ function createMiniDeviceInfoBaseFields(opts) {
|
|
|
1689
1919
|
try {
|
|
1690
1920
|
const g = globalThis;
|
|
1691
1921
|
const extra = g[globalKey];
|
|
1692
|
-
if (extra &&
|
|
1922
|
+
if (extra && typeof extra === 'object')
|
|
1693
1923
|
return { ...base, ...extra };
|
|
1694
1924
|
}
|
|
1695
1925
|
catch {
|
|
@@ -1732,6 +1962,25 @@ class ClsLoggerMini extends ClsLoggerCore {
|
|
|
1732
1962
|
createDeviceInfoBaseFields(options) {
|
|
1733
1963
|
return createMiniDeviceInfoBaseFields(options);
|
|
1734
1964
|
}
|
|
1965
|
+
/**
|
|
1966
|
+
* 小程序版本的可见性监听
|
|
1967
|
+
* - 监听 wx.onAppHide 在应用隐藏时发送日志
|
|
1968
|
+
* - 确保用户关闭小程序或切后台时,队列中的日志不会丢失
|
|
1969
|
+
*/
|
|
1970
|
+
setupVisibilityListener() {
|
|
1971
|
+
const wxAny = globalThis.wx;
|
|
1972
|
+
if (!wxAny || typeof wxAny.onAppHide !== 'function')
|
|
1973
|
+
return;
|
|
1974
|
+
// 避免重复监听
|
|
1975
|
+
const g = globalThis;
|
|
1976
|
+
if (g.__beLinkClsLoggerAppHideInstalled__)
|
|
1977
|
+
return;
|
|
1978
|
+
g.__beLinkClsLoggerAppHideInstalled__ = true;
|
|
1979
|
+
wxAny.onAppHide(() => {
|
|
1980
|
+
// 应用隐藏时立即发送队列中的日志
|
|
1981
|
+
void this.flushBatch();
|
|
1982
|
+
});
|
|
1983
|
+
}
|
|
1735
1984
|
normalize(m) {
|
|
1736
1985
|
const mod = (m?.default && m.default.AsyncClient ? m.default : m);
|
|
1737
1986
|
if (mod?.AsyncClient) {
|