@be-link/cls-logger 1.0.1-beta.16 → 1.0.1-beta.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/dist/index.js CHANGED
@@ -158,9 +158,9 @@ class ClsLoggerCore {
158
158
  this.sdkLoaderOverride = null;
159
159
  this.client = null;
160
160
  this.clientPromise = null;
161
- this.topicId = null;
162
- this.endpoint = 'ap-shanghai.cls.tencentcs.com';
163
- this.retryTimes = 10;
161
+ this.topicId = '17475bcd-6315-4b20-859c-e7b087fb3683';
162
+ this.endpoint = 'https://ap-shanghai.cls.tencentcs.com';
163
+ this.retryTimes = 3;
164
164
  this.source = '127.0.0.1';
165
165
  this.enabled = true;
166
166
  this.projectId = '';
@@ -180,6 +180,9 @@ class ClsLoggerCore {
180
180
  this.batchTimerDueAt = null;
181
181
  this.initTs = 0;
182
182
  this.startupDelayMs = 0;
183
+ this.useIdleCallback = false;
184
+ this.idleTimeout = 3000;
185
+ this.visibilityCleanup = null;
183
186
  // 参考文档:失败缓存 + 重试
184
187
  this.failedCacheKey = 'cls_failed_logs';
185
188
  this.failedCacheMax = 200;
@@ -205,10 +208,10 @@ class ClsLoggerCore {
205
208
  }
206
209
  init(options) {
207
210
  this.initTs = Date.now();
208
- const topicId = options?.tencentCloud?.topicID ?? options?.topic_id ?? options?.topicID ?? this.topicId ?? null;
211
+ const topicId = options?.tencentCloud?.topicID ?? options?.topic_id ?? options?.topicID ?? this.topicId;
209
212
  const endpoint = options?.tencentCloud?.endpoint ?? options?.endpoint ?? this.endpoint;
210
213
  const retryTimes = options?.tencentCloud?.retry_times ?? options?.retry_times ?? this.retryTimes;
211
- const source = options?.tencentCloud?.source ?? options?.source ?? this.source;
214
+ const source = options?.source ?? this.source;
212
215
  if (!topicId) {
213
216
  // eslint-disable-next-line no-console
214
217
  console.warn('ClsLogger.init 没有传 topicID/topic_id');
@@ -249,6 +252,8 @@ class ClsLoggerCore {
249
252
  this.batchMaxSize = options.batch?.maxSize ?? this.batchMaxSize;
250
253
  this.batchIntervalMs = options.batch?.intervalMs ?? this.batchIntervalMs;
251
254
  this.startupDelayMs = options.batch?.startupDelayMs ?? this.startupDelayMs;
255
+ this.useIdleCallback = options.batch?.useIdleCallback ?? this.useIdleCallback;
256
+ this.idleTimeout = options.batch?.idleTimeout ?? this.idleTimeout;
252
257
  this.failedCacheKey = options.failedCacheKey ?? this.failedCacheKey;
253
258
  this.failedCacheMax = options.failedCacheMax ?? this.failedCacheMax;
254
259
  // 预热(避免首条日志触发 import/初始化开销)
@@ -258,6 +263,8 @@ class ClsLoggerCore {
258
263
  if (this.enabled) {
259
264
  // 启动时尝试发送失败缓存
260
265
  this.flushFailed();
266
+ // 添加页面可见性监听(确保页面关闭时数据不丢失)
267
+ this.setupVisibilityListener();
261
268
  // 初始化后立即启动请求监听
262
269
  this.startRequestMonitor(options.requestMonitor);
263
270
  // 初始化后立即启动错误监控/性能监控
@@ -294,6 +301,89 @@ class ClsLoggerCore {
294
301
  return auto;
295
302
  return undefined;
296
303
  }
304
+ /**
305
+ * 设置页面可见性监听
306
+ * - visibilitychange: 页面隐藏时使用 sendBeacon 发送队列
307
+ * - pagehide: 作为移动端 fallback
308
+ */
309
+ setupVisibilityListener() {
310
+ if (typeof document === 'undefined' || typeof window === 'undefined')
311
+ return;
312
+ // 避免重复监听
313
+ if (this.visibilityCleanup)
314
+ return;
315
+ const handleVisibilityChange = () => {
316
+ if (document.visibilityState === 'hidden') {
317
+ this.flushBatchSync();
318
+ }
319
+ };
320
+ const handlePageHide = () => {
321
+ this.flushBatchSync();
322
+ };
323
+ document.addEventListener('visibilitychange', handleVisibilityChange);
324
+ window.addEventListener('pagehide', handlePageHide);
325
+ this.visibilityCleanup = () => {
326
+ document.removeEventListener('visibilitychange', handleVisibilityChange);
327
+ window.removeEventListener('pagehide', handlePageHide);
328
+ };
329
+ }
330
+ /**
331
+ * 同步发送内存队列(使用 sendBeacon)
332
+ * - 用于页面关闭时确保数据发送
333
+ * - sendBeacon 不可用时降级为缓存到 localStorage
334
+ */
335
+ flushBatchSync() {
336
+ if (this.memoryQueue.length === 0)
337
+ return;
338
+ // 清除定时器
339
+ if (this.batchTimer) {
340
+ try {
341
+ if (this.useIdleCallback && typeof cancelIdleCallback !== 'undefined') {
342
+ cancelIdleCallback(this.batchTimer);
343
+ }
344
+ else {
345
+ clearTimeout(this.batchTimer);
346
+ }
347
+ }
348
+ catch {
349
+ // ignore
350
+ }
351
+ this.batchTimer = null;
352
+ }
353
+ this.batchTimerDueAt = null;
354
+ const logs = [...this.memoryQueue];
355
+ this.memoryQueue = [];
356
+ // 优先使用 sendBeacon(页面关闭时可靠发送)
357
+ if (typeof navigator !== 'undefined' && typeof navigator.sendBeacon === 'function') {
358
+ try {
359
+ const payload = this.buildSendBeaconPayload(logs);
360
+ const blob = new Blob([payload], { type: 'application/json' });
361
+ const url = `${this.endpoint}/structuredlog?topic_id=${this.topicId}`;
362
+ const success = navigator.sendBeacon(url, blob);
363
+ if (!success) {
364
+ // sendBeacon 返回 false 时,降级缓存
365
+ this.cacheFailedReportLogs(logs);
366
+ }
367
+ }
368
+ catch {
369
+ this.cacheFailedReportLogs(logs);
370
+ }
371
+ }
372
+ else {
373
+ // 不支持 sendBeacon,降级缓存到 localStorage
374
+ this.cacheFailedReportLogs(logs);
375
+ }
376
+ }
377
+ /**
378
+ * 构建 sendBeacon 的 payload
379
+ */
380
+ buildSendBeaconPayload(logs) {
381
+ const logList = logs.map((log) => this.buildReportFields(log));
382
+ return JSON.stringify({
383
+ source: this.source,
384
+ logs: logList,
385
+ });
386
+ }
297
387
  startRequestMonitor(requestMonitor) {
298
388
  if (this.requestMonitorStarted)
299
389
  return;
@@ -548,25 +638,55 @@ class ClsLoggerCore {
548
638
  const desiredDelay = Math.max(0, desiredDueAt - now);
549
639
  if (!this.batchTimer) {
550
640
  this.batchTimerDueAt = desiredDueAt;
551
- this.batchTimer = setTimeout(() => {
552
- void this.flushBatch();
553
- }, desiredDelay);
641
+ this.scheduleFlush(desiredDelay);
554
642
  return;
555
643
  }
556
- // 启动合并窗口内:如果当前 timer 会“更早”触发,则延后到窗口结束,尽量减少多次发送
644
+ // 启动合并窗口内:如果当前 timer 会"更早"触发,则延后到窗口结束,尽量减少多次发送
557
645
  if (this.batchTimerDueAt !== null && this.batchTimerDueAt < desiredDueAt) {
558
- try {
559
- clearTimeout(this.batchTimer);
560
- }
561
- catch {
562
- // ignore
563
- }
646
+ this.cancelScheduledFlush();
564
647
  this.batchTimerDueAt = desiredDueAt;
648
+ this.scheduleFlush(desiredDelay);
649
+ }
650
+ }
651
+ /**
652
+ * 调度批量发送
653
+ * - 支持 requestIdleCallback(浏览器空闲时执行)
654
+ * - 降级为 setTimeout
655
+ */
656
+ scheduleFlush(desiredDelay) {
657
+ if (this.useIdleCallback && typeof requestIdleCallback !== 'undefined') {
658
+ // 使用 requestIdleCallback,设置 timeout 保证最终执行
659
+ const idleId = requestIdleCallback(() => {
660
+ void this.flushBatch();
661
+ }, { timeout: Math.max(desiredDelay, this.idleTimeout) });
662
+ // 存储 idleId 以便清理(类型兼容处理)
663
+ this.batchTimer = idleId;
664
+ }
665
+ else {
565
666
  this.batchTimer = setTimeout(() => {
566
667
  void this.flushBatch();
567
668
  }, desiredDelay);
568
669
  }
569
670
  }
671
+ /**
672
+ * 取消已调度的批量发送
673
+ */
674
+ cancelScheduledFlush() {
675
+ if (!this.batchTimer)
676
+ return;
677
+ try {
678
+ if (this.useIdleCallback && typeof cancelIdleCallback !== 'undefined') {
679
+ cancelIdleCallback(this.batchTimer);
680
+ }
681
+ else {
682
+ clearTimeout(this.batchTimer);
683
+ }
684
+ }
685
+ catch {
686
+ // ignore
687
+ }
688
+ this.batchTimer = null;
689
+ }
570
690
  getDesiredBatchFlushDueAt(nowTs) {
571
691
  const start = this.initTs || nowTs;
572
692
  const startupDelay = Number.isFinite(this.startupDelayMs) ? Math.max(0, this.startupDelayMs) : 0;
@@ -577,7 +697,7 @@ class ClsLoggerCore {
577
697
  }
578
698
  return nowTs + this.batchIntervalMs;
579
699
  }
580
- info(message, data = {}) {
700
+ info(message, data = {}, options) {
581
701
  let msg = '';
582
702
  let extra = {};
583
703
  if (message instanceof Error) {
@@ -593,9 +713,18 @@ class ClsLoggerCore {
593
713
  extra = data;
594
714
  }
595
715
  const payload = normalizeFlatFields({ message: msg, ...extra }, 'info');
596
- this.report({ type: 'info', data: payload, timestamp: Date.now() });
716
+ const log = { type: 'info', data: payload, timestamp: Date.now() };
717
+ // info 默认走批量队列,支持 immediate 选项立即发送
718
+ if (options?.immediate) {
719
+ void this.sendReportLogs([log]).catch(() => {
720
+ this.cacheFailedReportLogs([log]);
721
+ });
722
+ }
723
+ else {
724
+ this.report(log);
725
+ }
597
726
  }
598
- warn(message, data = {}) {
727
+ warn(message, data = {}, options) {
599
728
  let msg = '';
600
729
  let extra = {};
601
730
  if (message instanceof Error) {
@@ -611,9 +740,18 @@ class ClsLoggerCore {
611
740
  extra = data;
612
741
  }
613
742
  const payload = normalizeFlatFields({ message: msg, ...extra }, 'warn');
614
- this.report({ type: 'warn', data: payload, timestamp: Date.now() });
743
+ const log = { type: 'warn', data: payload, timestamp: Date.now() };
744
+ // warn 默认走批量队列,支持 immediate 选项立即发送
745
+ if (options?.immediate) {
746
+ void this.sendReportLogs([log]).catch(() => {
747
+ this.cacheFailedReportLogs([log]);
748
+ });
749
+ }
750
+ else {
751
+ this.report(log);
752
+ }
615
753
  }
616
- error(message, data = {}) {
754
+ error(message, data = {}, options) {
617
755
  let msg = '';
618
756
  let extra = {};
619
757
  if (message instanceof Error) {
@@ -629,7 +767,17 @@ class ClsLoggerCore {
629
767
  extra = data;
630
768
  }
631
769
  const payload = normalizeFlatFields({ message: msg, ...extra }, 'error');
632
- this.report({ type: 'error', data: payload, timestamp: Date.now() });
770
+ const log = { type: 'error', data: payload, timestamp: Date.now() };
771
+ // error 默认即时上报,除非显式指定 immediate: false
772
+ const immediate = options?.immediate ?? true;
773
+ if (immediate) {
774
+ void this.sendReportLogs([log]).catch(() => {
775
+ this.cacheFailedReportLogs([log]);
776
+ });
777
+ }
778
+ else {
779
+ this.report(log);
780
+ }
633
781
  }
634
782
  track(trackType, data = {}) {
635
783
  if (!trackType)
@@ -644,10 +792,7 @@ class ClsLoggerCore {
644
792
  * 立即发送内存队列
645
793
  */
646
794
  async flushBatch() {
647
- if (this.batchTimer) {
648
- clearTimeout(this.batchTimer);
649
- this.batchTimer = null;
650
- }
795
+ this.cancelScheduledFlush();
651
796
  this.batchTimerDueAt = null;
652
797
  if (this.memoryQueue.length === 0)
653
798
  return;
package/dist/index.umd.js CHANGED
@@ -160,9 +160,9 @@
160
160
  this.sdkLoaderOverride = null;
161
161
  this.client = null;
162
162
  this.clientPromise = null;
163
- this.topicId = null;
164
- this.endpoint = 'ap-shanghai.cls.tencentcs.com';
165
- this.retryTimes = 10;
163
+ this.topicId = '17475bcd-6315-4b20-859c-e7b087fb3683';
164
+ this.endpoint = 'https://ap-shanghai.cls.tencentcs.com';
165
+ this.retryTimes = 3;
166
166
  this.source = '127.0.0.1';
167
167
  this.enabled = true;
168
168
  this.projectId = '';
@@ -182,6 +182,9 @@
182
182
  this.batchTimerDueAt = null;
183
183
  this.initTs = 0;
184
184
  this.startupDelayMs = 0;
185
+ this.useIdleCallback = false;
186
+ this.idleTimeout = 3000;
187
+ this.visibilityCleanup = null;
185
188
  // 参考文档:失败缓存 + 重试
186
189
  this.failedCacheKey = 'cls_failed_logs';
187
190
  this.failedCacheMax = 200;
@@ -207,10 +210,10 @@
207
210
  }
208
211
  init(options) {
209
212
  this.initTs = Date.now();
210
- const topicId = options?.tencentCloud?.topicID ?? options?.topic_id ?? options?.topicID ?? this.topicId ?? null;
213
+ const topicId = options?.tencentCloud?.topicID ?? options?.topic_id ?? options?.topicID ?? this.topicId;
211
214
  const endpoint = options?.tencentCloud?.endpoint ?? options?.endpoint ?? this.endpoint;
212
215
  const retryTimes = options?.tencentCloud?.retry_times ?? options?.retry_times ?? this.retryTimes;
213
- const source = options?.tencentCloud?.source ?? options?.source ?? this.source;
216
+ const source = options?.source ?? this.source;
214
217
  if (!topicId) {
215
218
  // eslint-disable-next-line no-console
216
219
  console.warn('ClsLogger.init 没有传 topicID/topic_id');
@@ -251,6 +254,8 @@
251
254
  this.batchMaxSize = options.batch?.maxSize ?? this.batchMaxSize;
252
255
  this.batchIntervalMs = options.batch?.intervalMs ?? this.batchIntervalMs;
253
256
  this.startupDelayMs = options.batch?.startupDelayMs ?? this.startupDelayMs;
257
+ this.useIdleCallback = options.batch?.useIdleCallback ?? this.useIdleCallback;
258
+ this.idleTimeout = options.batch?.idleTimeout ?? this.idleTimeout;
254
259
  this.failedCacheKey = options.failedCacheKey ?? this.failedCacheKey;
255
260
  this.failedCacheMax = options.failedCacheMax ?? this.failedCacheMax;
256
261
  // 预热(避免首条日志触发 import/初始化开销)
@@ -260,6 +265,8 @@
260
265
  if (this.enabled) {
261
266
  // 启动时尝试发送失败缓存
262
267
  this.flushFailed();
268
+ // 添加页面可见性监听(确保页面关闭时数据不丢失)
269
+ this.setupVisibilityListener();
263
270
  // 初始化后立即启动请求监听
264
271
  this.startRequestMonitor(options.requestMonitor);
265
272
  // 初始化后立即启动错误监控/性能监控
@@ -296,6 +303,89 @@
296
303
  return auto;
297
304
  return undefined;
298
305
  }
306
+ /**
307
+ * 设置页面可见性监听
308
+ * - visibilitychange: 页面隐藏时使用 sendBeacon 发送队列
309
+ * - pagehide: 作为移动端 fallback
310
+ */
311
+ setupVisibilityListener() {
312
+ if (typeof document === 'undefined' || typeof window === 'undefined')
313
+ return;
314
+ // 避免重复监听
315
+ if (this.visibilityCleanup)
316
+ return;
317
+ const handleVisibilityChange = () => {
318
+ if (document.visibilityState === 'hidden') {
319
+ this.flushBatchSync();
320
+ }
321
+ };
322
+ const handlePageHide = () => {
323
+ this.flushBatchSync();
324
+ };
325
+ document.addEventListener('visibilitychange', handleVisibilityChange);
326
+ window.addEventListener('pagehide', handlePageHide);
327
+ this.visibilityCleanup = () => {
328
+ document.removeEventListener('visibilitychange', handleVisibilityChange);
329
+ window.removeEventListener('pagehide', handlePageHide);
330
+ };
331
+ }
332
+ /**
333
+ * 同步发送内存队列(使用 sendBeacon)
334
+ * - 用于页面关闭时确保数据发送
335
+ * - sendBeacon 不可用时降级为缓存到 localStorage
336
+ */
337
+ flushBatchSync() {
338
+ if (this.memoryQueue.length === 0)
339
+ return;
340
+ // 清除定时器
341
+ if (this.batchTimer) {
342
+ try {
343
+ if (this.useIdleCallback && typeof cancelIdleCallback !== 'undefined') {
344
+ cancelIdleCallback(this.batchTimer);
345
+ }
346
+ else {
347
+ clearTimeout(this.batchTimer);
348
+ }
349
+ }
350
+ catch {
351
+ // ignore
352
+ }
353
+ this.batchTimer = null;
354
+ }
355
+ this.batchTimerDueAt = null;
356
+ const logs = [...this.memoryQueue];
357
+ this.memoryQueue = [];
358
+ // 优先使用 sendBeacon(页面关闭时可靠发送)
359
+ if (typeof navigator !== 'undefined' && typeof navigator.sendBeacon === 'function') {
360
+ try {
361
+ const payload = this.buildSendBeaconPayload(logs);
362
+ const blob = new Blob([payload], { type: 'application/json' });
363
+ const url = `${this.endpoint}/structuredlog?topic_id=${this.topicId}`;
364
+ const success = navigator.sendBeacon(url, blob);
365
+ if (!success) {
366
+ // sendBeacon 返回 false 时,降级缓存
367
+ this.cacheFailedReportLogs(logs);
368
+ }
369
+ }
370
+ catch {
371
+ this.cacheFailedReportLogs(logs);
372
+ }
373
+ }
374
+ else {
375
+ // 不支持 sendBeacon,降级缓存到 localStorage
376
+ this.cacheFailedReportLogs(logs);
377
+ }
378
+ }
379
+ /**
380
+ * 构建 sendBeacon 的 payload
381
+ */
382
+ buildSendBeaconPayload(logs) {
383
+ const logList = logs.map((log) => this.buildReportFields(log));
384
+ return JSON.stringify({
385
+ source: this.source,
386
+ logs: logList,
387
+ });
388
+ }
299
389
  startRequestMonitor(requestMonitor) {
300
390
  if (this.requestMonitorStarted)
301
391
  return;
@@ -550,25 +640,55 @@
550
640
  const desiredDelay = Math.max(0, desiredDueAt - now);
551
641
  if (!this.batchTimer) {
552
642
  this.batchTimerDueAt = desiredDueAt;
553
- this.batchTimer = setTimeout(() => {
554
- void this.flushBatch();
555
- }, desiredDelay);
643
+ this.scheduleFlush(desiredDelay);
556
644
  return;
557
645
  }
558
- // 启动合并窗口内:如果当前 timer 会“更早”触发,则延后到窗口结束,尽量减少多次发送
646
+ // 启动合并窗口内:如果当前 timer 会"更早"触发,则延后到窗口结束,尽量减少多次发送
559
647
  if (this.batchTimerDueAt !== null && this.batchTimerDueAt < desiredDueAt) {
560
- try {
561
- clearTimeout(this.batchTimer);
562
- }
563
- catch {
564
- // ignore
565
- }
648
+ this.cancelScheduledFlush();
566
649
  this.batchTimerDueAt = desiredDueAt;
650
+ this.scheduleFlush(desiredDelay);
651
+ }
652
+ }
653
+ /**
654
+ * 调度批量发送
655
+ * - 支持 requestIdleCallback(浏览器空闲时执行)
656
+ * - 降级为 setTimeout
657
+ */
658
+ scheduleFlush(desiredDelay) {
659
+ if (this.useIdleCallback && typeof requestIdleCallback !== 'undefined') {
660
+ // 使用 requestIdleCallback,设置 timeout 保证最终执行
661
+ const idleId = requestIdleCallback(() => {
662
+ void this.flushBatch();
663
+ }, { timeout: Math.max(desiredDelay, this.idleTimeout) });
664
+ // 存储 idleId 以便清理(类型兼容处理)
665
+ this.batchTimer = idleId;
666
+ }
667
+ else {
567
668
  this.batchTimer = setTimeout(() => {
568
669
  void this.flushBatch();
569
670
  }, desiredDelay);
570
671
  }
571
672
  }
673
+ /**
674
+ * 取消已调度的批量发送
675
+ */
676
+ cancelScheduledFlush() {
677
+ if (!this.batchTimer)
678
+ return;
679
+ try {
680
+ if (this.useIdleCallback && typeof cancelIdleCallback !== 'undefined') {
681
+ cancelIdleCallback(this.batchTimer);
682
+ }
683
+ else {
684
+ clearTimeout(this.batchTimer);
685
+ }
686
+ }
687
+ catch {
688
+ // ignore
689
+ }
690
+ this.batchTimer = null;
691
+ }
572
692
  getDesiredBatchFlushDueAt(nowTs) {
573
693
  const start = this.initTs || nowTs;
574
694
  const startupDelay = Number.isFinite(this.startupDelayMs) ? Math.max(0, this.startupDelayMs) : 0;
@@ -579,7 +699,7 @@
579
699
  }
580
700
  return nowTs + this.batchIntervalMs;
581
701
  }
582
- info(message, data = {}) {
702
+ info(message, data = {}, options) {
583
703
  let msg = '';
584
704
  let extra = {};
585
705
  if (message instanceof Error) {
@@ -595,9 +715,18 @@
595
715
  extra = data;
596
716
  }
597
717
  const payload = normalizeFlatFields({ message: msg, ...extra }, 'info');
598
- this.report({ type: 'info', data: payload, timestamp: Date.now() });
718
+ const log = { type: 'info', data: payload, timestamp: Date.now() };
719
+ // info 默认走批量队列,支持 immediate 选项立即发送
720
+ if (options?.immediate) {
721
+ void this.sendReportLogs([log]).catch(() => {
722
+ this.cacheFailedReportLogs([log]);
723
+ });
724
+ }
725
+ else {
726
+ this.report(log);
727
+ }
599
728
  }
600
- warn(message, data = {}) {
729
+ warn(message, data = {}, options) {
601
730
  let msg = '';
602
731
  let extra = {};
603
732
  if (message instanceof Error) {
@@ -613,9 +742,18 @@
613
742
  extra = data;
614
743
  }
615
744
  const payload = normalizeFlatFields({ message: msg, ...extra }, 'warn');
616
- this.report({ type: 'warn', data: payload, timestamp: Date.now() });
745
+ const log = { type: 'warn', data: payload, timestamp: Date.now() };
746
+ // warn 默认走批量队列,支持 immediate 选项立即发送
747
+ if (options?.immediate) {
748
+ void this.sendReportLogs([log]).catch(() => {
749
+ this.cacheFailedReportLogs([log]);
750
+ });
751
+ }
752
+ else {
753
+ this.report(log);
754
+ }
617
755
  }
618
- error(message, data = {}) {
756
+ error(message, data = {}, options) {
619
757
  let msg = '';
620
758
  let extra = {};
621
759
  if (message instanceof Error) {
@@ -631,7 +769,17 @@
631
769
  extra = data;
632
770
  }
633
771
  const payload = normalizeFlatFields({ message: msg, ...extra }, 'error');
634
- this.report({ type: 'error', data: payload, timestamp: Date.now() });
772
+ const log = { type: 'error', data: payload, timestamp: Date.now() };
773
+ // error 默认即时上报,除非显式指定 immediate: false
774
+ const immediate = options?.immediate ?? true;
775
+ if (immediate) {
776
+ void this.sendReportLogs([log]).catch(() => {
777
+ this.cacheFailedReportLogs([log]);
778
+ });
779
+ }
780
+ else {
781
+ this.report(log);
782
+ }
635
783
  }
636
784
  track(trackType, data = {}) {
637
785
  if (!trackType)
@@ -646,10 +794,7 @@
646
794
  * 立即发送内存队列
647
795
  */
648
796
  async flushBatch() {
649
- if (this.batchTimer) {
650
- clearTimeout(this.batchTimer);
651
- this.batchTimer = null;
652
- }
797
+ this.cancelScheduledFlush();
653
798
  this.batchTimerDueAt = null;
654
799
  if (this.memoryQueue.length === 0)
655
800
  return;