sobey-monitor-sdk 1.1.4 → 1.1.6

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.cjs.js CHANGED
@@ -278,13 +278,22 @@ class Sender {
278
278
  * 策略:优先使用 sendBeacon(更可靠),不支持或失败时降级为 fetch
279
279
  */
280
280
  doSend(data) {
281
+ // 检查配置是否已初始化
282
+ if (!config.isInitialized()) {
283
+ // 配置未初始化,数据放回缓冲区等待下次发送
284
+ this.buffer.unshift(...data);
285
+ this.scheduleFlush();
286
+ return;
287
+ }
281
288
  const cfg = config.get();
282
289
  const dsn = cfg.dsn;
283
- // dsn 未配置,跳过发送
290
+ // dsn 未配置,将数据放回缓冲区等待下次发送
284
291
  if (!dsn) {
285
292
  if (cfg.debug) {
286
- console.warn('[Monitor] dsn not configured, skip sending');
293
+ console.warn('[Monitor] dsn not configured, buffering data for later');
287
294
  }
295
+ this.buffer.unshift(...data);
296
+ this.scheduleFlush();
288
297
  return;
289
298
  }
290
299
  const payload = JSON.stringify(data);
@@ -405,25 +414,83 @@ class Reporter {
405
414
  }
406
415
  }
407
416
  /**
408
- * 发送早期缓存的数据
417
+ * 发送早期缓存的数据(根据最新配置过滤)
409
418
  */
410
419
  flushEarlyBuffer() {
420
+ const cfg = config.get();
421
+ let skippedCount = 0;
422
+ // 全局开关检查:如果 enabled 为 false,清空缓存不上报
423
+ if (cfg.enabled === false) {
424
+ const totalCount = this.earlyBuffer.length;
425
+ this.earlyBuffer = [];
426
+ if (cfg.debug) {
427
+ console.log(`[Monitor] SDK disabled, discarded ${totalCount} early buffered items`);
428
+ }
429
+ return;
430
+ }
411
431
  while (this.earlyBuffer.length > 0) {
412
432
  const item = this.earlyBuffer.shift();
413
433
  if (!item)
414
434
  continue;
435
+ // 根据最新配置过滤数据
415
436
  switch (item.type) {
416
437
  case 'error':
438
+ // 检查错误监控是否启用
439
+ if (cfg.error?.enabled === false) {
440
+ skippedCount++;
441
+ continue;
442
+ }
443
+ // 根据错误类型检查具体开关
444
+ const errorType = item.data?.type;
445
+ if (errorType === 'js_error' && cfg.error?.jsError === false) {
446
+ skippedCount++;
447
+ continue;
448
+ }
449
+ if (errorType === 'promise_error' && cfg.error?.promiseError === false) {
450
+ skippedCount++;
451
+ continue;
452
+ }
453
+ if (errorType === 'resource_error' && cfg.error?.resourceError === false) {
454
+ skippedCount++;
455
+ continue;
456
+ }
457
+ if (errorType === 'http_error' && cfg.error?.httpError === false) {
458
+ skippedCount++;
459
+ continue;
460
+ }
417
461
  this.reportError(item.data);
418
462
  break;
419
463
  case 'performance':
464
+ // 检查性能监控是否启用
465
+ if (cfg.performance?.enabled === false) {
466
+ skippedCount++;
467
+ continue;
468
+ }
420
469
  this.reportPerformance(item.data);
421
470
  break;
422
471
  case 'behavior':
472
+ // 检查行为监控是否启用
473
+ if (cfg.behavior?.enabled === false) {
474
+ skippedCount++;
475
+ continue;
476
+ }
477
+ // 根据行为类型检查具体开关
478
+ const action = item.data?.action;
479
+ if (action === 'pv' && cfg.behavior?.pv === false) {
480
+ skippedCount++;
481
+ continue;
482
+ }
483
+ if (action === 'route' && cfg.behavior?.route === false) {
484
+ skippedCount++;
485
+ continue;
486
+ }
423
487
  this.reportBehavior(item.data);
424
488
  break;
425
489
  }
426
490
  }
491
+ if (skippedCount > 0 && cfg.debug) {
492
+ console.log(`[Monitor] Filtered out ${skippedCount} early buffered items based on config`);
493
+ }
427
494
  }
428
495
  /**
429
496
  * 缓存早期数据
@@ -552,17 +619,15 @@ const reporter = new Reporter();
552
619
  */
553
620
  function installJsErrorHandler() {
554
621
  window.addEventListener('error', (event) => {
555
- // 延迟检查配置(SDK 可能还在加载配置)
556
- if (config.isInitialized()) {
557
- const cfg = config.get();
558
- if (!cfg.error?.enabled || !cfg.error?.jsError) {
559
- return;
560
- }
561
- }
562
622
  // 过滤资源加载错误(由 resourceError 处理)
563
623
  if (event.target !== window) {
564
624
  return;
565
625
  }
626
+ // 检查配置(在事件触发时检查,而不是安装时)
627
+ const cfg = config.isInitialized() ? config.get() : null;
628
+ if (cfg?.error?.enabled === false || cfg?.error?.jsError === false) {
629
+ return;
630
+ }
566
631
  const errorData = {
567
632
  type: 'js_error',
568
633
  message: event.message || 'Unknown error',
@@ -583,6 +648,7 @@ function installJsErrorHandler() {
583
648
  });
584
649
  reporter.reportError(errorData);
585
650
  }, true);
651
+ console.log('[Monitor] JS error handler installed');
586
652
  }
587
653
 
588
654
  /**
@@ -593,12 +659,10 @@ function installJsErrorHandler() {
593
659
  */
594
660
  function installPromiseErrorHandler() {
595
661
  window.addEventListener('unhandledrejection', (event) => {
596
- // 延迟检查配置(SDK 可能还在加载配置)
597
- if (config.isInitialized()) {
598
- const cfg = config.get();
599
- if (!cfg.error?.enabled || !cfg.error?.promiseError) {
600
- return;
601
- }
662
+ // 检查配置(在事件触发时检查,而不是安装时)
663
+ const cfg = config.isInitialized() ? config.get() : null;
664
+ if (cfg?.error?.enabled === false || cfg?.error?.promiseError === false) {
665
+ return;
602
666
  }
603
667
  let message = 'Unhandled Promise rejection';
604
668
  let stack;
@@ -628,6 +692,7 @@ function installPromiseErrorHandler() {
628
692
  });
629
693
  reporter.reportError(errorData);
630
694
  });
695
+ console.log('[Monitor] Promise error handler installed');
631
696
  }
632
697
 
633
698
  /**
@@ -638,18 +703,16 @@ function installPromiseErrorHandler() {
638
703
  */
639
704
  function installResourceErrorHandler() {
640
705
  window.addEventListener('error', (event) => {
641
- // 延迟检查配置(SDK 可能还在加载配置)
642
- if (config.isInitialized()) {
643
- const cfg = config.get();
644
- if (!cfg.error?.enabled || !cfg.error?.resourceError) {
645
- return;
646
- }
647
- }
648
706
  const target = event.target;
649
707
  // 只处理资源加载错误(target 不是 window)
650
708
  if (!target || target === window || !(target instanceof HTMLElement)) {
651
709
  return;
652
710
  }
711
+ // 检查配置(在事件触发时检查,而不是安装时)
712
+ const cfg = config.isInitialized() ? config.get() : null;
713
+ if (cfg?.error?.enabled === false || cfg?.error?.resourceError === false) {
714
+ return;
715
+ }
653
716
  const tagName = target.tagName.toLowerCase();
654
717
  // 只监控特定标签的资源
655
718
  if (!['img', 'script', 'link', 'video', 'audio', 'source'].includes(tagName)) {
@@ -678,6 +741,7 @@ function installResourceErrorHandler() {
678
741
  });
679
742
  reporter.reportError(errorData);
680
743
  }, true); // 使用捕获阶段
744
+ console.log('[Monitor] Resource error handler installed');
681
745
  }
682
746
 
683
747
  /**
@@ -694,6 +758,7 @@ const originalFetch = window.fetch;
694
758
  function installHttpErrorHandler() {
695
759
  interceptXHR();
696
760
  interceptFetch();
761
+ console.log('[Monitor] HTTP error handler installed');
697
762
  }
698
763
  /**
699
764
  * 检查是否是 SDK 自身的请求
@@ -706,18 +771,6 @@ function isSdkRequest(url) {
706
771
  return false;
707
772
  return url.includes(cfg.dsn);
708
773
  }
709
- /**
710
- * 检查 HTTP 错误监控是否启用
711
- */
712
- function isHttpErrorEnabled() {
713
- if (!config.isInitialized())
714
- return true; // 配置未就绪时默认启用,数据会被缓存
715
- const cfg = config.get();
716
- return cfg.error?.enabled !== false && cfg.error?.httpError !== false;
717
- }
718
- /**
719
- * 拦截 XMLHttpRequest
720
- */
721
774
  function interceptXHR() {
722
775
  XMLHttpRequest.prototype.open = function (method, url, async = true, username, password) {
723
776
  const urlStr = url.toString();
@@ -747,16 +800,11 @@ function interceptXHR() {
747
800
  this.addEventListener('loadend', function () {
748
801
  if (!monitorData)
749
802
  return;
750
- if (!isHttpErrorEnabled())
751
- return;
752
803
  const duration = Date.now() - monitorData.startTime;
753
804
  const status = this.status;
754
805
  // 根据配置决定是否添加到面包屑
755
- let recordMode = 'error';
756
- if (config.isInitialized()) {
757
- const cfg = config.get();
758
- recordMode = cfg.behavior?.recordRequestBreadcrumb || 'error';
759
- }
806
+ const cfg = config.isInitialized() ? config.get() : null;
807
+ const recordMode = cfg?.behavior?.recordRequestBreadcrumb || 'error';
760
808
  // 重定向(3xx)始终记录,不受配置控制
761
809
  const isRedirect = status >= 300 && status < 400;
762
810
  // 其他异常情况(0-网络错误、4xx-客户端错误、5xx-服务端错误)根据配置控制
@@ -803,17 +851,11 @@ function interceptFetch() {
803
851
  const requestBody = typeof init?.body === 'string' ? init.body : undefined;
804
852
  try {
805
853
  const response = await originalFetch.call(window, input, init);
806
- if (!isHttpErrorEnabled()) {
807
- return response;
808
- }
809
854
  const duration = Date.now() - startTime;
810
855
  const status = response.status;
811
856
  // 根据配置决定是否添加到面包屑
812
- let recordMode = 'error';
813
- if (config.isInitialized()) {
814
- const cfg = config.get();
815
- recordMode = cfg.behavior?.recordRequestBreadcrumb || 'error';
816
- }
857
+ const cfg = config.isInitialized() ? config.get() : null;
858
+ const recordMode = cfg?.behavior?.recordRequestBreadcrumb || 'error';
817
859
  // 重定向(3xx)始终记录,不受配置控制
818
860
  const isRedirect = status >= 300 && status < 400;
819
861
  // 其他异常情况根据配置控制
@@ -852,16 +894,10 @@ function interceptFetch() {
852
894
  return response;
853
895
  }
854
896
  catch (error) {
855
- if (!isHttpErrorEnabled()) {
856
- throw error;
857
- }
858
897
  const duration = Date.now() - startTime;
859
898
  // 网络错误时根据配置决定是否添加到面包屑
860
- let recordMode = 'error';
861
- if (config.isInitialized()) {
862
- const cfg = config.get();
863
- recordMode = cfg.behavior?.recordRequestBreadcrumb || 'error';
864
- }
899
+ const cfg = config.isInitialized() ? config.get() : null;
900
+ const recordMode = cfg?.behavior?.recordRequestBreadcrumb || 'error';
865
901
  // 网络错误属于 error,当模式为 'all' 或 'error' 时都记录
866
902
  if (recordMode !== 'none') {
867
903
  context.addBreadcrumb({
@@ -899,10 +935,6 @@ function reportHttpError(httpInfo) {
899
935
  reporter.reportError(errorData);
900
936
  }
901
937
 
902
- /**
903
- * 白屏检测插件
904
- * 使用 DOM 采样检测页面是否白屏
905
- */
906
938
  // 采样点 - 页面关键区域
907
939
  const SAMPLE_POINTS = [
908
940
  { x: 0.5, y: 0.1 }, // 顶部中间
@@ -926,6 +958,7 @@ function installWhiteScreenDetector() {
926
958
  scheduleDetection();
927
959
  });
928
960
  }
961
+ console.log('[Monitor] White screen detector installed');
929
962
  }
930
963
  /**
931
964
  * 调度检测(延迟执行,给页面渲染时间)
@@ -933,13 +966,6 @@ function installWhiteScreenDetector() {
933
966
  function scheduleDetection() {
934
967
  // 延迟 1 秒检测
935
968
  setTimeout(() => {
936
- // 检测时才检查配置
937
- if (config.isInitialized()) {
938
- const cfg = config.get();
939
- if (!cfg.error?.enabled) {
940
- return;
941
- }
942
- }
943
969
  const isWhiteScreen = detectWhiteScreen();
944
970
  if (isWhiteScreen) {
945
971
  reportWhiteScreen();
@@ -1004,10 +1030,6 @@ function installErrorHandlers() {
1004
1030
  * 安装 Web Vitals 采集
1005
1031
  */
1006
1032
  function installWebVitals() {
1007
- const cfg = config.get();
1008
- if (!cfg.performance?.enabled || !cfg.performance?.webVitals) {
1009
- return;
1010
- }
1011
1033
  // 页面加载完成后采集
1012
1034
  if (document.readyState === 'complete') {
1013
1035
  collectMetrics();
@@ -1018,14 +1040,17 @@ function installWebVitals() {
1018
1040
  setTimeout(collectMetrics, 3000);
1019
1041
  });
1020
1042
  }
1021
- if (cfg.debug) {
1022
- console.log('[Monitor] Web Vitals collector installed');
1023
- }
1043
+ console.log('[Monitor] Web Vitals collector installed');
1024
1044
  }
1025
1045
  /**
1026
1046
  * 采集性能指标
1027
1047
  */
1028
1048
  function collectMetrics() {
1049
+ // 检查配置(在采集时检查,而不是安装时)
1050
+ const cfg = config.isInitialized() ? config.get() : null;
1051
+ if (cfg?.performance?.enabled === false || cfg?.performance?.webVitals === false) {
1052
+ return;
1053
+ }
1029
1054
  const metrics = {};
1030
1055
  // 使用 Performance API
1031
1056
  if (!window.performance) {
@@ -1134,22 +1159,13 @@ function installPerformanceMonitor() {
1134
1159
  installWebVitals();
1135
1160
  }
1136
1161
 
1137
- /**
1138
- * PV (Page View) 统计插件
1139
- */
1140
1162
  /**
1141
1163
  * 安装 PV 统计
1142
1164
  */
1143
1165
  function installPVTracker() {
1144
- const cfg = config.get();
1145
- if (!cfg.behavior?.enabled || !cfg.behavior?.pv) {
1146
- return;
1147
- }
1148
- // 页面加载时上报
1166
+ // 页面加载时上报(reporter 会缓存直到 SDK 就绪)
1149
1167
  reportPV();
1150
- if (cfg.debug) {
1151
- console.log('[Monitor] PV tracker installed');
1152
- }
1168
+ console.log('[Monitor] PV tracker installed');
1153
1169
  }
1154
1170
  /**
1155
1171
  * 上报 PV
@@ -1172,11 +1188,12 @@ function reportPV() {
1172
1188
  * 安装点击追踪
1173
1189
  */
1174
1190
  function installClickTracker() {
1175
- const cfg = config.get();
1176
- if (!cfg.behavior?.enabled || !cfg.behavior?.click) {
1177
- return;
1178
- }
1179
1191
  document.addEventListener('click', (event) => {
1192
+ // 检查配置(在事件触发时检查,而不是安装时)
1193
+ const cfg = config.isInitialized() ? config.get() : null;
1194
+ if (cfg?.behavior?.enabled === false || cfg?.behavior?.click === false) {
1195
+ return;
1196
+ }
1180
1197
  const target = event.target;
1181
1198
  if (!target)
1182
1199
  return;
@@ -1194,9 +1211,7 @@ function installClickTracker() {
1194
1211
  // data: clickData,
1195
1212
  // });
1196
1213
  }, true);
1197
- if (cfg.debug) {
1198
- console.log('[Monitor] Click tracker installed');
1199
- }
1214
+ console.log('[Monitor] Click tracker installed');
1200
1215
  }
1201
1216
  /**
1202
1217
  * 提取点击数据
@@ -1275,10 +1290,6 @@ const originalReplaceState = history.replaceState;
1275
1290
  * 安装路由监控
1276
1291
  */
1277
1292
  function installRouteTracker() {
1278
- const cfg = config.get();
1279
- if (!cfg.behavior?.enabled || !cfg.behavior?.route) {
1280
- return;
1281
- }
1282
1293
  // 拦截 pushState
1283
1294
  history.pushState = function (...args) {
1284
1295
  const result = originalPushState.apply(this, args);
@@ -1299,14 +1310,17 @@ function installRouteTracker() {
1299
1310
  window.addEventListener('hashchange', () => {
1300
1311
  handleRouteChange('hashchange');
1301
1312
  });
1302
- if (cfg.debug) {
1303
- console.log('[Monitor] Route tracker installed');
1304
- }
1313
+ console.log('[Monitor] Route tracker installed');
1305
1314
  }
1306
1315
  /**
1307
1316
  * 处理路由变化
1308
1317
  */
1309
1318
  function handleRouteChange(trigger) {
1319
+ // 检查配置(在事件触发时检查,而不是安装时)
1320
+ const cfg = config.isInitialized() ? config.get() : null;
1321
+ if (cfg?.behavior?.enabled === false || cfg?.behavior?.route === false) {
1322
+ return;
1323
+ }
1310
1324
  const routeData = {
1311
1325
  url: getPageUrl(),
1312
1326
  title: getPageTitle(),
@@ -1341,16 +1355,16 @@ const originalConsole = {
1341
1355
  * 安装控制台追踪
1342
1356
  */
1343
1357
  function installConsoleTracker() {
1344
- const cfg = config.get();
1345
- // 默认启用控制台追踪(跟随 behavior.enabled)
1346
- if (!cfg.behavior?.enabled) {
1347
- return;
1348
- }
1349
1358
  const levels = ['log', 'info', 'warn', 'error'];
1350
1359
  levels.forEach((level) => {
1351
1360
  console[level] = function (...args) {
1352
1361
  // 调用原始方法
1353
1362
  originalConsole[level].apply(console, args);
1363
+ // 检查配置(在调用时检查,而不是安装时)
1364
+ const cfg = config.isInitialized() ? config.get() : null;
1365
+ if (cfg?.behavior?.enabled === false) {
1366
+ return;
1367
+ }
1354
1368
  // 添加到面包屑(只记录 warn 和 error)
1355
1369
  if (level === 'warn' || level === 'error') {
1356
1370
  const message = formatConsoleArgs(args);
@@ -1365,9 +1379,7 @@ function installConsoleTracker() {
1365
1379
  }
1366
1380
  };
1367
1381
  });
1368
- if (cfg.debug) {
1369
- originalConsole.log('[Monitor] Console tracker installed');
1370
- }
1382
+ originalConsole.log('[Monitor] Console tracker installed');
1371
1383
  }
1372
1384
  /**
1373
1385
  * 格式化控制台参数
@@ -1399,24 +1411,22 @@ function formatConsoleArgs(args) {
1399
1411
  * 安装输入追踪
1400
1412
  */
1401
1413
  function installInputTracker() {
1402
- const cfg = config.get();
1403
- // 默认启用输入追踪(跟随 behavior.enabled)
1404
- if (!cfg.behavior?.enabled) {
1405
- return;
1406
- }
1407
1414
  // 监听 input 事件(使用 change 事件减少数据量)
1408
1415
  document.addEventListener('change', handleInputChange, true);
1409
1416
  // 监听焦点事件
1410
1417
  document.addEventListener('focus', handleFocus, true);
1411
1418
  document.addEventListener('blur', handleBlur, true);
1412
- if (cfg.debug) {
1413
- console.log('[Monitor] Input tracker installed');
1414
- }
1419
+ console.log('[Monitor] Input tracker installed');
1415
1420
  }
1416
1421
  /**
1417
1422
  * 处理输入变化
1418
1423
  */
1419
1424
  function handleInputChange(event) {
1425
+ // 检查配置(在事件触发时检查,而不是安装时)
1426
+ const cfg = config.isInitialized() ? config.get() : null;
1427
+ if (cfg?.behavior?.enabled === false) {
1428
+ return;
1429
+ }
1420
1430
  const target = event.target;
1421
1431
  if (!target || !isFormElement(target))
1422
1432
  return;
@@ -1431,6 +1441,11 @@ function handleInputChange(event) {
1431
1441
  * 处理焦点获取
1432
1442
  */
1433
1443
  function handleFocus(event) {
1444
+ // 检查配置
1445
+ const cfg = config.isInitialized() ? config.get() : null;
1446
+ if (cfg?.behavior?.enabled === false) {
1447
+ return;
1448
+ }
1434
1449
  const target = event.target;
1435
1450
  if (!target || !isFormElement(target))
1436
1451
  return;
@@ -1444,6 +1459,11 @@ function handleFocus(event) {
1444
1459
  * 处理焦点失去
1445
1460
  */
1446
1461
  function handleBlur(event) {
1462
+ // 检查配置
1463
+ const cfg = config.isInitialized() ? config.get() : null;
1464
+ if (cfg?.behavior?.enabled === false) {
1465
+ return;
1466
+ }
1447
1467
  const target = event.target;
1448
1468
  if (!target || !isFormElement(target))
1449
1469
  return;