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