@be-link/cls-logger 1.0.1-beta.0 → 1.0.1-beta.1
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 +35 -0
- package/dist/ClsLogger.d.ts +4 -0
- package/dist/ClsLogger.d.ts.map +1 -1
- package/dist/index.esm.js +79 -5
- package/dist/index.js +79 -5
- package/dist/index.umd.js +79 -5
- package/dist/requestMonitor.d.ts.map +1 -1
- package/dist/types.d.ts +7 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -28,3 +28,38 @@ clsLogger.putJson({ event: 'click', x: 1, y: 2 });
|
|
|
28
28
|
// 入队(写入 localStorage,默认 15 条自动 flush)
|
|
29
29
|
clsLogger.enqueue({ event: 'page_view', page: '/home' });
|
|
30
30
|
```
|
|
31
|
+
|
|
32
|
+
## 请求监控(requestMonitor)
|
|
33
|
+
|
|
34
|
+
初始化后默认会开启 HTTP 请求监控(Web/H5:`fetch`/`XMLHttpRequest`;小程序:`wx.request`/`cloud.callFunction`),并把请求信息通过 `track('http', payload)` 上报。
|
|
35
|
+
|
|
36
|
+
- **关闭**:`clsLogger.init({ requestMonitor: false })`
|
|
37
|
+
- **忽略上报请求避免递归**:默认会忽略 CLS 上报域名(会把 `endpoint` 自动加入 `ignoreUrls`),并在内部增加了“上报发送”保护,避免上报请求在跨域失败时出现递归上报/死循环。
|
|
38
|
+
- **自定义忽略**:`clsLogger.init({ requestMonitor: { ignoreUrls: ['example.com', /\\/internal\\//] } })`
|
|
39
|
+
|
|
40
|
+
## 性能监控(performanceMonitor)
|
|
41
|
+
|
|
42
|
+
初始化后默认会开启性能监控,并通过 `track('perf', payload)` 上报,包含:
|
|
43
|
+
|
|
44
|
+
- **Web Vitals**:FCP/LCP/CLS/FID
|
|
45
|
+
- **Navigation Timing**:TTFB 等
|
|
46
|
+
- **Resource Timing**:资源加载耗时(`PerformanceObserver({ type: 'resource', buffered: true })`,首屏会比较多)
|
|
47
|
+
|
|
48
|
+
常见降噪配置:
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
import clsLogger from '@be-link/cls-logger';
|
|
52
|
+
|
|
53
|
+
clsLogger.init({
|
|
54
|
+
topic_id: 'xxx',
|
|
55
|
+
projectName: 'my-project',
|
|
56
|
+
performanceMonitor: {
|
|
57
|
+
// 降低采样(0~1)
|
|
58
|
+
sampleRate: 0.1,
|
|
59
|
+
// 忽略本地开发资源 & sourcemap(按你的资源特征调整)
|
|
60
|
+
ignoreUrls: ['localhost:8080', /\\.map$/],
|
|
61
|
+
},
|
|
62
|
+
// 可选:启动阶段合并窗口,减少首屏 perf/resource 被 intervalMs 拆成多次上报
|
|
63
|
+
// batch: { startupDelayMs: 2000 },
|
|
64
|
+
});
|
|
65
|
+
```
|
package/dist/ClsLogger.d.ts
CHANGED
|
@@ -19,6 +19,9 @@ export declare class ClsLogger {
|
|
|
19
19
|
private batchMaxSize;
|
|
20
20
|
private batchIntervalMs;
|
|
21
21
|
private batchTimer;
|
|
22
|
+
private batchTimerDueAt;
|
|
23
|
+
private initTs;
|
|
24
|
+
private startupDelayMs;
|
|
22
25
|
private failedCacheKey;
|
|
23
26
|
private failedCacheMax;
|
|
24
27
|
private requestMonitorStarted;
|
|
@@ -66,6 +69,7 @@ export declare class ClsLogger {
|
|
|
66
69
|
* 参考《一、概述》:统一上报入口(内存队列 + 批量发送)
|
|
67
70
|
*/
|
|
68
71
|
report(log: ReportLog): void;
|
|
72
|
+
private getDesiredBatchFlushDueAt;
|
|
69
73
|
info(message: string, data?: FlatFields): void;
|
|
70
74
|
warn(message: string, data?: FlatFields): void;
|
|
71
75
|
error(message: string, data?: FlatFields): void;
|
package/dist/ClsLogger.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ClsLogger.d.ts","sourceRoot":"","sources":["../src/ClsLogger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAiC,MAAM,6BAA6B,CAAC;AAEzF,OAAO,KAAK,EACV,oBAAoB,EAGpB,UAAU,EAEV,UAAU,EACV,SAAS,EACT,SAAS,EAEV,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"ClsLogger.d.ts","sourceRoot":"","sources":["../src/ClsLogger.ts"],"names":[],"mappings":"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"}
|
package/dist/index.esm.js
CHANGED
|
@@ -134,6 +134,10 @@ function stringifyLogValue(v) {
|
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
const DEFAULT_IGNORE$1 = ['cls.tencentcs.com', /\/cls\//i];
|
|
137
|
+
function isClsSendingNow() {
|
|
138
|
+
const g = globalThis;
|
|
139
|
+
return (g.__beLinkClsLoggerSendingCount__ ?? 0) > 0;
|
|
140
|
+
}
|
|
137
141
|
function shouldIgnoreUrl$1(url, ignoreUrls) {
|
|
138
142
|
for (const rule of ignoreUrls) {
|
|
139
143
|
if (typeof rule === 'string') {
|
|
@@ -211,6 +215,9 @@ function installBrowserFetch(report, options) {
|
|
|
211
215
|
w.__beLinkClsLoggerFetchInstalled__ = true;
|
|
212
216
|
w.__beLinkClsLoggerRawFetch__ = w.fetch;
|
|
213
217
|
w.fetch = async (input, init) => {
|
|
218
|
+
// 避免 CLS SDK 上报请求被 requestMonitor 捕获后递归上报(尤其在跨域失败时会“上报失败→再上报”)
|
|
219
|
+
if (isClsSendingNow())
|
|
220
|
+
return w.__beLinkClsLoggerRawFetch__(input, init);
|
|
214
221
|
let url = '';
|
|
215
222
|
let method = '';
|
|
216
223
|
let body = undefined;
|
|
@@ -300,6 +307,9 @@ function installBrowserXhr(report, options) {
|
|
|
300
307
|
return rawOpen.apply(this, args);
|
|
301
308
|
};
|
|
302
309
|
XHR.prototype.send = function (...args) {
|
|
310
|
+
// CLS SDK 发起上报时:跳过监控,避免递归上报
|
|
311
|
+
if (isClsSendingNow())
|
|
312
|
+
return rawSend.apply(this, args);
|
|
303
313
|
const startTs = Date.now();
|
|
304
314
|
try {
|
|
305
315
|
const method = String(this.__beLinkClsLoggerMethod__ ?? 'GET');
|
|
@@ -1816,6 +1826,15 @@ function createAutoDeviceInfoBaseFields(envType, opts) {
|
|
|
1816
1826
|
};
|
|
1817
1827
|
}
|
|
1818
1828
|
|
|
1829
|
+
function enterClsSendingGuard() {
|
|
1830
|
+
const g = globalThis;
|
|
1831
|
+
const next = (g.__beLinkClsLoggerSendingCount__ ?? 0) + 1;
|
|
1832
|
+
g.__beLinkClsLoggerSendingCount__ = next;
|
|
1833
|
+
return () => {
|
|
1834
|
+
const cur = g.__beLinkClsLoggerSendingCount__ ?? 0;
|
|
1835
|
+
g.__beLinkClsLoggerSendingCount__ = cur > 0 ? cur - 1 : 0;
|
|
1836
|
+
};
|
|
1837
|
+
}
|
|
1819
1838
|
class ClsLogger {
|
|
1820
1839
|
constructor() {
|
|
1821
1840
|
this.client = null;
|
|
@@ -1837,6 +1856,9 @@ class ClsLogger {
|
|
|
1837
1856
|
this.batchMaxSize = 20;
|
|
1838
1857
|
this.batchIntervalMs = 500;
|
|
1839
1858
|
this.batchTimer = null;
|
|
1859
|
+
this.batchTimerDueAt = null;
|
|
1860
|
+
this.initTs = 0;
|
|
1861
|
+
this.startupDelayMs = 0;
|
|
1840
1862
|
// 参考文档:失败缓存 + 重试
|
|
1841
1863
|
this.failedCacheKey = 'cls_failed_logs';
|
|
1842
1864
|
this.failedCacheMax = 200;
|
|
@@ -1847,6 +1869,7 @@ class ClsLogger {
|
|
|
1847
1869
|
this.behaviorMonitorCleanup = null;
|
|
1848
1870
|
}
|
|
1849
1871
|
init(options) {
|
|
1872
|
+
this.initTs = Date.now();
|
|
1850
1873
|
const topicId = options?.tencentCloud?.topicID ?? options?.topic_id ?? options?.topicID ?? this.topicId ?? null;
|
|
1851
1874
|
const endpoint = options?.tencentCloud?.endpoint ?? options?.endpoint ?? this.endpoint;
|
|
1852
1875
|
const retryTimes = options?.tencentCloud?.retry_times ?? options?.retry_times ?? this.retryTimes;
|
|
@@ -1871,6 +1894,7 @@ class ClsLogger {
|
|
|
1871
1894
|
this.batchSize = options.batchSize ?? this.batchSize;
|
|
1872
1895
|
this.batchMaxSize = options.batch?.maxSize ?? this.batchMaxSize;
|
|
1873
1896
|
this.batchIntervalMs = options.batch?.intervalMs ?? this.batchIntervalMs;
|
|
1897
|
+
this.startupDelayMs = options.batch?.startupDelayMs ?? this.startupDelayMs;
|
|
1874
1898
|
this.failedCacheKey = options.failedCacheKey ?? this.failedCacheKey;
|
|
1875
1899
|
this.failedCacheMax = options.failedCacheMax ?? this.failedCacheMax;
|
|
1876
1900
|
// 预热 client
|
|
@@ -2026,7 +2050,13 @@ class ClsLogger {
|
|
|
2026
2050
|
}
|
|
2027
2051
|
logGroup.addLog(log);
|
|
2028
2052
|
const request = new PutLogsRequest(this.topicId, logGroup);
|
|
2029
|
-
|
|
2053
|
+
const exit = enterClsSendingGuard();
|
|
2054
|
+
try {
|
|
2055
|
+
client.PutLogs(request);
|
|
2056
|
+
}
|
|
2057
|
+
finally {
|
|
2058
|
+
exit();
|
|
2059
|
+
}
|
|
2030
2060
|
}
|
|
2031
2061
|
/**
|
|
2032
2062
|
* 直接上报:把 data 序列化后放入指定 key(默认 “日志内容”)
|
|
@@ -2099,7 +2129,13 @@ class ClsLogger {
|
|
|
2099
2129
|
if (logGroup.getLogs().length === 0)
|
|
2100
2130
|
return;
|
|
2101
2131
|
const request = new PutLogsRequest(this.topicId, logGroup);
|
|
2102
|
-
|
|
2132
|
+
const exit = enterClsSendingGuard();
|
|
2133
|
+
try {
|
|
2134
|
+
client.PutLogs(request);
|
|
2135
|
+
}
|
|
2136
|
+
finally {
|
|
2137
|
+
exit();
|
|
2138
|
+
}
|
|
2103
2139
|
}
|
|
2104
2140
|
/**
|
|
2105
2141
|
* 参考《一、概述》:统一上报入口(内存队列 + 批量发送)
|
|
@@ -2117,11 +2153,39 @@ class ClsLogger {
|
|
|
2117
2153
|
void this.flushBatch();
|
|
2118
2154
|
return;
|
|
2119
2155
|
}
|
|
2156
|
+
const now = Date.now();
|
|
2157
|
+
const desiredDueAt = this.getDesiredBatchFlushDueAt(now);
|
|
2158
|
+
const desiredDelay = Math.max(0, desiredDueAt - now);
|
|
2120
2159
|
if (!this.batchTimer) {
|
|
2160
|
+
this.batchTimerDueAt = desiredDueAt;
|
|
2121
2161
|
this.batchTimer = setTimeout(() => {
|
|
2122
2162
|
void this.flushBatch();
|
|
2123
|
-
},
|
|
2163
|
+
}, desiredDelay);
|
|
2164
|
+
return;
|
|
2124
2165
|
}
|
|
2166
|
+
// 启动合并窗口内:如果当前 timer 会“更早”触发,则延后到窗口结束,尽量减少多次发送
|
|
2167
|
+
if (this.batchTimerDueAt !== null && this.batchTimerDueAt < desiredDueAt) {
|
|
2168
|
+
try {
|
|
2169
|
+
clearTimeout(this.batchTimer);
|
|
2170
|
+
}
|
|
2171
|
+
catch {
|
|
2172
|
+
// ignore
|
|
2173
|
+
}
|
|
2174
|
+
this.batchTimerDueAt = desiredDueAt;
|
|
2175
|
+
this.batchTimer = setTimeout(() => {
|
|
2176
|
+
void this.flushBatch();
|
|
2177
|
+
}, desiredDelay);
|
|
2178
|
+
}
|
|
2179
|
+
}
|
|
2180
|
+
getDesiredBatchFlushDueAt(nowTs) {
|
|
2181
|
+
const start = this.initTs || nowTs;
|
|
2182
|
+
const startupDelay = Number.isFinite(this.startupDelayMs) ? Math.max(0, this.startupDelayMs) : 0;
|
|
2183
|
+
if (startupDelay > 0) {
|
|
2184
|
+
const end = start + startupDelay;
|
|
2185
|
+
if (nowTs < end)
|
|
2186
|
+
return end;
|
|
2187
|
+
}
|
|
2188
|
+
return nowTs + this.batchIntervalMs;
|
|
2125
2189
|
}
|
|
2126
2190
|
info(message, data = {}) {
|
|
2127
2191
|
const payload = normalizeFlatFields({ message, ...data }, 'info');
|
|
@@ -2152,6 +2216,7 @@ class ClsLogger {
|
|
|
2152
2216
|
clearTimeout(this.batchTimer);
|
|
2153
2217
|
this.batchTimer = null;
|
|
2154
2218
|
}
|
|
2219
|
+
this.batchTimerDueAt = null;
|
|
2155
2220
|
if (this.memoryQueue.length === 0)
|
|
2156
2221
|
return;
|
|
2157
2222
|
const logs = [...this.memoryQueue];
|
|
@@ -2177,7 +2242,7 @@ class ClsLogger {
|
|
|
2177
2242
|
appId: this.appId || undefined,
|
|
2178
2243
|
appVersion: this.appVersion || undefined,
|
|
2179
2244
|
// 保证“一维字段”:业务数据以 JSON 字符串形式落到 CLS
|
|
2180
|
-
|
|
2245
|
+
...mergedData,
|
|
2181
2246
|
};
|
|
2182
2247
|
}
|
|
2183
2248
|
async sendReportLogs(logs) {
|
|
@@ -2197,7 +2262,16 @@ class ClsLogger {
|
|
|
2197
2262
|
logGroup.addLog(log);
|
|
2198
2263
|
}
|
|
2199
2264
|
const request = new PutLogsRequest(this.topicId, logGroup);
|
|
2200
|
-
|
|
2265
|
+
// 只在“发起网络请求”的同步阶段打标记,避免 requestMonitor 监控 CLS 上报请求导致递归
|
|
2266
|
+
const exit = enterClsSendingGuard();
|
|
2267
|
+
let p;
|
|
2268
|
+
try {
|
|
2269
|
+
p = client.PutLogs(request);
|
|
2270
|
+
}
|
|
2271
|
+
finally {
|
|
2272
|
+
exit();
|
|
2273
|
+
}
|
|
2274
|
+
await p;
|
|
2201
2275
|
}
|
|
2202
2276
|
retrySendReportLogs(logs, retryCount) {
|
|
2203
2277
|
if (retryCount > this.retryTimes) {
|
package/dist/index.js
CHANGED
|
@@ -138,6 +138,10 @@ function stringifyLogValue(v) {
|
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
const DEFAULT_IGNORE$1 = ['cls.tencentcs.com', /\/cls\//i];
|
|
141
|
+
function isClsSendingNow() {
|
|
142
|
+
const g = globalThis;
|
|
143
|
+
return (g.__beLinkClsLoggerSendingCount__ ?? 0) > 0;
|
|
144
|
+
}
|
|
141
145
|
function shouldIgnoreUrl$1(url, ignoreUrls) {
|
|
142
146
|
for (const rule of ignoreUrls) {
|
|
143
147
|
if (typeof rule === 'string') {
|
|
@@ -215,6 +219,9 @@ function installBrowserFetch(report, options) {
|
|
|
215
219
|
w.__beLinkClsLoggerFetchInstalled__ = true;
|
|
216
220
|
w.__beLinkClsLoggerRawFetch__ = w.fetch;
|
|
217
221
|
w.fetch = async (input, init) => {
|
|
222
|
+
// 避免 CLS SDK 上报请求被 requestMonitor 捕获后递归上报(尤其在跨域失败时会“上报失败→再上报”)
|
|
223
|
+
if (isClsSendingNow())
|
|
224
|
+
return w.__beLinkClsLoggerRawFetch__(input, init);
|
|
218
225
|
let url = '';
|
|
219
226
|
let method = '';
|
|
220
227
|
let body = undefined;
|
|
@@ -304,6 +311,9 @@ function installBrowserXhr(report, options) {
|
|
|
304
311
|
return rawOpen.apply(this, args);
|
|
305
312
|
};
|
|
306
313
|
XHR.prototype.send = function (...args) {
|
|
314
|
+
// CLS SDK 发起上报时:跳过监控,避免递归上报
|
|
315
|
+
if (isClsSendingNow())
|
|
316
|
+
return rawSend.apply(this, args);
|
|
307
317
|
const startTs = Date.now();
|
|
308
318
|
try {
|
|
309
319
|
const method = String(this.__beLinkClsLoggerMethod__ ?? 'GET');
|
|
@@ -1820,6 +1830,15 @@ function createAutoDeviceInfoBaseFields(envType, opts) {
|
|
|
1820
1830
|
};
|
|
1821
1831
|
}
|
|
1822
1832
|
|
|
1833
|
+
function enterClsSendingGuard() {
|
|
1834
|
+
const g = globalThis;
|
|
1835
|
+
const next = (g.__beLinkClsLoggerSendingCount__ ?? 0) + 1;
|
|
1836
|
+
g.__beLinkClsLoggerSendingCount__ = next;
|
|
1837
|
+
return () => {
|
|
1838
|
+
const cur = g.__beLinkClsLoggerSendingCount__ ?? 0;
|
|
1839
|
+
g.__beLinkClsLoggerSendingCount__ = cur > 0 ? cur - 1 : 0;
|
|
1840
|
+
};
|
|
1841
|
+
}
|
|
1823
1842
|
class ClsLogger {
|
|
1824
1843
|
constructor() {
|
|
1825
1844
|
this.client = null;
|
|
@@ -1841,6 +1860,9 @@ class ClsLogger {
|
|
|
1841
1860
|
this.batchMaxSize = 20;
|
|
1842
1861
|
this.batchIntervalMs = 500;
|
|
1843
1862
|
this.batchTimer = null;
|
|
1863
|
+
this.batchTimerDueAt = null;
|
|
1864
|
+
this.initTs = 0;
|
|
1865
|
+
this.startupDelayMs = 0;
|
|
1844
1866
|
// 参考文档:失败缓存 + 重试
|
|
1845
1867
|
this.failedCacheKey = 'cls_failed_logs';
|
|
1846
1868
|
this.failedCacheMax = 200;
|
|
@@ -1851,6 +1873,7 @@ class ClsLogger {
|
|
|
1851
1873
|
this.behaviorMonitorCleanup = null;
|
|
1852
1874
|
}
|
|
1853
1875
|
init(options) {
|
|
1876
|
+
this.initTs = Date.now();
|
|
1854
1877
|
const topicId = options?.tencentCloud?.topicID ?? options?.topic_id ?? options?.topicID ?? this.topicId ?? null;
|
|
1855
1878
|
const endpoint = options?.tencentCloud?.endpoint ?? options?.endpoint ?? this.endpoint;
|
|
1856
1879
|
const retryTimes = options?.tencentCloud?.retry_times ?? options?.retry_times ?? this.retryTimes;
|
|
@@ -1875,6 +1898,7 @@ class ClsLogger {
|
|
|
1875
1898
|
this.batchSize = options.batchSize ?? this.batchSize;
|
|
1876
1899
|
this.batchMaxSize = options.batch?.maxSize ?? this.batchMaxSize;
|
|
1877
1900
|
this.batchIntervalMs = options.batch?.intervalMs ?? this.batchIntervalMs;
|
|
1901
|
+
this.startupDelayMs = options.batch?.startupDelayMs ?? this.startupDelayMs;
|
|
1878
1902
|
this.failedCacheKey = options.failedCacheKey ?? this.failedCacheKey;
|
|
1879
1903
|
this.failedCacheMax = options.failedCacheMax ?? this.failedCacheMax;
|
|
1880
1904
|
// 预热 client
|
|
@@ -2030,7 +2054,13 @@ class ClsLogger {
|
|
|
2030
2054
|
}
|
|
2031
2055
|
logGroup.addLog(log);
|
|
2032
2056
|
const request = new tencentcloudClsSdkJsWeb.PutLogsRequest(this.topicId, logGroup);
|
|
2033
|
-
|
|
2057
|
+
const exit = enterClsSendingGuard();
|
|
2058
|
+
try {
|
|
2059
|
+
client.PutLogs(request);
|
|
2060
|
+
}
|
|
2061
|
+
finally {
|
|
2062
|
+
exit();
|
|
2063
|
+
}
|
|
2034
2064
|
}
|
|
2035
2065
|
/**
|
|
2036
2066
|
* 直接上报:把 data 序列化后放入指定 key(默认 “日志内容”)
|
|
@@ -2103,7 +2133,13 @@ class ClsLogger {
|
|
|
2103
2133
|
if (logGroup.getLogs().length === 0)
|
|
2104
2134
|
return;
|
|
2105
2135
|
const request = new tencentcloudClsSdkJsWeb.PutLogsRequest(this.topicId, logGroup);
|
|
2106
|
-
|
|
2136
|
+
const exit = enterClsSendingGuard();
|
|
2137
|
+
try {
|
|
2138
|
+
client.PutLogs(request);
|
|
2139
|
+
}
|
|
2140
|
+
finally {
|
|
2141
|
+
exit();
|
|
2142
|
+
}
|
|
2107
2143
|
}
|
|
2108
2144
|
/**
|
|
2109
2145
|
* 参考《一、概述》:统一上报入口(内存队列 + 批量发送)
|
|
@@ -2121,11 +2157,39 @@ class ClsLogger {
|
|
|
2121
2157
|
void this.flushBatch();
|
|
2122
2158
|
return;
|
|
2123
2159
|
}
|
|
2160
|
+
const now = Date.now();
|
|
2161
|
+
const desiredDueAt = this.getDesiredBatchFlushDueAt(now);
|
|
2162
|
+
const desiredDelay = Math.max(0, desiredDueAt - now);
|
|
2124
2163
|
if (!this.batchTimer) {
|
|
2164
|
+
this.batchTimerDueAt = desiredDueAt;
|
|
2125
2165
|
this.batchTimer = setTimeout(() => {
|
|
2126
2166
|
void this.flushBatch();
|
|
2127
|
-
},
|
|
2167
|
+
}, desiredDelay);
|
|
2168
|
+
return;
|
|
2128
2169
|
}
|
|
2170
|
+
// 启动合并窗口内:如果当前 timer 会“更早”触发,则延后到窗口结束,尽量减少多次发送
|
|
2171
|
+
if (this.batchTimerDueAt !== null && this.batchTimerDueAt < desiredDueAt) {
|
|
2172
|
+
try {
|
|
2173
|
+
clearTimeout(this.batchTimer);
|
|
2174
|
+
}
|
|
2175
|
+
catch {
|
|
2176
|
+
// ignore
|
|
2177
|
+
}
|
|
2178
|
+
this.batchTimerDueAt = desiredDueAt;
|
|
2179
|
+
this.batchTimer = setTimeout(() => {
|
|
2180
|
+
void this.flushBatch();
|
|
2181
|
+
}, desiredDelay);
|
|
2182
|
+
}
|
|
2183
|
+
}
|
|
2184
|
+
getDesiredBatchFlushDueAt(nowTs) {
|
|
2185
|
+
const start = this.initTs || nowTs;
|
|
2186
|
+
const startupDelay = Number.isFinite(this.startupDelayMs) ? Math.max(0, this.startupDelayMs) : 0;
|
|
2187
|
+
if (startupDelay > 0) {
|
|
2188
|
+
const end = start + startupDelay;
|
|
2189
|
+
if (nowTs < end)
|
|
2190
|
+
return end;
|
|
2191
|
+
}
|
|
2192
|
+
return nowTs + this.batchIntervalMs;
|
|
2129
2193
|
}
|
|
2130
2194
|
info(message, data = {}) {
|
|
2131
2195
|
const payload = normalizeFlatFields({ message, ...data }, 'info');
|
|
@@ -2156,6 +2220,7 @@ class ClsLogger {
|
|
|
2156
2220
|
clearTimeout(this.batchTimer);
|
|
2157
2221
|
this.batchTimer = null;
|
|
2158
2222
|
}
|
|
2223
|
+
this.batchTimerDueAt = null;
|
|
2159
2224
|
if (this.memoryQueue.length === 0)
|
|
2160
2225
|
return;
|
|
2161
2226
|
const logs = [...this.memoryQueue];
|
|
@@ -2181,7 +2246,7 @@ class ClsLogger {
|
|
|
2181
2246
|
appId: this.appId || undefined,
|
|
2182
2247
|
appVersion: this.appVersion || undefined,
|
|
2183
2248
|
// 保证“一维字段”:业务数据以 JSON 字符串形式落到 CLS
|
|
2184
|
-
|
|
2249
|
+
...mergedData,
|
|
2185
2250
|
};
|
|
2186
2251
|
}
|
|
2187
2252
|
async sendReportLogs(logs) {
|
|
@@ -2201,7 +2266,16 @@ class ClsLogger {
|
|
|
2201
2266
|
logGroup.addLog(log);
|
|
2202
2267
|
}
|
|
2203
2268
|
const request = new tencentcloudClsSdkJsWeb.PutLogsRequest(this.topicId, logGroup);
|
|
2204
|
-
|
|
2269
|
+
// 只在“发起网络请求”的同步阶段打标记,避免 requestMonitor 监控 CLS 上报请求导致递归
|
|
2270
|
+
const exit = enterClsSendingGuard();
|
|
2271
|
+
let p;
|
|
2272
|
+
try {
|
|
2273
|
+
p = client.PutLogs(request);
|
|
2274
|
+
}
|
|
2275
|
+
finally {
|
|
2276
|
+
exit();
|
|
2277
|
+
}
|
|
2278
|
+
await p;
|
|
2205
2279
|
}
|
|
2206
2280
|
retrySendReportLogs(logs, retryCount) {
|
|
2207
2281
|
if (retryCount > this.retryTimes) {
|
package/dist/index.umd.js
CHANGED
|
@@ -138,6 +138,10 @@
|
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
const DEFAULT_IGNORE$1 = ['cls.tencentcs.com', /\/cls\//i];
|
|
141
|
+
function isClsSendingNow() {
|
|
142
|
+
const g = globalThis;
|
|
143
|
+
return (g.__beLinkClsLoggerSendingCount__ ?? 0) > 0;
|
|
144
|
+
}
|
|
141
145
|
function shouldIgnoreUrl$1(url, ignoreUrls) {
|
|
142
146
|
for (const rule of ignoreUrls) {
|
|
143
147
|
if (typeof rule === 'string') {
|
|
@@ -215,6 +219,9 @@
|
|
|
215
219
|
w.__beLinkClsLoggerFetchInstalled__ = true;
|
|
216
220
|
w.__beLinkClsLoggerRawFetch__ = w.fetch;
|
|
217
221
|
w.fetch = async (input, init) => {
|
|
222
|
+
// 避免 CLS SDK 上报请求被 requestMonitor 捕获后递归上报(尤其在跨域失败时会“上报失败→再上报”)
|
|
223
|
+
if (isClsSendingNow())
|
|
224
|
+
return w.__beLinkClsLoggerRawFetch__(input, init);
|
|
218
225
|
let url = '';
|
|
219
226
|
let method = '';
|
|
220
227
|
let body = undefined;
|
|
@@ -304,6 +311,9 @@
|
|
|
304
311
|
return rawOpen.apply(this, args);
|
|
305
312
|
};
|
|
306
313
|
XHR.prototype.send = function (...args) {
|
|
314
|
+
// CLS SDK 发起上报时:跳过监控,避免递归上报
|
|
315
|
+
if (isClsSendingNow())
|
|
316
|
+
return rawSend.apply(this, args);
|
|
307
317
|
const startTs = Date.now();
|
|
308
318
|
try {
|
|
309
319
|
const method = String(this.__beLinkClsLoggerMethod__ ?? 'GET');
|
|
@@ -1820,6 +1830,15 @@
|
|
|
1820
1830
|
};
|
|
1821
1831
|
}
|
|
1822
1832
|
|
|
1833
|
+
function enterClsSendingGuard() {
|
|
1834
|
+
const g = globalThis;
|
|
1835
|
+
const next = (g.__beLinkClsLoggerSendingCount__ ?? 0) + 1;
|
|
1836
|
+
g.__beLinkClsLoggerSendingCount__ = next;
|
|
1837
|
+
return () => {
|
|
1838
|
+
const cur = g.__beLinkClsLoggerSendingCount__ ?? 0;
|
|
1839
|
+
g.__beLinkClsLoggerSendingCount__ = cur > 0 ? cur - 1 : 0;
|
|
1840
|
+
};
|
|
1841
|
+
}
|
|
1823
1842
|
class ClsLogger {
|
|
1824
1843
|
constructor() {
|
|
1825
1844
|
this.client = null;
|
|
@@ -1841,6 +1860,9 @@
|
|
|
1841
1860
|
this.batchMaxSize = 20;
|
|
1842
1861
|
this.batchIntervalMs = 500;
|
|
1843
1862
|
this.batchTimer = null;
|
|
1863
|
+
this.batchTimerDueAt = null;
|
|
1864
|
+
this.initTs = 0;
|
|
1865
|
+
this.startupDelayMs = 0;
|
|
1844
1866
|
// 参考文档:失败缓存 + 重试
|
|
1845
1867
|
this.failedCacheKey = 'cls_failed_logs';
|
|
1846
1868
|
this.failedCacheMax = 200;
|
|
@@ -1851,6 +1873,7 @@
|
|
|
1851
1873
|
this.behaviorMonitorCleanup = null;
|
|
1852
1874
|
}
|
|
1853
1875
|
init(options) {
|
|
1876
|
+
this.initTs = Date.now();
|
|
1854
1877
|
const topicId = options?.tencentCloud?.topicID ?? options?.topic_id ?? options?.topicID ?? this.topicId ?? null;
|
|
1855
1878
|
const endpoint = options?.tencentCloud?.endpoint ?? options?.endpoint ?? this.endpoint;
|
|
1856
1879
|
const retryTimes = options?.tencentCloud?.retry_times ?? options?.retry_times ?? this.retryTimes;
|
|
@@ -1875,6 +1898,7 @@
|
|
|
1875
1898
|
this.batchSize = options.batchSize ?? this.batchSize;
|
|
1876
1899
|
this.batchMaxSize = options.batch?.maxSize ?? this.batchMaxSize;
|
|
1877
1900
|
this.batchIntervalMs = options.batch?.intervalMs ?? this.batchIntervalMs;
|
|
1901
|
+
this.startupDelayMs = options.batch?.startupDelayMs ?? this.startupDelayMs;
|
|
1878
1902
|
this.failedCacheKey = options.failedCacheKey ?? this.failedCacheKey;
|
|
1879
1903
|
this.failedCacheMax = options.failedCacheMax ?? this.failedCacheMax;
|
|
1880
1904
|
// 预热 client
|
|
@@ -2030,7 +2054,13 @@
|
|
|
2030
2054
|
}
|
|
2031
2055
|
logGroup.addLog(log);
|
|
2032
2056
|
const request = new tencentcloudClsSdkJsWeb.PutLogsRequest(this.topicId, logGroup);
|
|
2033
|
-
|
|
2057
|
+
const exit = enterClsSendingGuard();
|
|
2058
|
+
try {
|
|
2059
|
+
client.PutLogs(request);
|
|
2060
|
+
}
|
|
2061
|
+
finally {
|
|
2062
|
+
exit();
|
|
2063
|
+
}
|
|
2034
2064
|
}
|
|
2035
2065
|
/**
|
|
2036
2066
|
* 直接上报:把 data 序列化后放入指定 key(默认 “日志内容”)
|
|
@@ -2103,7 +2133,13 @@
|
|
|
2103
2133
|
if (logGroup.getLogs().length === 0)
|
|
2104
2134
|
return;
|
|
2105
2135
|
const request = new tencentcloudClsSdkJsWeb.PutLogsRequest(this.topicId, logGroup);
|
|
2106
|
-
|
|
2136
|
+
const exit = enterClsSendingGuard();
|
|
2137
|
+
try {
|
|
2138
|
+
client.PutLogs(request);
|
|
2139
|
+
}
|
|
2140
|
+
finally {
|
|
2141
|
+
exit();
|
|
2142
|
+
}
|
|
2107
2143
|
}
|
|
2108
2144
|
/**
|
|
2109
2145
|
* 参考《一、概述》:统一上报入口(内存队列 + 批量发送)
|
|
@@ -2121,11 +2157,39 @@
|
|
|
2121
2157
|
void this.flushBatch();
|
|
2122
2158
|
return;
|
|
2123
2159
|
}
|
|
2160
|
+
const now = Date.now();
|
|
2161
|
+
const desiredDueAt = this.getDesiredBatchFlushDueAt(now);
|
|
2162
|
+
const desiredDelay = Math.max(0, desiredDueAt - now);
|
|
2124
2163
|
if (!this.batchTimer) {
|
|
2164
|
+
this.batchTimerDueAt = desiredDueAt;
|
|
2125
2165
|
this.batchTimer = setTimeout(() => {
|
|
2126
2166
|
void this.flushBatch();
|
|
2127
|
-
},
|
|
2167
|
+
}, desiredDelay);
|
|
2168
|
+
return;
|
|
2128
2169
|
}
|
|
2170
|
+
// 启动合并窗口内:如果当前 timer 会“更早”触发,则延后到窗口结束,尽量减少多次发送
|
|
2171
|
+
if (this.batchTimerDueAt !== null && this.batchTimerDueAt < desiredDueAt) {
|
|
2172
|
+
try {
|
|
2173
|
+
clearTimeout(this.batchTimer);
|
|
2174
|
+
}
|
|
2175
|
+
catch {
|
|
2176
|
+
// ignore
|
|
2177
|
+
}
|
|
2178
|
+
this.batchTimerDueAt = desiredDueAt;
|
|
2179
|
+
this.batchTimer = setTimeout(() => {
|
|
2180
|
+
void this.flushBatch();
|
|
2181
|
+
}, desiredDelay);
|
|
2182
|
+
}
|
|
2183
|
+
}
|
|
2184
|
+
getDesiredBatchFlushDueAt(nowTs) {
|
|
2185
|
+
const start = this.initTs || nowTs;
|
|
2186
|
+
const startupDelay = Number.isFinite(this.startupDelayMs) ? Math.max(0, this.startupDelayMs) : 0;
|
|
2187
|
+
if (startupDelay > 0) {
|
|
2188
|
+
const end = start + startupDelay;
|
|
2189
|
+
if (nowTs < end)
|
|
2190
|
+
return end;
|
|
2191
|
+
}
|
|
2192
|
+
return nowTs + this.batchIntervalMs;
|
|
2129
2193
|
}
|
|
2130
2194
|
info(message, data = {}) {
|
|
2131
2195
|
const payload = normalizeFlatFields({ message, ...data }, 'info');
|
|
@@ -2156,6 +2220,7 @@
|
|
|
2156
2220
|
clearTimeout(this.batchTimer);
|
|
2157
2221
|
this.batchTimer = null;
|
|
2158
2222
|
}
|
|
2223
|
+
this.batchTimerDueAt = null;
|
|
2159
2224
|
if (this.memoryQueue.length === 0)
|
|
2160
2225
|
return;
|
|
2161
2226
|
const logs = [...this.memoryQueue];
|
|
@@ -2181,7 +2246,7 @@
|
|
|
2181
2246
|
appId: this.appId || undefined,
|
|
2182
2247
|
appVersion: this.appVersion || undefined,
|
|
2183
2248
|
// 保证“一维字段”:业务数据以 JSON 字符串形式落到 CLS
|
|
2184
|
-
|
|
2249
|
+
...mergedData,
|
|
2185
2250
|
};
|
|
2186
2251
|
}
|
|
2187
2252
|
async sendReportLogs(logs) {
|
|
@@ -2201,7 +2266,16 @@
|
|
|
2201
2266
|
logGroup.addLog(log);
|
|
2202
2267
|
}
|
|
2203
2268
|
const request = new tencentcloudClsSdkJsWeb.PutLogsRequest(this.topicId, logGroup);
|
|
2204
|
-
|
|
2269
|
+
// 只在“发起网络请求”的同步阶段打标记,避免 requestMonitor 监控 CLS 上报请求导致递归
|
|
2270
|
+
const exit = enterClsSendingGuard();
|
|
2271
|
+
let p;
|
|
2272
|
+
try {
|
|
2273
|
+
p = client.PutLogs(request);
|
|
2274
|
+
}
|
|
2275
|
+
finally {
|
|
2276
|
+
exit();
|
|
2277
|
+
}
|
|
2278
|
+
await p;
|
|
2205
2279
|
}
|
|
2206
2280
|
retrySendReportLogs(logs, retryCount) {
|
|
2207
2281
|
if (retryCount > this.retryTimes) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"requestMonitor.d.ts","sourceRoot":"","sources":["../src/requestMonitor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAGjE,KAAK,QAAQ,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,KAAK,IAAI,CAAC;AAEzD,UAAU,cAAe,SAAQ,qBAAqB;IACpD,wDAAwD;IACxD,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;
|
|
1
|
+
{"version":3,"file":"requestMonitor.d.ts","sourceRoot":"","sources":["../src/requestMonitor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAGjE,KAAK,QAAQ,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,KAAK,IAAI,CAAC;AAEzD,UAAU,cAAe,SAAQ,qBAAqB;IACpD,wDAAwD;IACxD,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAqYD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,QAAQ,EAAE,IAAI,GAAE,cAAmB,GAAG,IAAI,CA2BvF"}
|
package/dist/types.d.ts
CHANGED
|
@@ -28,6 +28,13 @@ export interface BatchOptions {
|
|
|
28
28
|
maxSize?: number;
|
|
29
29
|
/** 多久批量发送一次,默认 500ms */
|
|
30
30
|
intervalMs?: number;
|
|
31
|
+
/**
|
|
32
|
+
* 启动阶段合并窗口(ms)
|
|
33
|
+
* - 目的:减少初始化/首屏阶段(perf/resource 等日志密集期)被 intervalMs 拆成多次上报
|
|
34
|
+
* - 行为:在该窗口内尽量延迟 flush 到窗口结束(但仍受 maxSize 约束,达到阈值会立即发送)
|
|
35
|
+
* - 默认:0(不开启)
|
|
36
|
+
*/
|
|
37
|
+
startupDelayMs?: number;
|
|
31
38
|
}
|
|
32
39
|
export interface ClsLoggerInitOptions {
|
|
33
40
|
/**
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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,EAAE,MAAM,CAAC;IACjB,kBAAkB;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qBAAqB;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,8BAA8B;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,wBAAwB;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|