@be-link/cls-logger 1.0.1-beta.0 → 1.0.1-beta.10
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 +9 -90
- package/dist/ClsLogger.d.ts.map +1 -1
- package/dist/ClsLoggerCore.d.ts +127 -0
- package/dist/ClsLoggerCore.d.ts.map +1 -0
- package/dist/ClsLoggerMini.d.ts +8 -0
- package/dist/ClsLoggerMini.d.ts.map +1 -0
- package/dist/ClsLoggerWeb.d.ts +8 -0
- package/dist/ClsLoggerWeb.d.ts.map +1 -0
- package/dist/behaviorMonitor.d.ts.map +1 -1
- package/dist/clsSdkTypes.d.ts +18 -0
- package/dist/clsSdkTypes.d.ts.map +1 -0
- package/dist/errorMonitor.d.ts.map +1 -1
- package/dist/index.esm.js +468 -172
- package/dist/index.js +468 -172
- package/dist/index.umd.js +472 -174
- package/dist/mini.d.ts +6 -0
- package/dist/mini.d.ts.map +1 -0
- package/dist/mini.esm.js +2496 -0
- package/dist/mini.js +2521 -0
- package/dist/performanceMonitor.d.ts.map +1 -1
- package/dist/requestMonitor.d.ts.map +1 -1
- package/dist/sdkUtils.d.ts +3 -0
- package/dist/sdkUtils.d.ts.map +1 -0
- package/dist/types.d.ts +32 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/web.d.ts +6 -0
- package/dist/web.d.ts.map +1 -0
- package/dist/web.esm.js +2466 -0
- package/dist/web.js +2491 -0
- package/package.json +38 -2
package/dist/index.js
CHANGED
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
-
var tencentcloudClsSdkJsWeb = require('tencentcloud-cls-sdk-js-web');
|
|
6
|
-
|
|
7
5
|
function isPlainObject(value) {
|
|
8
6
|
return Object.prototype.toString.call(value) === '[object Object]';
|
|
9
7
|
}
|
|
@@ -138,6 +136,10 @@ function stringifyLogValue(v) {
|
|
|
138
136
|
}
|
|
139
137
|
|
|
140
138
|
const DEFAULT_IGNORE$1 = ['cls.tencentcs.com', /\/cls\//i];
|
|
139
|
+
function isClsSendingNow() {
|
|
140
|
+
const g = globalThis;
|
|
141
|
+
return (g.__beLinkClsLoggerSendingCount__ ?? 0) > 0;
|
|
142
|
+
}
|
|
141
143
|
function shouldIgnoreUrl$1(url, ignoreUrls) {
|
|
142
144
|
for (const rule of ignoreUrls) {
|
|
143
145
|
if (typeof rule === 'string') {
|
|
@@ -215,6 +217,9 @@ function installBrowserFetch(report, options) {
|
|
|
215
217
|
w.__beLinkClsLoggerFetchInstalled__ = true;
|
|
216
218
|
w.__beLinkClsLoggerRawFetch__ = w.fetch;
|
|
217
219
|
w.fetch = async (input, init) => {
|
|
220
|
+
// 避免 CLS SDK 上报请求被 requestMonitor 捕获后递归上报(尤其在跨域失败时会“上报失败→再上报”)
|
|
221
|
+
if (isClsSendingNow())
|
|
222
|
+
return w.__beLinkClsLoggerRawFetch__(input, init);
|
|
218
223
|
let url = '';
|
|
219
224
|
let method = '';
|
|
220
225
|
let body = undefined;
|
|
@@ -304,6 +309,9 @@ function installBrowserXhr(report, options) {
|
|
|
304
309
|
return rawOpen.apply(this, args);
|
|
305
310
|
};
|
|
306
311
|
XHR.prototype.send = function (...args) {
|
|
312
|
+
// CLS SDK 发起上报时:跳过监控,避免递归上报
|
|
313
|
+
if (isClsSendingNow())
|
|
314
|
+
return rawSend.apply(this, args);
|
|
307
315
|
const startTs = Date.now();
|
|
308
316
|
try {
|
|
309
317
|
const method = String(this.__beLinkClsLoggerMethod__ ?? 'GET');
|
|
@@ -530,6 +538,8 @@ function installRequestMonitor(report, opts = {}) {
|
|
|
530
538
|
}
|
|
531
539
|
|
|
532
540
|
const DEFAULT_MAX_TEXT = 4000;
|
|
541
|
+
const DEFAULT_DEDUPE_WINDOW_MS = 3000;
|
|
542
|
+
const DEFAULT_DEDUPE_MAX_KEYS = 200;
|
|
533
543
|
function truncate$2(s, maxLen) {
|
|
534
544
|
if (!s)
|
|
535
545
|
return s;
|
|
@@ -555,7 +565,17 @@ function getPagePath$1() {
|
|
|
555
565
|
function normalizeErrorLike(err, maxTextLength) {
|
|
556
566
|
if (err && typeof err === 'object') {
|
|
557
567
|
const anyErr = err;
|
|
558
|
-
|
|
568
|
+
let rawMsg = anyErr.message;
|
|
569
|
+
if (!rawMsg) {
|
|
570
|
+
const str = anyErr.toString?.();
|
|
571
|
+
if (!str || str === '[object Object]') {
|
|
572
|
+
rawMsg = stringifyLogValue(anyErr);
|
|
573
|
+
}
|
|
574
|
+
else {
|
|
575
|
+
rawMsg = str;
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
const message = truncate$2(String(rawMsg ?? ''), maxTextLength);
|
|
559
579
|
const name = truncate$2(String(anyErr.name ?? ''), 200);
|
|
560
580
|
const stack = truncate$2(String(anyErr.stack ?? ''), maxTextLength);
|
|
561
581
|
return { message, name, stack };
|
|
@@ -563,6 +583,54 @@ function normalizeErrorLike(err, maxTextLength) {
|
|
|
563
583
|
const message = truncate$2(stringifyLogValue(err), maxTextLength);
|
|
564
584
|
return { message, name: '', stack: '' };
|
|
565
585
|
}
|
|
586
|
+
function createDedupeGuard(options) {
|
|
587
|
+
const cache = new Map(); // key -> lastReportAt
|
|
588
|
+
const maxKeys = Math.max(0, options.dedupeMaxKeys);
|
|
589
|
+
const windowMs = Math.max(0, options.dedupeWindowMs);
|
|
590
|
+
function touch(key, now) {
|
|
591
|
+
// refresh insertion order
|
|
592
|
+
if (cache.has(key))
|
|
593
|
+
cache.delete(key);
|
|
594
|
+
cache.set(key, now);
|
|
595
|
+
if (maxKeys > 0) {
|
|
596
|
+
while (cache.size > maxKeys) {
|
|
597
|
+
const first = cache.keys().next().value;
|
|
598
|
+
if (!first)
|
|
599
|
+
break;
|
|
600
|
+
cache.delete(first);
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
return (key) => {
|
|
605
|
+
if (!key)
|
|
606
|
+
return true;
|
|
607
|
+
if (windowMs <= 0 || maxKeys === 0)
|
|
608
|
+
return true;
|
|
609
|
+
const now = Date.now();
|
|
610
|
+
const last = cache.get(key);
|
|
611
|
+
if (typeof last === 'number' && now - last < windowMs)
|
|
612
|
+
return false;
|
|
613
|
+
touch(key, now);
|
|
614
|
+
return true;
|
|
615
|
+
};
|
|
616
|
+
}
|
|
617
|
+
function buildErrorKey(type, payload) {
|
|
618
|
+
// 使用最核心、最稳定的字段构造签名,避免把瞬态字段(如 time)带入导致失效
|
|
619
|
+
const parts = [
|
|
620
|
+
type,
|
|
621
|
+
String(payload.source ?? ''),
|
|
622
|
+
String(payload.pagePath ?? ''),
|
|
623
|
+
String(payload.message ?? ''),
|
|
624
|
+
String(payload.errorName ?? ''),
|
|
625
|
+
String(payload.stack ?? ''),
|
|
626
|
+
String(payload.filename ?? ''),
|
|
627
|
+
String(payload.lineno ?? ''),
|
|
628
|
+
String(payload.colno ?? ''),
|
|
629
|
+
String(payload.tagName ?? ''),
|
|
630
|
+
String(payload.resourceUrl ?? ''),
|
|
631
|
+
];
|
|
632
|
+
return parts.join('|');
|
|
633
|
+
}
|
|
566
634
|
function installBrowserErrorMonitor(report, options) {
|
|
567
635
|
if (typeof window === 'undefined')
|
|
568
636
|
return;
|
|
@@ -570,6 +638,7 @@ function installBrowserErrorMonitor(report, options) {
|
|
|
570
638
|
if (w.__beLinkClsLoggerErrorInstalled__)
|
|
571
639
|
return;
|
|
572
640
|
w.__beLinkClsLoggerErrorInstalled__ = true;
|
|
641
|
+
const shouldReport = createDedupeGuard(options);
|
|
573
642
|
window.addEventListener('error', (event) => {
|
|
574
643
|
try {
|
|
575
644
|
if (!sampleHit$1(options.sampleRate))
|
|
@@ -592,6 +661,8 @@ function installBrowserErrorMonitor(report, options) {
|
|
|
592
661
|
if (e.stack)
|
|
593
662
|
payload.stack = e.stack;
|
|
594
663
|
}
|
|
664
|
+
if (!shouldReport(buildErrorKey(options.reportType, payload)))
|
|
665
|
+
return;
|
|
595
666
|
report(options.reportType, payload);
|
|
596
667
|
return;
|
|
597
668
|
}
|
|
@@ -603,6 +674,8 @@ function installBrowserErrorMonitor(report, options) {
|
|
|
603
674
|
payload.source = 'resource.error';
|
|
604
675
|
payload.tagName = tagName;
|
|
605
676
|
payload.resourceUrl = truncate$2(url, 2000);
|
|
677
|
+
if (!shouldReport(buildErrorKey(options.reportType, payload)))
|
|
678
|
+
return;
|
|
606
679
|
report(options.reportType, payload);
|
|
607
680
|
}
|
|
608
681
|
}
|
|
@@ -623,6 +696,8 @@ function installBrowserErrorMonitor(report, options) {
|
|
|
623
696
|
errorName: e.name,
|
|
624
697
|
stack: e.stack,
|
|
625
698
|
};
|
|
699
|
+
if (!shouldReport(buildErrorKey(options.reportType, payload)))
|
|
700
|
+
return;
|
|
626
701
|
report(options.reportType, payload);
|
|
627
702
|
}
|
|
628
703
|
catch {
|
|
@@ -635,6 +710,7 @@ function installMiniProgramErrorMonitor(report, options) {
|
|
|
635
710
|
if (g.__beLinkClsLoggerMpErrorInstalled__)
|
|
636
711
|
return;
|
|
637
712
|
g.__beLinkClsLoggerMpErrorInstalled__ = true;
|
|
713
|
+
const shouldReport = createDedupeGuard(options);
|
|
638
714
|
const wxAny = globalThis.wx;
|
|
639
715
|
// wx.* 事件(兼容在 App 已经创建后的场景)
|
|
640
716
|
try {
|
|
@@ -643,10 +719,16 @@ function installMiniProgramErrorMonitor(report, options) {
|
|
|
643
719
|
try {
|
|
644
720
|
if (!sampleHit$1(options.sampleRate))
|
|
645
721
|
return;
|
|
646
|
-
|
|
722
|
+
const e = normalizeErrorLike(msg, options.maxTextLength);
|
|
723
|
+
const payload = {
|
|
647
724
|
source: 'wx.onError',
|
|
648
|
-
message:
|
|
649
|
-
|
|
725
|
+
message: e.message,
|
|
726
|
+
errorName: e.name,
|
|
727
|
+
stack: e.stack,
|
|
728
|
+
};
|
|
729
|
+
if (!shouldReport(buildErrorKey(options.reportType, payload)))
|
|
730
|
+
return;
|
|
731
|
+
report(options.reportType, payload);
|
|
650
732
|
}
|
|
651
733
|
catch {
|
|
652
734
|
// ignore
|
|
@@ -664,12 +746,15 @@ function installMiniProgramErrorMonitor(report, options) {
|
|
|
664
746
|
if (!sampleHit$1(options.sampleRate))
|
|
665
747
|
return;
|
|
666
748
|
const e = normalizeErrorLike(res?.reason, options.maxTextLength);
|
|
667
|
-
|
|
749
|
+
const payload = {
|
|
668
750
|
source: 'wx.onUnhandledRejection',
|
|
669
751
|
message: e.message,
|
|
670
752
|
errorName: e.name,
|
|
671
753
|
stack: e.stack,
|
|
672
|
-
}
|
|
754
|
+
};
|
|
755
|
+
if (!shouldReport(buildErrorKey(options.reportType, payload)))
|
|
756
|
+
return;
|
|
757
|
+
report(options.reportType, payload);
|
|
673
758
|
}
|
|
674
759
|
catch {
|
|
675
760
|
// ignore
|
|
@@ -691,10 +776,15 @@ function installMiniProgramErrorMonitor(report, options) {
|
|
|
691
776
|
next.onError = function (...args) {
|
|
692
777
|
try {
|
|
693
778
|
if (sampleHit$1(options.sampleRate)) {
|
|
694
|
-
|
|
779
|
+
const e = normalizeErrorLike(args?.[0], options.maxTextLength);
|
|
780
|
+
const payload = {
|
|
695
781
|
source: 'App.onError',
|
|
696
|
-
message:
|
|
697
|
-
|
|
782
|
+
message: e.message,
|
|
783
|
+
errorName: e.name,
|
|
784
|
+
stack: e.stack,
|
|
785
|
+
};
|
|
786
|
+
if (shouldReport(buildErrorKey(options.reportType, payload)))
|
|
787
|
+
report(options.reportType, payload);
|
|
698
788
|
}
|
|
699
789
|
}
|
|
700
790
|
catch {
|
|
@@ -710,12 +800,14 @@ function installMiniProgramErrorMonitor(report, options) {
|
|
|
710
800
|
if (sampleHit$1(options.sampleRate)) {
|
|
711
801
|
const reason = args?.[0]?.reason ?? args?.[0];
|
|
712
802
|
const e = normalizeErrorLike(reason, options.maxTextLength);
|
|
713
|
-
|
|
803
|
+
const payload = {
|
|
714
804
|
source: 'App.onUnhandledRejection',
|
|
715
805
|
message: e.message,
|
|
716
806
|
errorName: e.name,
|
|
717
807
|
stack: e.stack,
|
|
718
|
-
}
|
|
808
|
+
};
|
|
809
|
+
if (shouldReport(buildErrorKey(options.reportType, payload)))
|
|
810
|
+
report(options.reportType, payload);
|
|
719
811
|
}
|
|
720
812
|
}
|
|
721
813
|
catch {
|
|
@@ -744,6 +836,8 @@ function installErrorMonitor(report, opts = {}) {
|
|
|
744
836
|
sampleRate: raw.sampleRate ?? 1,
|
|
745
837
|
captureResourceError: raw.captureResourceError ?? true,
|
|
746
838
|
maxTextLength: raw.maxTextLength ?? DEFAULT_MAX_TEXT,
|
|
839
|
+
dedupeWindowMs: raw.dedupeWindowMs ?? DEFAULT_DEDUPE_WINDOW_MS,
|
|
840
|
+
dedupeMaxKeys: raw.dedupeMaxKeys ?? DEFAULT_DEDUPE_MAX_KEYS,
|
|
747
841
|
};
|
|
748
842
|
if (isMiniProgramEnv()) {
|
|
749
843
|
installMiniProgramErrorMonitor(report, options);
|
|
@@ -994,140 +1088,58 @@ function installBrowserPerformanceMonitor(report, options) {
|
|
|
994
1088
|
}
|
|
995
1089
|
}
|
|
996
1090
|
}
|
|
997
|
-
function wrapMiniProgramRouteApi(report, reportType, apiName, options) {
|
|
998
|
-
const wxAny = globalThis.wx;
|
|
999
|
-
if (!wxAny || typeof wxAny[apiName] !== 'function')
|
|
1000
|
-
return;
|
|
1001
|
-
const flagKey = `__beLinkClsLoggerMpRouteWrapped__${apiName}`;
|
|
1002
|
-
if (wxAny[flagKey])
|
|
1003
|
-
return;
|
|
1004
|
-
wxAny[flagKey] = true;
|
|
1005
|
-
const raw = wxAny[apiName].bind(wxAny);
|
|
1006
|
-
wxAny[apiName] = (opts) => {
|
|
1007
|
-
const start = Date.now();
|
|
1008
|
-
const url = opts?.url ? String(opts.url) : '';
|
|
1009
|
-
const wrapCb = (cb, success) => {
|
|
1010
|
-
return (...args) => {
|
|
1011
|
-
try {
|
|
1012
|
-
if (sampleHit(options.sampleRate)) {
|
|
1013
|
-
report(reportType, {
|
|
1014
|
-
metric: 'route',
|
|
1015
|
-
api: apiName,
|
|
1016
|
-
url,
|
|
1017
|
-
duration: Date.now() - start,
|
|
1018
|
-
success: success ? 1 : 0,
|
|
1019
|
-
error: success ? '' : truncate$1(stringifyLogValue(args?.[0]), options.maxTextLength),
|
|
1020
|
-
unit: 'ms',
|
|
1021
|
-
});
|
|
1022
|
-
}
|
|
1023
|
-
}
|
|
1024
|
-
catch {
|
|
1025
|
-
// ignore
|
|
1026
|
-
}
|
|
1027
|
-
if (typeof cb === 'function')
|
|
1028
|
-
return cb(...args);
|
|
1029
|
-
return undefined;
|
|
1030
|
-
};
|
|
1031
|
-
};
|
|
1032
|
-
const next = { ...(opts ?? {}) };
|
|
1033
|
-
next.success = wrapCb(next.success, true);
|
|
1034
|
-
next.fail = wrapCb(next.fail, false);
|
|
1035
|
-
return raw(next);
|
|
1036
|
-
};
|
|
1037
|
-
}
|
|
1038
|
-
function installMiniProgramPageRenderMonitor(report, reportType, options) {
|
|
1039
|
-
const g = globalThis;
|
|
1040
|
-
if (typeof g.Page !== 'function')
|
|
1041
|
-
return;
|
|
1042
|
-
if (g.__beLinkClsLoggerPageWrapped__)
|
|
1043
|
-
return;
|
|
1044
|
-
g.__beLinkClsLoggerPageWrapped__ = true;
|
|
1045
|
-
const rawPage = g.Page;
|
|
1046
|
-
g.Page = (pageOptions) => {
|
|
1047
|
-
const next = { ...(pageOptions ?? {}) };
|
|
1048
|
-
const rawOnLoad = next.onLoad;
|
|
1049
|
-
const rawOnReady = next.onReady;
|
|
1050
|
-
next.onLoad = function (...args) {
|
|
1051
|
-
try {
|
|
1052
|
-
this.__beLinkClsLoggerPageLoadTs__ = Date.now();
|
|
1053
|
-
}
|
|
1054
|
-
catch {
|
|
1055
|
-
// ignore
|
|
1056
|
-
}
|
|
1057
|
-
if (typeof rawOnLoad === 'function')
|
|
1058
|
-
return rawOnLoad.apply(this, args);
|
|
1059
|
-
return undefined;
|
|
1060
|
-
};
|
|
1061
|
-
next.onReady = function (...args) {
|
|
1062
|
-
try {
|
|
1063
|
-
const start = this.__beLinkClsLoggerPageLoadTs__;
|
|
1064
|
-
if (typeof start === 'number' && sampleHit(options.sampleRate)) {
|
|
1065
|
-
report(reportType, {
|
|
1066
|
-
metric: 'page-render',
|
|
1067
|
-
route: this?.route ? String(this.route) : '',
|
|
1068
|
-
duration: Date.now() - start,
|
|
1069
|
-
unit: 'ms',
|
|
1070
|
-
});
|
|
1071
|
-
}
|
|
1072
|
-
}
|
|
1073
|
-
catch {
|
|
1074
|
-
// ignore
|
|
1075
|
-
}
|
|
1076
|
-
if (typeof rawOnReady === 'function')
|
|
1077
|
-
return rawOnReady.apply(this, args);
|
|
1078
|
-
return undefined;
|
|
1079
|
-
};
|
|
1080
|
-
return rawPage(next);
|
|
1081
|
-
};
|
|
1082
|
-
}
|
|
1083
1091
|
function installMiniProgramPerformanceMonitor(report, options) {
|
|
1084
1092
|
const g = globalThis;
|
|
1093
|
+
const ctx = g.wx || g.Taro;
|
|
1094
|
+
if (!ctx || typeof ctx.getPerformance !== 'function')
|
|
1095
|
+
return;
|
|
1085
1096
|
if (g.__beLinkClsLoggerMpPerfInstalled__)
|
|
1086
1097
|
return;
|
|
1087
1098
|
g.__beLinkClsLoggerMpPerfInstalled__ = true;
|
|
1088
|
-
// 路由切换耗时(用 API 回调近似)
|
|
1089
|
-
for (const apiName of ['navigateTo', 'redirectTo', 'switchTab', 'reLaunch']) {
|
|
1090
|
-
try {
|
|
1091
|
-
wrapMiniProgramRouteApi(report, options.reportType, apiName, options);
|
|
1092
|
-
}
|
|
1093
|
-
catch {
|
|
1094
|
-
// ignore
|
|
1095
|
-
}
|
|
1096
|
-
}
|
|
1097
|
-
// 页面渲染耗时(onLoad -> onReady)
|
|
1098
|
-
try {
|
|
1099
|
-
installMiniProgramPageRenderMonitor(report, options.reportType, options);
|
|
1100
|
-
}
|
|
1101
|
-
catch {
|
|
1102
|
-
// ignore
|
|
1103
|
-
}
|
|
1104
|
-
// wx.getPerformance()(若可用,尝试读取已有 entries)
|
|
1105
1099
|
try {
|
|
1106
|
-
const
|
|
1107
|
-
if (
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
: typeof perf.getEntriesByType === 'function'
|
|
1118
|
-
? perf.getEntriesByType('navigation')
|
|
1119
|
-
: [];
|
|
1100
|
+
const perf = ctx.getPerformance();
|
|
1101
|
+
if (!perf || typeof perf.createObserver !== 'function')
|
|
1102
|
+
return;
|
|
1103
|
+
const observer = perf.createObserver((entryList) => {
|
|
1104
|
+
try {
|
|
1105
|
+
const entries = entryList.getEntries();
|
|
1106
|
+
for (const entry of entries) {
|
|
1107
|
+
if (!sampleHit(options.sampleRate))
|
|
1108
|
+
continue;
|
|
1109
|
+
// Page Render: firstRender
|
|
1110
|
+
if (entry.entryType === 'render' && entry.name === 'firstRender') {
|
|
1120
1111
|
report(options.reportType, {
|
|
1121
|
-
metric: '
|
|
1122
|
-
|
|
1112
|
+
metric: 'page-render',
|
|
1113
|
+
duration: entry.duration,
|
|
1114
|
+
pagePath: entry.path || '',
|
|
1115
|
+
unit: 'ms',
|
|
1123
1116
|
});
|
|
1124
1117
|
}
|
|
1125
|
-
|
|
1126
|
-
|
|
1118
|
+
// Route Switch: route
|
|
1119
|
+
else if (entry.entryType === 'navigation' && entry.name === 'route') {
|
|
1120
|
+
report(options.reportType, {
|
|
1121
|
+
metric: 'route',
|
|
1122
|
+
duration: entry.duration,
|
|
1123
|
+
pagePath: entry.path || '',
|
|
1124
|
+
unit: 'ms',
|
|
1125
|
+
});
|
|
1126
|
+
}
|
|
1127
|
+
// App Launch: appLaunch (Cold)
|
|
1128
|
+
else if (entry.entryType === 'navigation' && entry.name === 'appLaunch') {
|
|
1129
|
+
report(options.reportType, {
|
|
1130
|
+
metric: 'app-launch',
|
|
1131
|
+
duration: entry.duration,
|
|
1132
|
+
launchType: 'cold',
|
|
1133
|
+
unit: 'ms',
|
|
1134
|
+
});
|
|
1127
1135
|
}
|
|
1128
|
-
}
|
|
1136
|
+
}
|
|
1129
1137
|
}
|
|
1130
|
-
|
|
1138
|
+
catch {
|
|
1139
|
+
// ignore
|
|
1140
|
+
}
|
|
1141
|
+
});
|
|
1142
|
+
observer.observe({ entryTypes: ['navigation', 'render'] });
|
|
1131
1143
|
}
|
|
1132
1144
|
catch {
|
|
1133
1145
|
// ignore
|
|
@@ -1451,7 +1463,7 @@ function installBehaviorMonitor(report, envType, options = {}) {
|
|
|
1451
1463
|
const tag = (el.tagName || '').toLowerCase();
|
|
1452
1464
|
const trackId = getAttr(el, clickTrackIdAttr);
|
|
1453
1465
|
// 过滤无效点击:白名单 tag + 没有 trackId
|
|
1454
|
-
if (clickWhiteList.includes(tag)
|
|
1466
|
+
if (clickWhiteList.includes(tag) || !trackId)
|
|
1455
1467
|
return;
|
|
1456
1468
|
void uvStatePromise.then(({ uvId, meta }) => {
|
|
1457
1469
|
if (destroyed)
|
|
@@ -1492,8 +1504,12 @@ function installBehaviorMonitor(report, envType, options = {}) {
|
|
|
1492
1504
|
g.Page = function patchedPage(conf) {
|
|
1493
1505
|
const originalOnShow = conf?.onShow;
|
|
1494
1506
|
conf.onShow = function (...args) {
|
|
1495
|
-
if (pvEnabled)
|
|
1496
|
-
|
|
1507
|
+
if (pvEnabled) {
|
|
1508
|
+
const pagePath = getPagePath();
|
|
1509
|
+
if (pagePath?.length > 0) {
|
|
1510
|
+
reportPv(pagePath);
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1497
1513
|
return typeof originalOnShow === 'function' ? originalOnShow.apply(this, args) : undefined;
|
|
1498
1514
|
};
|
|
1499
1515
|
// 点击:wrap 页面 methods(bindtap 等会调用到这里的 handler)
|
|
@@ -1820,9 +1836,29 @@ function createAutoDeviceInfoBaseFields(envType, opts) {
|
|
|
1820
1836
|
};
|
|
1821
1837
|
}
|
|
1822
1838
|
|
|
1823
|
-
|
|
1839
|
+
function enterClsSendingGuard() {
|
|
1840
|
+
const g = globalThis;
|
|
1841
|
+
const next = (g.__beLinkClsLoggerSendingCount__ ?? 0) + 1;
|
|
1842
|
+
g.__beLinkClsLoggerSendingCount__ = next;
|
|
1843
|
+
return () => {
|
|
1844
|
+
const cur = g.__beLinkClsLoggerSendingCount__ ?? 0;
|
|
1845
|
+
g.__beLinkClsLoggerSendingCount__ = cur > 0 ? cur - 1 : 0;
|
|
1846
|
+
};
|
|
1847
|
+
}
|
|
1848
|
+
/**
|
|
1849
|
+
* CLS Logger 核心基类
|
|
1850
|
+
* - 负责所有上报、队列、监控逻辑
|
|
1851
|
+
* - 不包含具体的 SDK 加载实现(由子类负责)
|
|
1852
|
+
* - 这样可以把 web/mini 的 SDK 依赖彻底解耦到子入口
|
|
1853
|
+
*/
|
|
1854
|
+
class ClsLoggerCore {
|
|
1824
1855
|
constructor() {
|
|
1856
|
+
this.sdk = null;
|
|
1857
|
+
this.sdkPromise = null;
|
|
1858
|
+
this.sdkOverride = null;
|
|
1859
|
+
this.sdkLoaderOverride = null;
|
|
1825
1860
|
this.client = null;
|
|
1861
|
+
this.clientPromise = null;
|
|
1826
1862
|
this.topicId = null;
|
|
1827
1863
|
this.endpoint = 'ap-shanghai.cls.tencentcs.com';
|
|
1828
1864
|
this.retryTimes = 10;
|
|
@@ -1841,6 +1877,9 @@ class ClsLogger {
|
|
|
1841
1877
|
this.batchMaxSize = 20;
|
|
1842
1878
|
this.batchIntervalMs = 500;
|
|
1843
1879
|
this.batchTimer = null;
|
|
1880
|
+
this.batchTimerDueAt = null;
|
|
1881
|
+
this.initTs = 0;
|
|
1882
|
+
this.startupDelayMs = 0;
|
|
1844
1883
|
// 参考文档:失败缓存 + 重试
|
|
1845
1884
|
this.failedCacheKey = 'cls_failed_logs';
|
|
1846
1885
|
this.failedCacheMax = 200;
|
|
@@ -1850,7 +1889,22 @@ class ClsLogger {
|
|
|
1850
1889
|
this.behaviorMonitorStarted = false;
|
|
1851
1890
|
this.behaviorMonitorCleanup = null;
|
|
1852
1891
|
}
|
|
1892
|
+
/**
|
|
1893
|
+
* 子类可按需重写(默认检测 wx)
|
|
1894
|
+
*/
|
|
1895
|
+
detectEnvType() {
|
|
1896
|
+
const g = globalThis;
|
|
1897
|
+
// 微信、支付宝、字节跳动、UniApp 等小程序环境通常都有特定全局变量
|
|
1898
|
+
if ((g.wx && typeof g.wx.getSystemInfoSync === 'function') ||
|
|
1899
|
+
(g.my && typeof g.my.getSystemInfoSync === 'function') ||
|
|
1900
|
+
(g.tt && typeof g.tt.getSystemInfoSync === 'function') ||
|
|
1901
|
+
(g.uni && typeof g.uni.getSystemInfoSync === 'function')) {
|
|
1902
|
+
return 'miniprogram';
|
|
1903
|
+
}
|
|
1904
|
+
return 'browser';
|
|
1905
|
+
}
|
|
1853
1906
|
init(options) {
|
|
1907
|
+
this.initTs = Date.now();
|
|
1854
1908
|
const topicId = options?.tencentCloud?.topicID ?? options?.topic_id ?? options?.topicID ?? this.topicId ?? null;
|
|
1855
1909
|
const endpoint = options?.tencentCloud?.endpoint ?? options?.endpoint ?? this.endpoint;
|
|
1856
1910
|
const retryTimes = options?.tencentCloud?.retry_times ?? options?.retry_times ?? this.retryTimes;
|
|
@@ -1860,25 +1914,46 @@ class ClsLogger {
|
|
|
1860
1914
|
console.warn('ClsLogger.init 没有传 topicID/topic_id');
|
|
1861
1915
|
return;
|
|
1862
1916
|
}
|
|
1917
|
+
const nextEnvType = options.envType ?? this.detectEnvType();
|
|
1918
|
+
// envType/endpoint/retryTimes 变化时:重置 client(以及可能的 sdk)
|
|
1919
|
+
const envChanged = nextEnvType !== this.envType;
|
|
1920
|
+
const endpointChanged = endpoint !== this.endpoint;
|
|
1921
|
+
const retryChanged = retryTimes !== this.retryTimes;
|
|
1922
|
+
if (envChanged || endpointChanged || retryChanged) {
|
|
1923
|
+
this.client = null;
|
|
1924
|
+
this.clientPromise = null;
|
|
1925
|
+
}
|
|
1926
|
+
if (envChanged) {
|
|
1927
|
+
this.sdk = null;
|
|
1928
|
+
this.sdkPromise = null;
|
|
1929
|
+
}
|
|
1863
1930
|
this.topicId = topicId;
|
|
1864
1931
|
this.endpoint = endpoint;
|
|
1865
1932
|
this.retryTimes = retryTimes;
|
|
1866
1933
|
this.source = source;
|
|
1934
|
+
this.userId = options.userId ?? this.userId;
|
|
1935
|
+
this.userName = options.userName ?? this.userName;
|
|
1867
1936
|
this.projectId = options.projectId ?? this.projectId;
|
|
1868
1937
|
this.projectName = options.projectName ?? this.projectName;
|
|
1869
1938
|
this.appId = options.appId ?? this.appId;
|
|
1870
1939
|
this.appVersion = options.appVersion ?? this.appVersion;
|
|
1871
|
-
this.envType =
|
|
1940
|
+
this.envType = nextEnvType;
|
|
1941
|
+
// 可选:外部注入 SDK(优先级:sdkLoader > sdk)
|
|
1942
|
+
this.sdkLoaderOverride = options.sdkLoader ?? this.sdkLoaderOverride;
|
|
1943
|
+
this.sdkOverride = options.sdk ?? this.sdkOverride;
|
|
1872
1944
|
this.userGenerateBaseFields = options.generateBaseFields ?? this.userGenerateBaseFields;
|
|
1873
1945
|
this.autoGenerateBaseFields = createAutoDeviceInfoBaseFields(this.envType, options.deviceInfo);
|
|
1874
1946
|
this.storageKey = options.storageKey ?? this.storageKey;
|
|
1875
1947
|
this.batchSize = options.batchSize ?? this.batchSize;
|
|
1876
1948
|
this.batchMaxSize = options.batch?.maxSize ?? this.batchMaxSize;
|
|
1877
1949
|
this.batchIntervalMs = options.batch?.intervalMs ?? this.batchIntervalMs;
|
|
1950
|
+
this.startupDelayMs = options.batch?.startupDelayMs ?? this.startupDelayMs;
|
|
1878
1951
|
this.failedCacheKey = options.failedCacheKey ?? this.failedCacheKey;
|
|
1879
1952
|
this.failedCacheMax = options.failedCacheMax ?? this.failedCacheMax;
|
|
1880
|
-
//
|
|
1881
|
-
this.getInstance()
|
|
1953
|
+
// 预热(避免首条日志触发 import/初始化开销)
|
|
1954
|
+
void this.getInstance().catch(() => {
|
|
1955
|
+
// ignore
|
|
1956
|
+
});
|
|
1882
1957
|
// 启动时尝试发送失败缓存
|
|
1883
1958
|
this.flushFailed();
|
|
1884
1959
|
// 初始化后立即启动请求监听
|
|
@@ -1903,7 +1978,7 @@ class ClsLogger {
|
|
|
1903
1978
|
try {
|
|
1904
1979
|
const userRaw = this.userGenerateBaseFields ? this.userGenerateBaseFields() : undefined;
|
|
1905
1980
|
if (userRaw && isPlainObject(userRaw))
|
|
1906
|
-
user = normalizeFlatFields(userRaw, 'generateBaseFields');
|
|
1981
|
+
user = normalizeFlatFields({ ...userRaw }, 'generateBaseFields');
|
|
1907
1982
|
}
|
|
1908
1983
|
catch {
|
|
1909
1984
|
user = undefined;
|
|
@@ -1982,20 +2057,29 @@ class ClsLogger {
|
|
|
1982
2057
|
this.behaviorMonitorCleanup = null;
|
|
1983
2058
|
this.behaviorMonitorStarted = false;
|
|
1984
2059
|
}
|
|
1985
|
-
|
|
2060
|
+
/**
|
|
2061
|
+
* 获取 CLS client(按环境懒加载 SDK)
|
|
2062
|
+
*/
|
|
2063
|
+
async getInstance() {
|
|
1986
2064
|
if (this.client)
|
|
1987
2065
|
return this.client;
|
|
1988
|
-
this.
|
|
1989
|
-
|
|
1990
|
-
|
|
2066
|
+
if (this.clientPromise)
|
|
2067
|
+
return this.clientPromise;
|
|
2068
|
+
this.clientPromise = this.loadSdk()
|
|
2069
|
+
.then(({ AsyncClient }) => {
|
|
2070
|
+
const client = new AsyncClient({
|
|
2071
|
+
endpoint: this.endpoint,
|
|
2072
|
+
retry_times: this.retryTimes,
|
|
2073
|
+
});
|
|
2074
|
+
this.client = client;
|
|
2075
|
+
return client;
|
|
2076
|
+
})
|
|
2077
|
+
.catch((err) => {
|
|
2078
|
+
// 失败后允许下次重试
|
|
2079
|
+
this.clientPromise = null;
|
|
2080
|
+
throw err;
|
|
1991
2081
|
});
|
|
1992
|
-
return this.
|
|
1993
|
-
}
|
|
1994
|
-
detectEnvType() {
|
|
1995
|
-
const wxAny = globalThis.wx;
|
|
1996
|
-
if (wxAny && typeof wxAny.getSystemInfoSync === 'function')
|
|
1997
|
-
return 'miniprogram';
|
|
1998
|
-
return 'browser';
|
|
2082
|
+
return this.clientPromise;
|
|
1999
2083
|
}
|
|
2000
2084
|
/**
|
|
2001
2085
|
* 直接上报:埋点入参必须是一维(扁平)Object
|
|
@@ -2021,16 +2105,33 @@ class ClsLogger {
|
|
|
2021
2105
|
appVersion: this.appVersion || undefined,
|
|
2022
2106
|
...normalizedFields,
|
|
2023
2107
|
});
|
|
2024
|
-
|
|
2025
|
-
|
|
2108
|
+
// 同步 API:内部异步发送,避免把网络异常冒泡到业务(尤其小程序)
|
|
2109
|
+
void this.putAsync(finalFields).catch(() => {
|
|
2110
|
+
// ignore
|
|
2111
|
+
});
|
|
2112
|
+
}
|
|
2113
|
+
async putAsync(finalFields) {
|
|
2114
|
+
if (!this.topicId)
|
|
2115
|
+
return;
|
|
2116
|
+
const sdk = await this.loadSdk();
|
|
2117
|
+
const client = await this.getInstance();
|
|
2118
|
+
const logGroup = new sdk.LogGroup('127.0.0.1');
|
|
2026
2119
|
logGroup.setSource(this.source);
|
|
2027
|
-
const log = new
|
|
2120
|
+
const log = new sdk.Log(Date.now());
|
|
2028
2121
|
for (const key of Object.keys(finalFields)) {
|
|
2029
2122
|
log.addContent(key, stringifyLogValue(finalFields[key]));
|
|
2030
2123
|
}
|
|
2031
2124
|
logGroup.addLog(log);
|
|
2032
|
-
const request = new
|
|
2033
|
-
|
|
2125
|
+
const request = new sdk.PutLogsRequest(this.topicId, logGroup);
|
|
2126
|
+
const exit = enterClsSendingGuard();
|
|
2127
|
+
let p;
|
|
2128
|
+
try {
|
|
2129
|
+
p = client.PutLogs(request);
|
|
2130
|
+
}
|
|
2131
|
+
finally {
|
|
2132
|
+
exit();
|
|
2133
|
+
}
|
|
2134
|
+
await p;
|
|
2034
2135
|
}
|
|
2035
2136
|
/**
|
|
2036
2137
|
* 直接上报:把 data 序列化后放入指定 key(默认 “日志内容”)
|
|
@@ -2089,11 +2190,19 @@ class ClsLogger {
|
|
|
2089
2190
|
console.warn('ClsLogger.putBatch:未初始化 topic_id');
|
|
2090
2191
|
return;
|
|
2091
2192
|
}
|
|
2092
|
-
|
|
2093
|
-
|
|
2193
|
+
void this.putBatchAsync(queue).catch(() => {
|
|
2194
|
+
// ignore
|
|
2195
|
+
});
|
|
2196
|
+
}
|
|
2197
|
+
async putBatchAsync(queue) {
|
|
2198
|
+
if (!this.topicId)
|
|
2199
|
+
return;
|
|
2200
|
+
const sdk = await this.loadSdk();
|
|
2201
|
+
const client = await this.getInstance();
|
|
2202
|
+
const logGroup = new sdk.LogGroup('127.0.0.1');
|
|
2094
2203
|
logGroup.setSource(this.source);
|
|
2095
2204
|
for (const item of queue) {
|
|
2096
|
-
const log = new
|
|
2205
|
+
const log = new sdk.Log(item.time);
|
|
2097
2206
|
const data = item.data ?? {};
|
|
2098
2207
|
for (const key of Object.keys(data)) {
|
|
2099
2208
|
log.addContent(key, stringifyLogValue(data[key]));
|
|
@@ -2102,8 +2211,16 @@ class ClsLogger {
|
|
|
2102
2211
|
}
|
|
2103
2212
|
if (logGroup.getLogs().length === 0)
|
|
2104
2213
|
return;
|
|
2105
|
-
const request = new
|
|
2106
|
-
|
|
2214
|
+
const request = new sdk.PutLogsRequest(this.topicId, logGroup);
|
|
2215
|
+
const exit = enterClsSendingGuard();
|
|
2216
|
+
let p;
|
|
2217
|
+
try {
|
|
2218
|
+
p = client.PutLogs(request);
|
|
2219
|
+
}
|
|
2220
|
+
finally {
|
|
2221
|
+
exit();
|
|
2222
|
+
}
|
|
2223
|
+
await p;
|
|
2107
2224
|
}
|
|
2108
2225
|
/**
|
|
2109
2226
|
* 参考《一、概述》:统一上报入口(内存队列 + 批量发送)
|
|
@@ -2121,11 +2238,39 @@ class ClsLogger {
|
|
|
2121
2238
|
void this.flushBatch();
|
|
2122
2239
|
return;
|
|
2123
2240
|
}
|
|
2241
|
+
const now = Date.now();
|
|
2242
|
+
const desiredDueAt = this.getDesiredBatchFlushDueAt(now);
|
|
2243
|
+
const desiredDelay = Math.max(0, desiredDueAt - now);
|
|
2124
2244
|
if (!this.batchTimer) {
|
|
2245
|
+
this.batchTimerDueAt = desiredDueAt;
|
|
2125
2246
|
this.batchTimer = setTimeout(() => {
|
|
2126
2247
|
void this.flushBatch();
|
|
2127
|
-
},
|
|
2248
|
+
}, desiredDelay);
|
|
2249
|
+
return;
|
|
2128
2250
|
}
|
|
2251
|
+
// 启动合并窗口内:如果当前 timer 会“更早”触发,则延后到窗口结束,尽量减少多次发送
|
|
2252
|
+
if (this.batchTimerDueAt !== null && this.batchTimerDueAt < desiredDueAt) {
|
|
2253
|
+
try {
|
|
2254
|
+
clearTimeout(this.batchTimer);
|
|
2255
|
+
}
|
|
2256
|
+
catch {
|
|
2257
|
+
// ignore
|
|
2258
|
+
}
|
|
2259
|
+
this.batchTimerDueAt = desiredDueAt;
|
|
2260
|
+
this.batchTimer = setTimeout(() => {
|
|
2261
|
+
void this.flushBatch();
|
|
2262
|
+
}, desiredDelay);
|
|
2263
|
+
}
|
|
2264
|
+
}
|
|
2265
|
+
getDesiredBatchFlushDueAt(nowTs) {
|
|
2266
|
+
const start = this.initTs || nowTs;
|
|
2267
|
+
const startupDelay = Number.isFinite(this.startupDelayMs) ? Math.max(0, this.startupDelayMs) : 0;
|
|
2268
|
+
if (startupDelay > 0) {
|
|
2269
|
+
const end = start + startupDelay;
|
|
2270
|
+
if (nowTs < end)
|
|
2271
|
+
return end;
|
|
2272
|
+
}
|
|
2273
|
+
return nowTs + this.batchIntervalMs;
|
|
2129
2274
|
}
|
|
2130
2275
|
info(message, data = {}) {
|
|
2131
2276
|
const payload = normalizeFlatFields({ message, ...data }, 'info');
|
|
@@ -2156,6 +2301,7 @@ class ClsLogger {
|
|
|
2156
2301
|
clearTimeout(this.batchTimer);
|
|
2157
2302
|
this.batchTimer = null;
|
|
2158
2303
|
}
|
|
2304
|
+
this.batchTimerDueAt = null;
|
|
2159
2305
|
if (this.memoryQueue.length === 0)
|
|
2160
2306
|
return;
|
|
2161
2307
|
const logs = [...this.memoryQueue];
|
|
@@ -2181,18 +2327,19 @@ class ClsLogger {
|
|
|
2181
2327
|
appId: this.appId || undefined,
|
|
2182
2328
|
appVersion: this.appVersion || undefined,
|
|
2183
2329
|
// 保证“一维字段”:业务数据以 JSON 字符串形式落到 CLS
|
|
2184
|
-
|
|
2330
|
+
...mergedData,
|
|
2185
2331
|
};
|
|
2186
2332
|
}
|
|
2187
2333
|
async sendReportLogs(logs) {
|
|
2188
2334
|
if (!this.topicId)
|
|
2189
2335
|
return;
|
|
2190
|
-
const
|
|
2191
|
-
const
|
|
2336
|
+
const sdk = await this.loadSdk();
|
|
2337
|
+
const client = await this.getInstance();
|
|
2338
|
+
const logGroup = new sdk.LogGroup('127.0.0.1');
|
|
2192
2339
|
logGroup.setSource(this.source);
|
|
2193
2340
|
for (const item of logs) {
|
|
2194
2341
|
const fields = this.buildReportFields(item);
|
|
2195
|
-
const log = new
|
|
2342
|
+
const log = new sdk.Log(fields.timestamp);
|
|
2196
2343
|
for (const key of Object.keys(fields)) {
|
|
2197
2344
|
if (key === 'timestamp')
|
|
2198
2345
|
continue;
|
|
@@ -2200,8 +2347,17 @@ class ClsLogger {
|
|
|
2200
2347
|
}
|
|
2201
2348
|
logGroup.addLog(log);
|
|
2202
2349
|
}
|
|
2203
|
-
const request = new
|
|
2204
|
-
|
|
2350
|
+
const request = new sdk.PutLogsRequest(this.topicId, logGroup);
|
|
2351
|
+
// 只在“发起网络请求”的同步阶段打标记,避免 requestMonitor 监控 CLS 上报请求导致递归
|
|
2352
|
+
const exit = enterClsSendingGuard();
|
|
2353
|
+
let p;
|
|
2354
|
+
try {
|
|
2355
|
+
p = client.PutLogs(request);
|
|
2356
|
+
}
|
|
2357
|
+
finally {
|
|
2358
|
+
exit();
|
|
2359
|
+
}
|
|
2360
|
+
await p;
|
|
2205
2361
|
}
|
|
2206
2362
|
retrySendReportLogs(logs, retryCount) {
|
|
2207
2363
|
if (retryCount > this.retryTimes) {
|
|
@@ -2267,6 +2423,146 @@ class ClsLogger {
|
|
|
2267
2423
|
}
|
|
2268
2424
|
}
|
|
2269
2425
|
|
|
2426
|
+
function readGlobal(key) {
|
|
2427
|
+
try {
|
|
2428
|
+
const g = globalThis;
|
|
2429
|
+
return g[key] ?? null;
|
|
2430
|
+
}
|
|
2431
|
+
catch {
|
|
2432
|
+
return null;
|
|
2433
|
+
}
|
|
2434
|
+
}
|
|
2435
|
+
function tryRequire(moduleName) {
|
|
2436
|
+
try {
|
|
2437
|
+
// 说明:
|
|
2438
|
+
// - ESM 构建(exports.import/module)里通常不存在模块作用域的 require
|
|
2439
|
+
// - 一些小程序运行时/构建链路会把 require 挂到 globalThis 上
|
|
2440
|
+
// 因此这里同时探测“模块作用域 require”与 “globalThis.require”
|
|
2441
|
+
// eslint-disable-next-line @typescript-eslint/no-implied-eval
|
|
2442
|
+
const localReq = (typeof require === 'function' ? require : null);
|
|
2443
|
+
const globalReq = readGlobal('require');
|
|
2444
|
+
const candidates = [localReq, globalReq].filter((fn) => typeof fn === 'function');
|
|
2445
|
+
for (const fn of candidates) {
|
|
2446
|
+
try {
|
|
2447
|
+
return fn(moduleName);
|
|
2448
|
+
}
|
|
2449
|
+
catch {
|
|
2450
|
+
// continue
|
|
2451
|
+
}
|
|
2452
|
+
}
|
|
2453
|
+
return null;
|
|
2454
|
+
}
|
|
2455
|
+
catch {
|
|
2456
|
+
return null;
|
|
2457
|
+
}
|
|
2458
|
+
}
|
|
2459
|
+
|
|
2460
|
+
/**
|
|
2461
|
+
* 兼容版 ClsLogger(默认入口)
|
|
2462
|
+
* - 保留了自动识别环境 + 动态 import 的逻辑
|
|
2463
|
+
* - 如果业务想瘦身,建议改用 @be-link/cls-logger/web 或 /mini
|
|
2464
|
+
*/
|
|
2465
|
+
class ClsLogger extends ClsLoggerCore {
|
|
2466
|
+
async loadSdk() {
|
|
2467
|
+
if (this.sdk)
|
|
2468
|
+
return this.sdk;
|
|
2469
|
+
if (this.sdkPromise)
|
|
2470
|
+
return this.sdkPromise;
|
|
2471
|
+
const normalizeSdk = (m) => {
|
|
2472
|
+
const mod = (m?.default && m.default.AsyncClient ? m.default : m);
|
|
2473
|
+
if (mod?.AsyncClient && mod?.Log && mod?.LogGroup && mod?.PutLogsRequest) {
|
|
2474
|
+
return {
|
|
2475
|
+
AsyncClient: mod.AsyncClient,
|
|
2476
|
+
Log: mod.Log,
|
|
2477
|
+
LogGroup: mod.LogGroup,
|
|
2478
|
+
PutLogsRequest: mod.PutLogsRequest,
|
|
2479
|
+
};
|
|
2480
|
+
}
|
|
2481
|
+
return null;
|
|
2482
|
+
};
|
|
2483
|
+
// 1) 外部注入的 loader(最高优先级)
|
|
2484
|
+
if (this.sdkLoaderOverride) {
|
|
2485
|
+
try {
|
|
2486
|
+
const loaded = await this.sdkLoaderOverride();
|
|
2487
|
+
const sdk = normalizeSdk(loaded);
|
|
2488
|
+
if (sdk) {
|
|
2489
|
+
this.sdk = sdk;
|
|
2490
|
+
return sdk;
|
|
2491
|
+
}
|
|
2492
|
+
}
|
|
2493
|
+
catch {
|
|
2494
|
+
// ignore and fallback
|
|
2495
|
+
}
|
|
2496
|
+
}
|
|
2497
|
+
// 2) 外部直接注入的 sdk
|
|
2498
|
+
if (this.sdkOverride) {
|
|
2499
|
+
const sdk = normalizeSdk(this.sdkOverride);
|
|
2500
|
+
if (sdk) {
|
|
2501
|
+
this.sdk = sdk;
|
|
2502
|
+
return sdk;
|
|
2503
|
+
}
|
|
2504
|
+
}
|
|
2505
|
+
const isMini = this.envType === 'miniprogram';
|
|
2506
|
+
// 3) 尝试 require / 全局变量
|
|
2507
|
+
// Mini Program: 尝试直接 require
|
|
2508
|
+
if (isMini) {
|
|
2509
|
+
const reqMod = tryRequire('tencentcloud-cls-sdk-js-mini');
|
|
2510
|
+
const reqSdk = normalizeSdk(reqMod);
|
|
2511
|
+
if (reqSdk) {
|
|
2512
|
+
this.sdk = reqSdk;
|
|
2513
|
+
return reqSdk;
|
|
2514
|
+
}
|
|
2515
|
+
}
|
|
2516
|
+
else {
|
|
2517
|
+
// Web: 优先读全局变量 (UMD)
|
|
2518
|
+
const g = readGlobal('tencentcloudClsSdkJsWeb');
|
|
2519
|
+
const sdk = normalizeSdk(g);
|
|
2520
|
+
if (sdk) {
|
|
2521
|
+
this.sdk = sdk;
|
|
2522
|
+
return sdk;
|
|
2523
|
+
}
|
|
2524
|
+
// Web: 尝试 require
|
|
2525
|
+
const reqMod = tryRequire('tencentcloud-cls-sdk-js-web');
|
|
2526
|
+
const reqSdk = normalizeSdk(reqMod);
|
|
2527
|
+
if (reqSdk) {
|
|
2528
|
+
this.sdk = reqSdk;
|
|
2529
|
+
return reqSdk;
|
|
2530
|
+
}
|
|
2531
|
+
}
|
|
2532
|
+
// 4) 动态 import
|
|
2533
|
+
// 使用字符串字面量,确保 Bundler 能正确识别并进行 Code Splitting
|
|
2534
|
+
if (isMini) {
|
|
2535
|
+
this.sdkPromise = import('tencentcloud-cls-sdk-js-mini')
|
|
2536
|
+
.then((m) => {
|
|
2537
|
+
const sdk = normalizeSdk(m);
|
|
2538
|
+
if (!sdk)
|
|
2539
|
+
throw new Error(`ClsLogger.loadSdk: invalid sdk module for mini`);
|
|
2540
|
+
this.sdk = sdk;
|
|
2541
|
+
return sdk;
|
|
2542
|
+
})
|
|
2543
|
+
.catch((err) => {
|
|
2544
|
+
this.sdkPromise = null;
|
|
2545
|
+
throw err;
|
|
2546
|
+
});
|
|
2547
|
+
}
|
|
2548
|
+
else {
|
|
2549
|
+
this.sdkPromise = import('tencentcloud-cls-sdk-js-web')
|
|
2550
|
+
.then((m) => {
|
|
2551
|
+
const sdk = normalizeSdk(m);
|
|
2552
|
+
if (!sdk)
|
|
2553
|
+
throw new Error(`ClsLogger.loadSdk: invalid sdk module for web`);
|
|
2554
|
+
this.sdk = sdk;
|
|
2555
|
+
return sdk;
|
|
2556
|
+
})
|
|
2557
|
+
.catch((err) => {
|
|
2558
|
+
this.sdkPromise = null;
|
|
2559
|
+
throw err;
|
|
2560
|
+
});
|
|
2561
|
+
}
|
|
2562
|
+
return this.sdkPromise;
|
|
2563
|
+
}
|
|
2564
|
+
}
|
|
2565
|
+
|
|
2270
2566
|
const clsLogger = new ClsLogger();
|
|
2271
2567
|
|
|
2272
2568
|
exports.ClsLogger = ClsLogger;
|