sobey-monitor-sdk 1.1.4 → 1.1.5

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/dist/index.esm.js CHANGED
@@ -274,13 +274,22 @@ class Sender {
274
274
  * 策略:优先使用 sendBeacon(更可靠),不支持或失败时降级为 fetch
275
275
  */
276
276
  doSend(data) {
277
+ // 检查配置是否已初始化
278
+ if (!config.isInitialized()) {
279
+ // 配置未初始化,数据放回缓冲区等待下次发送
280
+ this.buffer.unshift(...data);
281
+ this.scheduleFlush();
282
+ return;
283
+ }
277
284
  const cfg = config.get();
278
285
  const dsn = cfg.dsn;
279
- // dsn 未配置,跳过发送
286
+ // dsn 未配置,将数据放回缓冲区等待下次发送
280
287
  if (!dsn) {
281
288
  if (cfg.debug) {
282
- console.warn('[Monitor] dsn not configured, skip sending');
289
+ console.warn('[Monitor] dsn not configured, buffering data for later');
283
290
  }
291
+ this.buffer.unshift(...data);
292
+ this.scheduleFlush();
284
293
  return;
285
294
  }
286
295
  const payload = JSON.stringify(data);
@@ -401,25 +410,83 @@ class Reporter {
401
410
  }
402
411
  }
403
412
  /**
404
- * 发送早期缓存的数据
413
+ * 发送早期缓存的数据(根据最新配置过滤)
405
414
  */
406
415
  flushEarlyBuffer() {
416
+ const cfg = config.get();
417
+ let skippedCount = 0;
418
+ // 全局开关检查:如果 enabled 为 false,清空缓存不上报
419
+ if (cfg.enabled === false) {
420
+ const totalCount = this.earlyBuffer.length;
421
+ this.earlyBuffer = [];
422
+ if (cfg.debug) {
423
+ console.log(`[Monitor] SDK disabled, discarded ${totalCount} early buffered items`);
424
+ }
425
+ return;
426
+ }
407
427
  while (this.earlyBuffer.length > 0) {
408
428
  const item = this.earlyBuffer.shift();
409
429
  if (!item)
410
430
  continue;
431
+ // 根据最新配置过滤数据
411
432
  switch (item.type) {
412
433
  case 'error':
434
+ // 检查错误监控是否启用
435
+ if (cfg.error?.enabled === false) {
436
+ skippedCount++;
437
+ continue;
438
+ }
439
+ // 根据错误类型检查具体开关
440
+ const errorType = item.data?.type;
441
+ if (errorType === 'js_error' && cfg.error?.jsError === false) {
442
+ skippedCount++;
443
+ continue;
444
+ }
445
+ if (errorType === 'promise_error' && cfg.error?.promiseError === false) {
446
+ skippedCount++;
447
+ continue;
448
+ }
449
+ if (errorType === 'resource_error' && cfg.error?.resourceError === false) {
450
+ skippedCount++;
451
+ continue;
452
+ }
453
+ if (errorType === 'http_error' && cfg.error?.httpError === false) {
454
+ skippedCount++;
455
+ continue;
456
+ }
413
457
  this.reportError(item.data);
414
458
  break;
415
459
  case 'performance':
460
+ // 检查性能监控是否启用
461
+ if (cfg.performance?.enabled === false) {
462
+ skippedCount++;
463
+ continue;
464
+ }
416
465
  this.reportPerformance(item.data);
417
466
  break;
418
467
  case 'behavior':
468
+ // 检查行为监控是否启用
469
+ if (cfg.behavior?.enabled === false) {
470
+ skippedCount++;
471
+ continue;
472
+ }
473
+ // 根据行为类型检查具体开关
474
+ const action = item.data?.action;
475
+ if (action === 'pv' && cfg.behavior?.pv === false) {
476
+ skippedCount++;
477
+ continue;
478
+ }
479
+ if (action === 'route' && cfg.behavior?.route === false) {
480
+ skippedCount++;
481
+ continue;
482
+ }
419
483
  this.reportBehavior(item.data);
420
484
  break;
421
485
  }
422
486
  }
487
+ if (skippedCount > 0 && cfg.debug) {
488
+ console.log(`[Monitor] Filtered out ${skippedCount} early buffered items based on config`);
489
+ }
423
490
  }
424
491
  /**
425
492
  * 缓存早期数据
@@ -548,17 +615,15 @@ const reporter = new Reporter();
548
615
  */
549
616
  function installJsErrorHandler() {
550
617
  window.addEventListener('error', (event) => {
551
- // 延迟检查配置(SDK 可能还在加载配置)
552
- if (config.isInitialized()) {
553
- const cfg = config.get();
554
- if (!cfg.error?.enabled || !cfg.error?.jsError) {
555
- return;
556
- }
557
- }
558
618
  // 过滤资源加载错误(由 resourceError 处理)
559
619
  if (event.target !== window) {
560
620
  return;
561
621
  }
622
+ // 检查配置(在事件触发时检查,而不是安装时)
623
+ const cfg = config.isInitialized() ? config.get() : null;
624
+ if (cfg?.error?.enabled === false || cfg?.error?.jsError === false) {
625
+ return;
626
+ }
562
627
  const errorData = {
563
628
  type: 'js_error',
564
629
  message: event.message || 'Unknown error',
@@ -579,6 +644,7 @@ function installJsErrorHandler() {
579
644
  });
580
645
  reporter.reportError(errorData);
581
646
  }, true);
647
+ console.log('[Monitor] JS error handler installed');
582
648
  }
583
649
 
584
650
  /**
@@ -589,12 +655,10 @@ function installJsErrorHandler() {
589
655
  */
590
656
  function installPromiseErrorHandler() {
591
657
  window.addEventListener('unhandledrejection', (event) => {
592
- // 延迟检查配置(SDK 可能还在加载配置)
593
- if (config.isInitialized()) {
594
- const cfg = config.get();
595
- if (!cfg.error?.enabled || !cfg.error?.promiseError) {
596
- return;
597
- }
658
+ // 检查配置(在事件触发时检查,而不是安装时)
659
+ const cfg = config.isInitialized() ? config.get() : null;
660
+ if (cfg?.error?.enabled === false || cfg?.error?.promiseError === false) {
661
+ return;
598
662
  }
599
663
  let message = 'Unhandled Promise rejection';
600
664
  let stack;
@@ -624,6 +688,7 @@ function installPromiseErrorHandler() {
624
688
  });
625
689
  reporter.reportError(errorData);
626
690
  });
691
+ console.log('[Monitor] Promise error handler installed');
627
692
  }
628
693
 
629
694
  /**
@@ -634,18 +699,16 @@ function installPromiseErrorHandler() {
634
699
  */
635
700
  function installResourceErrorHandler() {
636
701
  window.addEventListener('error', (event) => {
637
- // 延迟检查配置(SDK 可能还在加载配置)
638
- if (config.isInitialized()) {
639
- const cfg = config.get();
640
- if (!cfg.error?.enabled || !cfg.error?.resourceError) {
641
- return;
642
- }
643
- }
644
702
  const target = event.target;
645
703
  // 只处理资源加载错误(target 不是 window)
646
704
  if (!target || target === window || !(target instanceof HTMLElement)) {
647
705
  return;
648
706
  }
707
+ // 检查配置(在事件触发时检查,而不是安装时)
708
+ const cfg = config.isInitialized() ? config.get() : null;
709
+ if (cfg?.error?.enabled === false || cfg?.error?.resourceError === false) {
710
+ return;
711
+ }
649
712
  const tagName = target.tagName.toLowerCase();
650
713
  // 只监控特定标签的资源
651
714
  if (!['img', 'script', 'link', 'video', 'audio', 'source'].includes(tagName)) {
@@ -674,6 +737,7 @@ function installResourceErrorHandler() {
674
737
  });
675
738
  reporter.reportError(errorData);
676
739
  }, true); // 使用捕获阶段
740
+ console.log('[Monitor] Resource error handler installed');
677
741
  }
678
742
 
679
743
  /**
@@ -690,6 +754,7 @@ const originalFetch = window.fetch;
690
754
  function installHttpErrorHandler() {
691
755
  interceptXHR();
692
756
  interceptFetch();
757
+ console.log('[Monitor] HTTP error handler installed');
693
758
  }
694
759
  /**
695
760
  * 检查是否是 SDK 自身的请求
@@ -702,18 +767,6 @@ function isSdkRequest(url) {
702
767
  return false;
703
768
  return url.includes(cfg.dsn);
704
769
  }
705
- /**
706
- * 检查 HTTP 错误监控是否启用
707
- */
708
- function isHttpErrorEnabled() {
709
- if (!config.isInitialized())
710
- return true; // 配置未就绪时默认启用,数据会被缓存
711
- const cfg = config.get();
712
- return cfg.error?.enabled !== false && cfg.error?.httpError !== false;
713
- }
714
- /**
715
- * 拦截 XMLHttpRequest
716
- */
717
770
  function interceptXHR() {
718
771
  XMLHttpRequest.prototype.open = function (method, url, async = true, username, password) {
719
772
  const urlStr = url.toString();
@@ -743,16 +796,11 @@ function interceptXHR() {
743
796
  this.addEventListener('loadend', function () {
744
797
  if (!monitorData)
745
798
  return;
746
- if (!isHttpErrorEnabled())
747
- return;
748
799
  const duration = Date.now() - monitorData.startTime;
749
800
  const status = this.status;
750
801
  // 根据配置决定是否添加到面包屑
751
- let recordMode = 'error';
752
- if (config.isInitialized()) {
753
- const cfg = config.get();
754
- recordMode = cfg.behavior?.recordRequestBreadcrumb || 'error';
755
- }
802
+ const cfg = config.get();
803
+ const recordMode = cfg.behavior?.recordRequestBreadcrumb || 'error';
756
804
  // 重定向(3xx)始终记录,不受配置控制
757
805
  const isRedirect = status >= 300 && status < 400;
758
806
  // 其他异常情况(0-网络错误、4xx-客户端错误、5xx-服务端错误)根据配置控制
@@ -799,17 +847,11 @@ function interceptFetch() {
799
847
  const requestBody = typeof init?.body === 'string' ? init.body : undefined;
800
848
  try {
801
849
  const response = await originalFetch.call(window, input, init);
802
- if (!isHttpErrorEnabled()) {
803
- return response;
804
- }
805
850
  const duration = Date.now() - startTime;
806
851
  const status = response.status;
807
852
  // 根据配置决定是否添加到面包屑
808
- let recordMode = 'error';
809
- if (config.isInitialized()) {
810
- const cfg = config.get();
811
- recordMode = cfg.behavior?.recordRequestBreadcrumb || 'error';
812
- }
853
+ const cfg = config.get();
854
+ const recordMode = cfg.behavior?.recordRequestBreadcrumb || 'error';
813
855
  // 重定向(3xx)始终记录,不受配置控制
814
856
  const isRedirect = status >= 300 && status < 400;
815
857
  // 其他异常情况根据配置控制
@@ -848,16 +890,10 @@ function interceptFetch() {
848
890
  return response;
849
891
  }
850
892
  catch (error) {
851
- if (!isHttpErrorEnabled()) {
852
- throw error;
853
- }
854
893
  const duration = Date.now() - startTime;
855
894
  // 网络错误时根据配置决定是否添加到面包屑
856
- let recordMode = 'error';
857
- if (config.isInitialized()) {
858
- const cfg = config.get();
859
- recordMode = cfg.behavior?.recordRequestBreadcrumb || 'error';
860
- }
895
+ const cfg = config.get();
896
+ const recordMode = cfg.behavior?.recordRequestBreadcrumb || 'error';
861
897
  // 网络错误属于 error,当模式为 'all' 或 'error' 时都记录
862
898
  if (recordMode !== 'none') {
863
899
  context.addBreadcrumb({
@@ -895,10 +931,6 @@ function reportHttpError(httpInfo) {
895
931
  reporter.reportError(errorData);
896
932
  }
897
933
 
898
- /**
899
- * 白屏检测插件
900
- * 使用 DOM 采样检测页面是否白屏
901
- */
902
934
  // 采样点 - 页面关键区域
903
935
  const SAMPLE_POINTS = [
904
936
  { x: 0.5, y: 0.1 }, // 顶部中间
@@ -922,6 +954,7 @@ function installWhiteScreenDetector() {
922
954
  scheduleDetection();
923
955
  });
924
956
  }
957
+ console.log('[Monitor] White screen detector installed');
925
958
  }
926
959
  /**
927
960
  * 调度检测(延迟执行,给页面渲染时间)
@@ -929,13 +962,6 @@ function installWhiteScreenDetector() {
929
962
  function scheduleDetection() {
930
963
  // 延迟 1 秒检测
931
964
  setTimeout(() => {
932
- // 检测时才检查配置
933
- if (config.isInitialized()) {
934
- const cfg = config.get();
935
- if (!cfg.error?.enabled) {
936
- return;
937
- }
938
- }
939
965
  const isWhiteScreen = detectWhiteScreen();
940
966
  if (isWhiteScreen) {
941
967
  reportWhiteScreen();
@@ -1000,10 +1026,6 @@ function installErrorHandlers() {
1000
1026
  * 安装 Web Vitals 采集
1001
1027
  */
1002
1028
  function installWebVitals() {
1003
- const cfg = config.get();
1004
- if (!cfg.performance?.enabled || !cfg.performance?.webVitals) {
1005
- return;
1006
- }
1007
1029
  // 页面加载完成后采集
1008
1030
  if (document.readyState === 'complete') {
1009
1031
  collectMetrics();
@@ -1014,14 +1036,17 @@ function installWebVitals() {
1014
1036
  setTimeout(collectMetrics, 3000);
1015
1037
  });
1016
1038
  }
1017
- if (cfg.debug) {
1018
- console.log('[Monitor] Web Vitals collector installed');
1019
- }
1039
+ console.log('[Monitor] Web Vitals collector installed');
1020
1040
  }
1021
1041
  /**
1022
1042
  * 采集性能指标
1023
1043
  */
1024
1044
  function collectMetrics() {
1045
+ // 检查配置(在采集时检查,而不是安装时)
1046
+ const cfg = config.isInitialized() ? config.get() : null;
1047
+ if (cfg?.performance?.enabled === false || cfg?.performance?.webVitals === false) {
1048
+ return;
1049
+ }
1025
1050
  const metrics = {};
1026
1051
  // 使用 Performance API
1027
1052
  if (!window.performance) {
@@ -1130,22 +1155,13 @@ function installPerformanceMonitor() {
1130
1155
  installWebVitals();
1131
1156
  }
1132
1157
 
1133
- /**
1134
- * PV (Page View) 统计插件
1135
- */
1136
1158
  /**
1137
1159
  * 安装 PV 统计
1138
1160
  */
1139
1161
  function installPVTracker() {
1140
- const cfg = config.get();
1141
- if (!cfg.behavior?.enabled || !cfg.behavior?.pv) {
1142
- return;
1143
- }
1144
- // 页面加载时上报
1162
+ // 页面加载时上报(reporter 会缓存直到 SDK 就绪)
1145
1163
  reportPV();
1146
- if (cfg.debug) {
1147
- console.log('[Monitor] PV tracker installed');
1148
- }
1164
+ console.log('[Monitor] PV tracker installed');
1149
1165
  }
1150
1166
  /**
1151
1167
  * 上报 PV
@@ -1168,11 +1184,12 @@ function reportPV() {
1168
1184
  * 安装点击追踪
1169
1185
  */
1170
1186
  function installClickTracker() {
1171
- const cfg = config.get();
1172
- if (!cfg.behavior?.enabled || !cfg.behavior?.click) {
1173
- return;
1174
- }
1175
1187
  document.addEventListener('click', (event) => {
1188
+ // 检查配置(在事件触发时检查,而不是安装时)
1189
+ const cfg = config.isInitialized() ? config.get() : null;
1190
+ if (cfg?.behavior?.enabled === false || cfg?.behavior?.click === false) {
1191
+ return;
1192
+ }
1176
1193
  const target = event.target;
1177
1194
  if (!target)
1178
1195
  return;
@@ -1190,9 +1207,7 @@ function installClickTracker() {
1190
1207
  // data: clickData,
1191
1208
  // });
1192
1209
  }, true);
1193
- if (cfg.debug) {
1194
- console.log('[Monitor] Click tracker installed');
1195
- }
1210
+ console.log('[Monitor] Click tracker installed');
1196
1211
  }
1197
1212
  /**
1198
1213
  * 提取点击数据
@@ -1271,10 +1286,6 @@ const originalReplaceState = history.replaceState;
1271
1286
  * 安装路由监控
1272
1287
  */
1273
1288
  function installRouteTracker() {
1274
- const cfg = config.get();
1275
- if (!cfg.behavior?.enabled || !cfg.behavior?.route) {
1276
- return;
1277
- }
1278
1289
  // 拦截 pushState
1279
1290
  history.pushState = function (...args) {
1280
1291
  const result = originalPushState.apply(this, args);
@@ -1295,14 +1306,17 @@ function installRouteTracker() {
1295
1306
  window.addEventListener('hashchange', () => {
1296
1307
  handleRouteChange('hashchange');
1297
1308
  });
1298
- if (cfg.debug) {
1299
- console.log('[Monitor] Route tracker installed');
1300
- }
1309
+ console.log('[Monitor] Route tracker installed');
1301
1310
  }
1302
1311
  /**
1303
1312
  * 处理路由变化
1304
1313
  */
1305
1314
  function handleRouteChange(trigger) {
1315
+ // 检查配置(在事件触发时检查,而不是安装时)
1316
+ const cfg = config.isInitialized() ? config.get() : null;
1317
+ if (cfg?.behavior?.enabled === false || cfg?.behavior?.route === false) {
1318
+ return;
1319
+ }
1306
1320
  const routeData = {
1307
1321
  url: getPageUrl(),
1308
1322
  title: getPageTitle(),
@@ -1337,16 +1351,16 @@ const originalConsole = {
1337
1351
  * 安装控制台追踪
1338
1352
  */
1339
1353
  function installConsoleTracker() {
1340
- const cfg = config.get();
1341
- // 默认启用控制台追踪(跟随 behavior.enabled)
1342
- if (!cfg.behavior?.enabled) {
1343
- return;
1344
- }
1345
1354
  const levels = ['log', 'info', 'warn', 'error'];
1346
1355
  levels.forEach((level) => {
1347
1356
  console[level] = function (...args) {
1348
1357
  // 调用原始方法
1349
1358
  originalConsole[level].apply(console, args);
1359
+ // 检查配置(在调用时检查,而不是安装时)
1360
+ const cfg = config.isInitialized() ? config.get() : null;
1361
+ if (cfg?.behavior?.enabled === false) {
1362
+ return;
1363
+ }
1350
1364
  // 添加到面包屑(只记录 warn 和 error)
1351
1365
  if (level === 'warn' || level === 'error') {
1352
1366
  const message = formatConsoleArgs(args);
@@ -1361,9 +1375,7 @@ function installConsoleTracker() {
1361
1375
  }
1362
1376
  };
1363
1377
  });
1364
- if (cfg.debug) {
1365
- originalConsole.log('[Monitor] Console tracker installed');
1366
- }
1378
+ originalConsole.log('[Monitor] Console tracker installed');
1367
1379
  }
1368
1380
  /**
1369
1381
  * 格式化控制台参数
@@ -1395,24 +1407,22 @@ function formatConsoleArgs(args) {
1395
1407
  * 安装输入追踪
1396
1408
  */
1397
1409
  function installInputTracker() {
1398
- const cfg = config.get();
1399
- // 默认启用输入追踪(跟随 behavior.enabled)
1400
- if (!cfg.behavior?.enabled) {
1401
- return;
1402
- }
1403
1410
  // 监听 input 事件(使用 change 事件减少数据量)
1404
1411
  document.addEventListener('change', handleInputChange, true);
1405
1412
  // 监听焦点事件
1406
1413
  document.addEventListener('focus', handleFocus, true);
1407
1414
  document.addEventListener('blur', handleBlur, true);
1408
- if (cfg.debug) {
1409
- console.log('[Monitor] Input tracker installed');
1410
- }
1415
+ console.log('[Monitor] Input tracker installed');
1411
1416
  }
1412
1417
  /**
1413
1418
  * 处理输入变化
1414
1419
  */
1415
1420
  function handleInputChange(event) {
1421
+ // 检查配置(在事件触发时检查,而不是安装时)
1422
+ const cfg = config.isInitialized() ? config.get() : null;
1423
+ if (cfg?.behavior?.enabled === false) {
1424
+ return;
1425
+ }
1416
1426
  const target = event.target;
1417
1427
  if (!target || !isFormElement(target))
1418
1428
  return;
@@ -1427,6 +1437,11 @@ function handleInputChange(event) {
1427
1437
  * 处理焦点获取
1428
1438
  */
1429
1439
  function handleFocus(event) {
1440
+ // 检查配置
1441
+ const cfg = config.isInitialized() ? config.get() : null;
1442
+ if (cfg?.behavior?.enabled === false) {
1443
+ return;
1444
+ }
1430
1445
  const target = event.target;
1431
1446
  if (!target || !isFormElement(target))
1432
1447
  return;
@@ -1440,6 +1455,11 @@ function handleFocus(event) {
1440
1455
  * 处理焦点失去
1441
1456
  */
1442
1457
  function handleBlur(event) {
1458
+ // 检查配置
1459
+ const cfg = config.isInitialized() ? config.get() : null;
1460
+ if (cfg?.behavior?.enabled === false) {
1461
+ return;
1462
+ }
1443
1463
  const target = event.target;
1444
1464
  if (!target || !isFormElement(target))
1445
1465
  return;