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