sobey-monitor-sdk 1.0.7 → 1.0.9

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 CHANGED
@@ -115,14 +115,29 @@ ReactDOM.render(
115
115
  |--------|------|--------|------|
116
116
  | `appId` | string | 必填 | 应用唯一标识 |
117
117
  | `dsn` | string | 必填 | 数据上报地址 |
118
- | `debug` | boolean | false | 调试模式 |
118
+ | `enabled` | boolean | true | **全局开关**,设为 false 则完全禁用所有监控上报 |
119
+ | `debug` | boolean | false | 调试模式,开启后会在控制台输出日志 |
119
120
  | `sampling.error` | number | 1 | 错误采样率 (0-1) |
120
121
  | `sampling.performance` | number | 1 | 性能采样率 (0-1) |
121
122
  | `sampling.behavior` | number | 1 | 行为采样率 (0-1) |
122
- | `behavior.maxBreadcrumbs` | number | 20 | 行为回溯最大记录数 |
123
- | `behavior.recordRequestBreadcrumb` | 'all' \| 'error' \| 'none' | 'error' | 网络请求记录模式:'all' 记录所有、'error' 记录非 2xx 响应(重定向、4xx、5xx、网络错误)、'none' 不记录 |
124
- | `report.maxBufferSize` | number | 10 | 缓冲区最大数量 |
123
+ | `report.maxBufferSize` | number | 10 | 缓冲区最大数量,达到后立即上报 |
125
124
  | `report.flushInterval` | number | 5000 | 上报间隔 (ms) |
125
+ | `report.timeout` | number | 10000 | 请求超时 (ms) |
126
+ | `error.enabled` | boolean | true | 是否启用错误监控 |
127
+ | `error.jsError` | boolean | true | 是否捕获 JS 运行时错误 |
128
+ | `error.promiseError` | boolean | true | 是否捕获 Promise 异常 |
129
+ | `error.resourceError` | boolean | true | 是否捕获资源加载错误 |
130
+ | `error.httpError` | boolean | true | 是否捕获 HTTP 请求错误 |
131
+ | `performance.enabled` | boolean | true | 是否启用性能监控 |
132
+ | `performance.webVitals` | boolean | true | 是否采集 Web Vitals 指标 |
133
+ | `performance.resource` | boolean | true | 是否采集资源加载性能 |
134
+ | `performance.api` | boolean | true | 是否采集接口性能 |
135
+ | `behavior.enabled` | boolean | true | 是否启用行为监控 |
136
+ | `behavior.pv` | boolean | true | 是否采集页面访问 (PV) |
137
+ | `behavior.click` | boolean | true | 是否采集点击事件 |
138
+ | `behavior.route` | boolean | true | 是否采集路由变化 |
139
+ | `behavior.maxBreadcrumbs` | number | 20 | 行为回溯最大记录数 |
140
+ | `behavior.recordRequestBreadcrumb` | 'all' \| 'error' \| 'none' | 'error' | 网络请求记录模式:'all' 记录所有、'error' 记录失败请求、'none' 不记录 |
126
141
 
127
142
  ## 产物清单
128
143
 
@@ -9,8 +9,9 @@ declare class ConfigManager {
9
9
  private config;
10
10
  /**
11
11
  * 初始化配置
12
+ * @param skipValidation 是否跳过必填校验(用于远程配置已获取 appId/dsn 的场景)
12
13
  */
13
- init(userConfig: MonitorConfig): void;
14
+ init(userConfig: MonitorConfig, skipValidation?: boolean): void;
14
15
  /**
15
16
  * 获取配置
16
17
  */
package/dist/index.cjs.js CHANGED
@@ -49,13 +49,28 @@ class ConfigManager {
49
49
  }
50
50
  /**
51
51
  * 初始化配置
52
+ * @param skipValidation 是否跳过必填校验(用于远程配置已获取 appId/dsn 的场景)
52
53
  */
53
- init(userConfig) {
54
- if (!userConfig.appId) {
55
- throw new Error('[Monitor] appId is required');
54
+ init(userConfig, skipValidation = false) {
55
+ if (!skipValidation) {
56
+ // 如果没有 configUrl,则 appId dsn 是必填的
57
+ if (!userConfig.configUrl) {
58
+ if (!userConfig.appId) {
59
+ throw new Error('[Monitor] appId is required when configUrl is not provided');
60
+ }
61
+ if (!userConfig.dsn) {
62
+ throw new Error('[Monitor] dsn is required when configUrl is not provided');
63
+ }
64
+ }
56
65
  }
57
- if (!userConfig.dsn) {
58
- throw new Error('[Monitor] dsn is required');
66
+ else {
67
+ // 远程配置模式下,校验合并后的配置
68
+ if (!userConfig.appId) {
69
+ throw new Error('[Monitor] appId is required (not found in remote config)');
70
+ }
71
+ if (!userConfig.dsn) {
72
+ throw new Error('[Monitor] dsn is required (not found in remote config)');
73
+ }
59
74
  }
60
75
  this.config = this.mergeConfig(DEFAULT_CONFIG, userConfig);
61
76
  }
@@ -1635,17 +1650,34 @@ class MonitorSDK {
1635
1650
  }
1636
1651
  /**
1637
1652
  * 初始化 SDK
1653
+ * 如果配置了 configUrl,将先从远程获取配置(仅获取一次)
1638
1654
  */
1639
- init(userConfig) {
1655
+ async init(userConfig) {
1640
1656
  if (this.initialized) {
1641
1657
  console.warn('[Monitor] SDK already initialized');
1642
1658
  return;
1643
1659
  }
1644
- // 初始化配置
1660
+ let finalConfig = { ...userConfig };
1661
+ // 如果配置了 configUrl,从远程获取配置
1662
+ if (userConfig.configUrl) {
1663
+ try {
1664
+ const remoteConfig = await this.fetchRemoteConfig(userConfig.configUrl);
1665
+ // 远程配置作为基础,用户本地配置覆盖远程配置
1666
+ finalConfig = this.mergeUserConfig(remoteConfig, userConfig);
1667
+ if (userConfig.debug) {
1668
+ console.log('[Monitor] Remote config loaded:', remoteConfig);
1669
+ }
1670
+ }
1671
+ catch (error) {
1672
+ console.error('[Monitor] Failed to fetch remote config:', error);
1673
+ throw new Error('[Monitor] Failed to fetch remote config. Please check configUrl.');
1674
+ }
1675
+ }
1676
+ // 初始化配置(远程配置模式下跳过初始校验,因为已经合并了远程配置)
1645
1677
  config.init({
1646
- ...userConfig,
1647
- version: userConfig.version || SDK_VERSION,
1648
- });
1678
+ ...finalConfig,
1679
+ version: finalConfig.version || SDK_VERSION,
1680
+ }, !!userConfig.configUrl);
1649
1681
  // 初始化上下文
1650
1682
  const maxBreadcrumbs = config.get().behavior?.maxBreadcrumbs || 20;
1651
1683
  context.init(maxBreadcrumbs);
@@ -1658,6 +1690,50 @@ class MonitorSDK {
1658
1690
  installPerformanceMonitor();
1659
1691
  installBehaviorMonitor();
1660
1692
  }
1693
+ /**
1694
+ * 从远程获取配置
1695
+ */
1696
+ async fetchRemoteConfig(configUrl) {
1697
+ const response = await fetch(configUrl, {
1698
+ method: 'GET',
1699
+ headers: {
1700
+ 'Content-Type': 'application/json',
1701
+ },
1702
+ });
1703
+ if (!response.ok) {
1704
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
1705
+ }
1706
+ return response.json();
1707
+ }
1708
+ /**
1709
+ * 合并配置:远程配置为基础,用户本地配置优先
1710
+ */
1711
+ mergeUserConfig(remote, local) {
1712
+ const result = { ...remote };
1713
+ for (const key in local) {
1714
+ if (Object.prototype.hasOwnProperty.call(local, key)) {
1715
+ const localValue = local[key];
1716
+ const remoteValue = remote[key];
1717
+ // 跳过 configUrl,不需要传递给最终配置
1718
+ if (key === 'configUrl')
1719
+ continue;
1720
+ if (localValue !== null &&
1721
+ localValue !== undefined &&
1722
+ typeof localValue === 'object' &&
1723
+ !Array.isArray(localValue) &&
1724
+ remoteValue !== null &&
1725
+ remoteValue !== undefined &&
1726
+ typeof remoteValue === 'object' &&
1727
+ !Array.isArray(remoteValue)) {
1728
+ result[key] = { ...remoteValue, ...localValue };
1729
+ }
1730
+ else if (localValue !== undefined) {
1731
+ result[key] = localValue;
1732
+ }
1733
+ }
1734
+ }
1735
+ return result;
1736
+ }
1661
1737
  /**
1662
1738
  * 设置用户信息
1663
1739
  */