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