@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/README.md +18 -12
- package/dist/ClsLoggerCore.d.ts +33 -4
- package/dist/ClsLoggerCore.d.ts.map +1 -1
- package/dist/index.esm.js +170 -25
- package/dist/index.js +170 -25
- package/dist/index.umd.js +170 -25
- package/dist/mini.esm.js +170 -25
- package/dist/mini.js +170 -25
- package/dist/types.d.ts +32 -6
- package/dist/types.d.ts.map +1 -1
- package/dist/web.esm.js +170 -25
- package/dist/web.js +170 -25
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -26,10 +26,12 @@ import clsLogger from '@be-link/cls-logger';
|
|
|
26
26
|
clsLogger.init({
|
|
27
27
|
projectId: 'my-project',
|
|
28
28
|
appId: 'my-app-id',
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
// 默认配置可不传
|
|
30
|
+
// tencentCloud: {
|
|
31
|
+
// endpoint: 'https://ap-shanghai.cls.tencentcs.com',
|
|
32
|
+
// topicID: '17475bcd-6315-4b20-859c-e7b087fb3683',
|
|
33
|
+
// },
|
|
34
|
+
source: 'my-source',
|
|
33
35
|
});
|
|
34
36
|
```
|
|
35
37
|
|
|
@@ -45,10 +47,8 @@ import { clsLogger } from '@be-link/cls-logger/web';
|
|
|
45
47
|
|
|
46
48
|
clsLogger.init({
|
|
47
49
|
projectId: 'my-web-project',
|
|
48
|
-
tencentCloud: {
|
|
49
|
-
|
|
50
|
-
topicID: 'your-web-topic-id',
|
|
51
|
-
},
|
|
50
|
+
// tencentCloud: { ... }, // 可选
|
|
51
|
+
source: 'web-source',
|
|
52
52
|
});
|
|
53
53
|
```
|
|
54
54
|
|
|
@@ -61,10 +61,8 @@ import { clsLogger } from '@be-link/cls-logger/mini';
|
|
|
61
61
|
clsLogger.init({
|
|
62
62
|
projectId: 'my-mini-project',
|
|
63
63
|
appId: 'wx123456789', // 小程序建议传入 appId
|
|
64
|
-
tencentCloud: {
|
|
65
|
-
|
|
66
|
-
topicID: 'your-mini-topic-id',
|
|
67
|
-
},
|
|
64
|
+
// tencentCloud: { ... }, // 可选
|
|
65
|
+
source: 'mini-source',
|
|
68
66
|
});
|
|
69
67
|
```
|
|
70
68
|
|
|
@@ -134,7 +132,15 @@ clsLogger.stat({
|
|
|
134
132
|
- **UV ID**: 自动生成并持久化。小程序环境下优先尝试使用 `openid`。
|
|
135
133
|
- **点击追踪**: 自动监听点击事件。
|
|
136
134
|
- **Web**: 识别带 `data-track-id` 的元素。
|
|
135
|
+
```html
|
|
136
|
+
<!-- HTML -->
|
|
137
|
+
<button data-track-id="submit_btn">提交</button>
|
|
138
|
+
```
|
|
137
139
|
- **小程序**: 识别带 `data-track-id` 的组件(需在 `dataset` 中)。
|
|
140
|
+
```html
|
|
141
|
+
<!-- WXML -->
|
|
142
|
+
<button data-track-id="submit_btn" bindtap="handleClick">提交</button>
|
|
143
|
+
```
|
|
138
144
|
|
|
139
145
|
## 高级用法
|
|
140
146
|
|
package/dist/ClsLoggerCore.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ClsLoggerInitOptions, ErrorMonitorOptions, EnvType, FlatFields, PerformanceMonitorOptions, PutOptions, QueueItem, ReportLog, RequestMonitorOptions, BehaviorMonitorOptions, DeviceInfoOptions } from './types';
|
|
1
|
+
import type { ClsLoggerInitOptions, ErrorMonitorOptions, EnvType, FlatFields, PerformanceMonitorOptions, PutOptions, QueueItem, ReportLog, ReportOptions, RequestMonitorOptions, BehaviorMonitorOptions, DeviceInfoOptions } from './types';
|
|
2
2
|
import type { ClsSdkModule } from './clsSdkTypes';
|
|
3
3
|
/**
|
|
4
4
|
* CLS Logger 核心基类
|
|
@@ -38,6 +38,9 @@ export declare abstract class ClsLoggerCore {
|
|
|
38
38
|
protected batchTimerDueAt: number | null;
|
|
39
39
|
protected initTs: number;
|
|
40
40
|
protected startupDelayMs: number;
|
|
41
|
+
protected useIdleCallback: boolean;
|
|
42
|
+
protected idleTimeout: number;
|
|
43
|
+
protected visibilityCleanup: (() => void) | null;
|
|
41
44
|
protected failedCacheKey: string;
|
|
42
45
|
protected failedCacheMax: number;
|
|
43
46
|
protected requestMonitorStarted: boolean;
|
|
@@ -77,6 +80,22 @@ export declare abstract class ClsLoggerCore {
|
|
|
77
80
|
protected detectEnvType(): EnvType;
|
|
78
81
|
init(options: ClsLoggerInitOptions): void;
|
|
79
82
|
private getBaseFields;
|
|
83
|
+
/**
|
|
84
|
+
* 设置页面可见性监听
|
|
85
|
+
* - visibilitychange: 页面隐藏时使用 sendBeacon 发送队列
|
|
86
|
+
* - pagehide: 作为移动端 fallback
|
|
87
|
+
*/
|
|
88
|
+
private setupVisibilityListener;
|
|
89
|
+
/**
|
|
90
|
+
* 同步发送内存队列(使用 sendBeacon)
|
|
91
|
+
* - 用于页面关闭时确保数据发送
|
|
92
|
+
* - sendBeacon 不可用时降级为缓存到 localStorage
|
|
93
|
+
*/
|
|
94
|
+
private flushBatchSync;
|
|
95
|
+
/**
|
|
96
|
+
* 构建 sendBeacon 的 payload
|
|
97
|
+
*/
|
|
98
|
+
private buildSendBeaconPayload;
|
|
80
99
|
private startRequestMonitor;
|
|
81
100
|
private startErrorMonitor;
|
|
82
101
|
private startPerformanceMonitor;
|
|
@@ -121,10 +140,20 @@ export declare abstract class ClsLoggerCore {
|
|
|
121
140
|
* 参考《一、概述》:统一上报入口(内存队列 + 批量发送)
|
|
122
141
|
*/
|
|
123
142
|
report(log: ReportLog): void;
|
|
143
|
+
/**
|
|
144
|
+
* 调度批量发送
|
|
145
|
+
* - 支持 requestIdleCallback(浏览器空闲时执行)
|
|
146
|
+
* - 降级为 setTimeout
|
|
147
|
+
*/
|
|
148
|
+
private scheduleFlush;
|
|
149
|
+
/**
|
|
150
|
+
* 取消已调度的批量发送
|
|
151
|
+
*/
|
|
152
|
+
private cancelScheduledFlush;
|
|
124
153
|
private getDesiredBatchFlushDueAt;
|
|
125
|
-
info(message: string | Error, data?: FlatFields): void;
|
|
126
|
-
warn(message: string | Error, data?: FlatFields): void;
|
|
127
|
-
error(message: string | Error, data?: FlatFields): void;
|
|
154
|
+
info(message: string | Error, data?: FlatFields, options?: ReportOptions): void;
|
|
155
|
+
warn(message: string | Error, data?: FlatFields, options?: ReportOptions): void;
|
|
156
|
+
error(message: string | Error, data?: FlatFields, options?: ReportOptions): void;
|
|
128
157
|
track(trackType: string, data?: FlatFields): void;
|
|
129
158
|
/**
|
|
130
159
|
* 立即发送内存队列
|
|
@@ -1 +1 @@
|
|
|
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,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,
|
|
1
|
+
{"version":3,"file":"ClsLoggerCore.d.ts","sourceRoot":"","sources":["../src/ClsLoggerCore.ts"],"names":[],"mappings":"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"}
|
package/dist/index.esm.js
CHANGED
|
@@ -154,9 +154,9 @@ class ClsLoggerCore {
|
|
|
154
154
|
this.sdkLoaderOverride = null;
|
|
155
155
|
this.client = null;
|
|
156
156
|
this.clientPromise = null;
|
|
157
|
-
this.topicId =
|
|
158
|
-
this.endpoint = 'ap-shanghai.cls.tencentcs.com';
|
|
159
|
-
this.retryTimes =
|
|
157
|
+
this.topicId = '17475bcd-6315-4b20-859c-e7b087fb3683';
|
|
158
|
+
this.endpoint = 'https://ap-shanghai.cls.tencentcs.com';
|
|
159
|
+
this.retryTimes = 3;
|
|
160
160
|
this.source = '127.0.0.1';
|
|
161
161
|
this.enabled = true;
|
|
162
162
|
this.projectId = '';
|
|
@@ -176,6 +176,9 @@ class ClsLoggerCore {
|
|
|
176
176
|
this.batchTimerDueAt = null;
|
|
177
177
|
this.initTs = 0;
|
|
178
178
|
this.startupDelayMs = 0;
|
|
179
|
+
this.useIdleCallback = false;
|
|
180
|
+
this.idleTimeout = 3000;
|
|
181
|
+
this.visibilityCleanup = null;
|
|
179
182
|
// 参考文档:失败缓存 + 重试
|
|
180
183
|
this.failedCacheKey = 'cls_failed_logs';
|
|
181
184
|
this.failedCacheMax = 200;
|
|
@@ -201,10 +204,10 @@ class ClsLoggerCore {
|
|
|
201
204
|
}
|
|
202
205
|
init(options) {
|
|
203
206
|
this.initTs = Date.now();
|
|
204
|
-
const topicId = options?.tencentCloud?.topicID ?? options?.topic_id ?? options?.topicID ?? this.topicId
|
|
207
|
+
const topicId = options?.tencentCloud?.topicID ?? options?.topic_id ?? options?.topicID ?? this.topicId;
|
|
205
208
|
const endpoint = options?.tencentCloud?.endpoint ?? options?.endpoint ?? this.endpoint;
|
|
206
209
|
const retryTimes = options?.tencentCloud?.retry_times ?? options?.retry_times ?? this.retryTimes;
|
|
207
|
-
const source = options?.
|
|
210
|
+
const source = options?.source ?? this.source;
|
|
208
211
|
if (!topicId) {
|
|
209
212
|
// eslint-disable-next-line no-console
|
|
210
213
|
console.warn('ClsLogger.init 没有传 topicID/topic_id');
|
|
@@ -245,6 +248,8 @@ class ClsLoggerCore {
|
|
|
245
248
|
this.batchMaxSize = options.batch?.maxSize ?? this.batchMaxSize;
|
|
246
249
|
this.batchIntervalMs = options.batch?.intervalMs ?? this.batchIntervalMs;
|
|
247
250
|
this.startupDelayMs = options.batch?.startupDelayMs ?? this.startupDelayMs;
|
|
251
|
+
this.useIdleCallback = options.batch?.useIdleCallback ?? this.useIdleCallback;
|
|
252
|
+
this.idleTimeout = options.batch?.idleTimeout ?? this.idleTimeout;
|
|
248
253
|
this.failedCacheKey = options.failedCacheKey ?? this.failedCacheKey;
|
|
249
254
|
this.failedCacheMax = options.failedCacheMax ?? this.failedCacheMax;
|
|
250
255
|
// 预热(避免首条日志触发 import/初始化开销)
|
|
@@ -254,6 +259,8 @@ class ClsLoggerCore {
|
|
|
254
259
|
if (this.enabled) {
|
|
255
260
|
// 启动时尝试发送失败缓存
|
|
256
261
|
this.flushFailed();
|
|
262
|
+
// 添加页面可见性监听(确保页面关闭时数据不丢失)
|
|
263
|
+
this.setupVisibilityListener();
|
|
257
264
|
// 初始化后立即启动请求监听
|
|
258
265
|
this.startRequestMonitor(options.requestMonitor);
|
|
259
266
|
// 初始化后立即启动错误监控/性能监控
|
|
@@ -290,6 +297,89 @@ class ClsLoggerCore {
|
|
|
290
297
|
return auto;
|
|
291
298
|
return undefined;
|
|
292
299
|
}
|
|
300
|
+
/**
|
|
301
|
+
* 设置页面可见性监听
|
|
302
|
+
* - visibilitychange: 页面隐藏时使用 sendBeacon 发送队列
|
|
303
|
+
* - pagehide: 作为移动端 fallback
|
|
304
|
+
*/
|
|
305
|
+
setupVisibilityListener() {
|
|
306
|
+
if (typeof document === 'undefined' || typeof window === 'undefined')
|
|
307
|
+
return;
|
|
308
|
+
// 避免重复监听
|
|
309
|
+
if (this.visibilityCleanup)
|
|
310
|
+
return;
|
|
311
|
+
const handleVisibilityChange = () => {
|
|
312
|
+
if (document.visibilityState === 'hidden') {
|
|
313
|
+
this.flushBatchSync();
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
const handlePageHide = () => {
|
|
317
|
+
this.flushBatchSync();
|
|
318
|
+
};
|
|
319
|
+
document.addEventListener('visibilitychange', handleVisibilityChange);
|
|
320
|
+
window.addEventListener('pagehide', handlePageHide);
|
|
321
|
+
this.visibilityCleanup = () => {
|
|
322
|
+
document.removeEventListener('visibilitychange', handleVisibilityChange);
|
|
323
|
+
window.removeEventListener('pagehide', handlePageHide);
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* 同步发送内存队列(使用 sendBeacon)
|
|
328
|
+
* - 用于页面关闭时确保数据发送
|
|
329
|
+
* - sendBeacon 不可用时降级为缓存到 localStorage
|
|
330
|
+
*/
|
|
331
|
+
flushBatchSync() {
|
|
332
|
+
if (this.memoryQueue.length === 0)
|
|
333
|
+
return;
|
|
334
|
+
// 清除定时器
|
|
335
|
+
if (this.batchTimer) {
|
|
336
|
+
try {
|
|
337
|
+
if (this.useIdleCallback && typeof cancelIdleCallback !== 'undefined') {
|
|
338
|
+
cancelIdleCallback(this.batchTimer);
|
|
339
|
+
}
|
|
340
|
+
else {
|
|
341
|
+
clearTimeout(this.batchTimer);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
catch {
|
|
345
|
+
// ignore
|
|
346
|
+
}
|
|
347
|
+
this.batchTimer = null;
|
|
348
|
+
}
|
|
349
|
+
this.batchTimerDueAt = null;
|
|
350
|
+
const logs = [...this.memoryQueue];
|
|
351
|
+
this.memoryQueue = [];
|
|
352
|
+
// 优先使用 sendBeacon(页面关闭时可靠发送)
|
|
353
|
+
if (typeof navigator !== 'undefined' && typeof navigator.sendBeacon === 'function') {
|
|
354
|
+
try {
|
|
355
|
+
const payload = this.buildSendBeaconPayload(logs);
|
|
356
|
+
const blob = new Blob([payload], { type: 'application/json' });
|
|
357
|
+
const url = `${this.endpoint}/structuredlog?topic_id=${this.topicId}`;
|
|
358
|
+
const success = navigator.sendBeacon(url, blob);
|
|
359
|
+
if (!success) {
|
|
360
|
+
// sendBeacon 返回 false 时,降级缓存
|
|
361
|
+
this.cacheFailedReportLogs(logs);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
catch {
|
|
365
|
+
this.cacheFailedReportLogs(logs);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
else {
|
|
369
|
+
// 不支持 sendBeacon,降级缓存到 localStorage
|
|
370
|
+
this.cacheFailedReportLogs(logs);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* 构建 sendBeacon 的 payload
|
|
375
|
+
*/
|
|
376
|
+
buildSendBeaconPayload(logs) {
|
|
377
|
+
const logList = logs.map((log) => this.buildReportFields(log));
|
|
378
|
+
return JSON.stringify({
|
|
379
|
+
source: this.source,
|
|
380
|
+
logs: logList,
|
|
381
|
+
});
|
|
382
|
+
}
|
|
293
383
|
startRequestMonitor(requestMonitor) {
|
|
294
384
|
if (this.requestMonitorStarted)
|
|
295
385
|
return;
|
|
@@ -544,25 +634,55 @@ class ClsLoggerCore {
|
|
|
544
634
|
const desiredDelay = Math.max(0, desiredDueAt - now);
|
|
545
635
|
if (!this.batchTimer) {
|
|
546
636
|
this.batchTimerDueAt = desiredDueAt;
|
|
547
|
-
this.
|
|
548
|
-
void this.flushBatch();
|
|
549
|
-
}, desiredDelay);
|
|
637
|
+
this.scheduleFlush(desiredDelay);
|
|
550
638
|
return;
|
|
551
639
|
}
|
|
552
|
-
// 启动合并窗口内:如果当前 timer
|
|
640
|
+
// 启动合并窗口内:如果当前 timer 会"更早"触发,则延后到窗口结束,尽量减少多次发送
|
|
553
641
|
if (this.batchTimerDueAt !== null && this.batchTimerDueAt < desiredDueAt) {
|
|
554
|
-
|
|
555
|
-
clearTimeout(this.batchTimer);
|
|
556
|
-
}
|
|
557
|
-
catch {
|
|
558
|
-
// ignore
|
|
559
|
-
}
|
|
642
|
+
this.cancelScheduledFlush();
|
|
560
643
|
this.batchTimerDueAt = desiredDueAt;
|
|
644
|
+
this.scheduleFlush(desiredDelay);
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* 调度批量发送
|
|
649
|
+
* - 支持 requestIdleCallback(浏览器空闲时执行)
|
|
650
|
+
* - 降级为 setTimeout
|
|
651
|
+
*/
|
|
652
|
+
scheduleFlush(desiredDelay) {
|
|
653
|
+
if (this.useIdleCallback && typeof requestIdleCallback !== 'undefined') {
|
|
654
|
+
// 使用 requestIdleCallback,设置 timeout 保证最终执行
|
|
655
|
+
const idleId = requestIdleCallback(() => {
|
|
656
|
+
void this.flushBatch();
|
|
657
|
+
}, { timeout: Math.max(desiredDelay, this.idleTimeout) });
|
|
658
|
+
// 存储 idleId 以便清理(类型兼容处理)
|
|
659
|
+
this.batchTimer = idleId;
|
|
660
|
+
}
|
|
661
|
+
else {
|
|
561
662
|
this.batchTimer = setTimeout(() => {
|
|
562
663
|
void this.flushBatch();
|
|
563
664
|
}, desiredDelay);
|
|
564
665
|
}
|
|
565
666
|
}
|
|
667
|
+
/**
|
|
668
|
+
* 取消已调度的批量发送
|
|
669
|
+
*/
|
|
670
|
+
cancelScheduledFlush() {
|
|
671
|
+
if (!this.batchTimer)
|
|
672
|
+
return;
|
|
673
|
+
try {
|
|
674
|
+
if (this.useIdleCallback && typeof cancelIdleCallback !== 'undefined') {
|
|
675
|
+
cancelIdleCallback(this.batchTimer);
|
|
676
|
+
}
|
|
677
|
+
else {
|
|
678
|
+
clearTimeout(this.batchTimer);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
catch {
|
|
682
|
+
// ignore
|
|
683
|
+
}
|
|
684
|
+
this.batchTimer = null;
|
|
685
|
+
}
|
|
566
686
|
getDesiredBatchFlushDueAt(nowTs) {
|
|
567
687
|
const start = this.initTs || nowTs;
|
|
568
688
|
const startupDelay = Number.isFinite(this.startupDelayMs) ? Math.max(0, this.startupDelayMs) : 0;
|
|
@@ -573,7 +693,7 @@ class ClsLoggerCore {
|
|
|
573
693
|
}
|
|
574
694
|
return nowTs + this.batchIntervalMs;
|
|
575
695
|
}
|
|
576
|
-
info(message, data = {}) {
|
|
696
|
+
info(message, data = {}, options) {
|
|
577
697
|
let msg = '';
|
|
578
698
|
let extra = {};
|
|
579
699
|
if (message instanceof Error) {
|
|
@@ -589,9 +709,18 @@ class ClsLoggerCore {
|
|
|
589
709
|
extra = data;
|
|
590
710
|
}
|
|
591
711
|
const payload = normalizeFlatFields({ message: msg, ...extra }, 'info');
|
|
592
|
-
|
|
712
|
+
const log = { type: 'info', data: payload, timestamp: Date.now() };
|
|
713
|
+
// info 默认走批量队列,支持 immediate 选项立即发送
|
|
714
|
+
if (options?.immediate) {
|
|
715
|
+
void this.sendReportLogs([log]).catch(() => {
|
|
716
|
+
this.cacheFailedReportLogs([log]);
|
|
717
|
+
});
|
|
718
|
+
}
|
|
719
|
+
else {
|
|
720
|
+
this.report(log);
|
|
721
|
+
}
|
|
593
722
|
}
|
|
594
|
-
warn(message, data = {}) {
|
|
723
|
+
warn(message, data = {}, options) {
|
|
595
724
|
let msg = '';
|
|
596
725
|
let extra = {};
|
|
597
726
|
if (message instanceof Error) {
|
|
@@ -607,9 +736,18 @@ class ClsLoggerCore {
|
|
|
607
736
|
extra = data;
|
|
608
737
|
}
|
|
609
738
|
const payload = normalizeFlatFields({ message: msg, ...extra }, 'warn');
|
|
610
|
-
|
|
739
|
+
const log = { type: 'warn', data: payload, timestamp: Date.now() };
|
|
740
|
+
// warn 默认走批量队列,支持 immediate 选项立即发送
|
|
741
|
+
if (options?.immediate) {
|
|
742
|
+
void this.sendReportLogs([log]).catch(() => {
|
|
743
|
+
this.cacheFailedReportLogs([log]);
|
|
744
|
+
});
|
|
745
|
+
}
|
|
746
|
+
else {
|
|
747
|
+
this.report(log);
|
|
748
|
+
}
|
|
611
749
|
}
|
|
612
|
-
error(message, data = {}) {
|
|
750
|
+
error(message, data = {}, options) {
|
|
613
751
|
let msg = '';
|
|
614
752
|
let extra = {};
|
|
615
753
|
if (message instanceof Error) {
|
|
@@ -625,7 +763,17 @@ class ClsLoggerCore {
|
|
|
625
763
|
extra = data;
|
|
626
764
|
}
|
|
627
765
|
const payload = normalizeFlatFields({ message: msg, ...extra }, 'error');
|
|
628
|
-
|
|
766
|
+
const log = { type: 'error', data: payload, timestamp: Date.now() };
|
|
767
|
+
// error 默认即时上报,除非显式指定 immediate: false
|
|
768
|
+
const immediate = options?.immediate ?? true;
|
|
769
|
+
if (immediate) {
|
|
770
|
+
void this.sendReportLogs([log]).catch(() => {
|
|
771
|
+
this.cacheFailedReportLogs([log]);
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
else {
|
|
775
|
+
this.report(log);
|
|
776
|
+
}
|
|
629
777
|
}
|
|
630
778
|
track(trackType, data = {}) {
|
|
631
779
|
if (!trackType)
|
|
@@ -640,10 +788,7 @@ class ClsLoggerCore {
|
|
|
640
788
|
* 立即发送内存队列
|
|
641
789
|
*/
|
|
642
790
|
async flushBatch() {
|
|
643
|
-
|
|
644
|
-
clearTimeout(this.batchTimer);
|
|
645
|
-
this.batchTimer = null;
|
|
646
|
-
}
|
|
791
|
+
this.cancelScheduledFlush();
|
|
647
792
|
this.batchTimerDueAt = null;
|
|
648
793
|
if (this.memoryQueue.length === 0)
|
|
649
794
|
return;
|