@arms/rum-core 0.1.7 → 0.1.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/es/utils/base.js CHANGED
@@ -1,4 +1,5 @@
1
- import { isFunction } from "./is";
1
+ import { isFunction } from './is';
2
+ import { logger } from '../monitor/logger';
2
3
 
3
4
  /**
4
5
  * 劫持函数或原型
@@ -7,19 +8,74 @@ export function interceptFunction(target, name, callback, isPrototype) {
7
8
  if (isPrototype === void 0) {
8
9
  isPrototype = false;
9
10
  }
10
- var registeredMethod = target[name];
11
- var proxyMethod = function proxyMethod() {
12
- var ctx = isPrototype ? this : target;
13
- for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
14
- args[_key] = arguments[_key];
11
+ try {
12
+ var registeredMethod = target[name];
13
+ var proxyMethod = function proxyMethod() {
14
+ var ctx = isPrototype ? this : target;
15
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
16
+ args[_key] = arguments[_key];
17
+ }
18
+ try {
19
+ // callback 异常隔离:SDK 采集逻辑出错不能中断客户原函数执行
20
+ callback.apply(ctx, args);
21
+ } catch (e) {
22
+ logger.warn('interceptFunction', "callback error in \"" + name + "\"", e);
23
+ }
24
+ if (isFunction(registeredMethod)) {
25
+ return registeredMethod.apply(ctx, args);
26
+ }
27
+ };
28
+ disguiseProxy(proxyMethod, registeredMethod);
29
+ target[name] = proxyMethod;
30
+ } catch (e) {
31
+ // 劫持安装保护:target 属性只读 accessor 或被 freeze 时(strict mode 下抛 TypeError),
32
+ // 上报后静默返回,不中断 SDK 初始化
33
+ logger.warn('interceptFunction', "install proxy failed for \"" + name + "\"", e);
34
+ }
35
+ }
36
+
37
+ /**
38
+ * 伪装代理函数,降低劫持对客户代码的感知:
39
+ * 1. toString 返回原函数源码(String()、模板字符串、console.log、日志序列化等路径可见原函数)
40
+ * 2. name/length 与原函数对齐
41
+ * 3. _rum_intercepted 不可枚举,避免污染 Object.keys / for...in
42
+ * 注意:defineProperty 在部分小程序运行时可能失败,需降级为直接挂载,不能中断劫持主流程
43
+ *
44
+ */
45
+ function disguiseProxy(proxyMethod, originalMethod) {
46
+ try {
47
+ if (isFunction(originalMethod)) {
48
+ Object.defineProperty(proxyMethod, 'toString', {
49
+ value: function value() {
50
+ return originalMethod.toString();
51
+ },
52
+ writable: true,
53
+ configurable: true
54
+ });
55
+ Object.defineProperty(proxyMethod, 'name', {
56
+ value: originalMethod.name,
57
+ configurable: true
58
+ });
59
+ Object.defineProperty(proxyMethod, 'length', {
60
+ value: originalMethod.length,
61
+ configurable: true
62
+ });
15
63
  }
16
- callback.apply(ctx, args);
17
- if (isFunction(registeredMethod)) {
18
- return registeredMethod.apply(ctx, args);
64
+ Object.defineProperty(proxyMethod, '_rum_intercepted', {
65
+ value: originalMethod,
66
+ enumerable: false,
67
+ writable: true,
68
+ configurable: true
69
+ });
70
+ } catch (e) {
71
+ // 降级:defineProperty 不可用时直接挂载,保证 restoreFunction 仍可用
72
+ try {
73
+ proxyMethod._rum_intercepted = originalMethod;
74
+ } catch (err) {
75
+ // 二层保护:strict mode 下对不可扩展对象直接赋值也会抛 TypeError,上报后放弃挂载
76
+ logger.warn('interceptFunction', 'disguiseProxy fallback failed', err);
19
77
  }
20
- };
21
- proxyMethod._rum_intercepted = registeredMethod;
22
- target[name] = proxyMethod;
78
+ }
23
79
  }
24
80
 
25
81
  /**
@@ -147,72 +203,6 @@ export function delay(func, wait) {
147
203
  return setTimeout.apply(void 0, [func, +wait || 0].concat(args));
148
204
  }
149
205
 
150
- // // 类型匹配正则
151
- // const TYPE_REG = /^\[object ([a-z]*)\]$/;
152
- // /**
153
- // * @desc 需要严格区分object和array
154
- // * @param obj 任意对象
155
- // */
156
- // export function getType(obj: any) {
157
- // let type = Object.prototype.toString.call(obj);
158
- // type = type.toLowerCase() || '';
159
- // const arr = type.match(TYPE_REG);
160
- // return arr?.[1];
161
- // }
162
- //
163
- // /**
164
- // * @desc 将字符串转换成正则表达式
165
- // * @param str 任意字符串
166
- // * 注意,这里只支持 /xxx/ 这种格式,不支持修饰符,例如 /xxx/ig
167
- // */
168
- // export function transStrToReg(str: string) {
169
- // if (getType(str) !== 'string') return str;
170
- // if (str.length > 2 && str[0] === '/' && str[str.length - 1] === '/') {
171
- // return new RegExp(str.substr(1, str.length - 2));
172
- // }
173
- // return str;
174
- // }
175
- //
176
- // /**
177
- // *
178
- // * @param config 配置
179
- // * @param keys 需要转换的字段
180
- // */
181
- // export function toRegFormat(config: any, keys: any[]) {
182
- // if (getType(keys) === 'string') {
183
- // keys = [keys];
184
- // }
185
- // for (let i = 0, len = keys.length; i < len; i++) {
186
- // const paths = keys[i].split('.');
187
- // const lastIndex = paths.length - 1;
188
- // if (!config) break;
189
- // let tmp = config;
190
- // for (let j = 0, jlen = paths.length; j < jlen; j++) {
191
- // if (paths[j] === '[]') {
192
- // if (getType(tmp) === 'array') {
193
- // let lastPath = paths.splice(j + 1);
194
- // lastPath = lastPath.join('.');
195
- // for (let x = 0, xlen = tmp.length; x < xlen; x++) {
196
- // toRegFormat(tmp[x], lastPath);
197
- // }
198
- // }
199
- // break;
200
- // }
201
- // // 最后一层了,需要往前一层才能修改对象
202
- // if (lastIndex === j && getType(tmp[paths[j]]) === 'string') {
203
- // tmp[paths[j]] = transStrToReg(tmp[paths[j]]);
204
- // break;
205
- // }
206
- // tmp = tmp[paths[j]];
207
- // if (!tmp) break;
208
- // }
209
- // if (!tmp || getType(tmp) !== 'array') continue;
210
- // for (let j = 0, jlen = tmp.length; j < jlen; j++) {
211
- // tmp[j] = transStrToReg(tmp[j]);
212
- // }
213
- // }
214
- // }
215
-
216
206
  /**
217
207
  * 解析正则表达式字符串,支持 /pattern/flags 格式
218
208
  * @param str 正则表达式字符串,如 "/a/ig" 或 "a"
package/lib/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
 
3
- var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
3
+ function _interopRequireDefault(e){return e&&e.__esModule?e:{"default":e}}
4
4
  exports.__esModule = true;
5
5
  var _exportNames = {
6
6
  Client: true,
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
 
3
- var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
3
+ function _interopRequireDefault(e){return e&&e.__esModule?e:{"default":e}}
4
4
  exports.__esModule = true;
5
5
  exports["default"] = void 0;
6
6
  var _client = require("../types/client");
@@ -73,14 +73,16 @@ var Client = /*#__PURE__*/function () {
73
73
  }
74
74
  if (processor.match(ctx)) {
75
75
  var res = processor.process(ctx);
76
- if (res) {
77
- ctx.setRumEvent(res);
78
- }
76
+ // 无论返回值是否为 falsy 都写回 context,
77
+ // 确保 processor 返回 null/undefined(丢弃事件)时能正确生效
78
+ ctx.setRumEvent(res);
79
79
  }
80
80
  }
81
81
  reporter.report(ctx, options);
82
82
  });
83
- collectors.forEach(function (collector) {
83
+
84
+ // 采集器可全部经 Shell 扩展通道注册,此处允许为空
85
+ (collectors || []).forEach(function (collector) {
84
86
  collector.setup(ctx, _this2.sendEvent);
85
87
  });
86
88
  };
@@ -21,7 +21,7 @@ export declare abstract class ConfigManager implements IConfigManager {
21
21
  * 更新配置
22
22
  * @param config 新配置
23
23
  */
24
- setConfig(config: Partial<IConfiguration>): Promise<void>;
24
+ setConfig(config: Partial<IConfiguration>): void;
25
25
  /**
26
26
  * 检查缓存是否有效
27
27
  * @param cachedData 缓存数据
@@ -1,11 +1,7 @@
1
1
  "use strict";
2
2
 
3
- var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
3
  exports.__esModule = true;
5
4
  exports.ConfigManager = void 0;
6
- var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
7
- var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
8
- var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
9
5
  function parseRemoteConfig(config) {
10
6
  var remoteConfig = config.remoteConfig || {};
11
7
  var enable = false;
@@ -38,75 +34,46 @@ var ConfigManager = exports.ConfigManager = /*#__PURE__*/function () {
38
34
  * 初始化配置管理器
39
35
  * @param config 初始配置
40
36
  */
41
- _proto.init =
42
- /*#__PURE__*/
43
- function () {
44
- var _init = (0, _asyncToGenerator2["default"])(/*#__PURE__*/_regenerator["default"].mark(function _callee(config) {
45
- var _this = this;
46
- var remoteConfig, cacheConfig, asyncConfigPromise;
47
- return _regenerator["default"].wrap(function (_context) {
48
- while (1) switch (_context.prev = _context.next) {
49
- case 0:
50
- if (!this.initialized) {
51
- _context.next = 1;
52
- break;
53
- }
54
- return _context.abrupt("return");
55
- case 1:
56
- this.currentConfig = (0, _extends2["default"])({}, config);
57
- this.initialized = true;
58
- remoteConfig = parseRemoteConfig(config);
59
- this.currentConfig.remoteConfig = remoteConfig;
60
- if (remoteConfig.enable) {
61
- _context.next = 2;
62
- break;
63
- }
64
- return _context.abrupt("return");
65
- case 2:
66
- _context.next = 3;
67
- return this.getCacheConfig();
68
- case 3:
69
- cacheConfig = _context.sent;
70
- if (cacheConfig && cacheConfig.content) {
71
- this.currentConfig = this.mergeRemoteCfg(cacheConfig.content);
72
- }
73
- if (!this.isCacheValid(cacheConfig, remoteConfig.cacheTimeout)) {
74
- _context.next = 4;
75
- break;
76
- }
77
- return _context.abrupt("return");
78
- case 4:
79
- asyncConfigPromise = this.fetchRemoteCfg(this.currentConfig).then(function (resp) {
80
- if (!resp) return;
81
- var asyncRemoteConfig = _this.parseConfig(resp);
82
- _this.currentConfig = _this.mergeRemoteCfg(asyncRemoteConfig);
83
- _this.setCacheConfig({
84
- timestamp: Date.now(),
85
- content: resp
86
- });
87
- });
88
- if (!(remoteConfig.mode === 'remote-first')) {
89
- _context.next = 5;
90
- break;
91
- }
92
- _context.next = 5;
93
- return asyncConfigPromise;
94
- case 5:
95
- case "end":
96
- return _context.stop();
97
- }
98
- }, _callee, this);
99
- }));
100
- function init(_x) {
101
- return _init.apply(this, arguments);
37
+ _proto.init = function init(config) {
38
+ var _this = this;
39
+ if (this.initialized) {
40
+ return Promise.resolve();
102
41
  }
103
- return init;
104
- }()
42
+ this.currentConfig = Object.assign({}, config);
43
+ this.initialized = true;
44
+ var remoteConfig = parseRemoteConfig(config);
45
+ this.currentConfig.remoteConfig = remoteConfig;
46
+ if (!remoteConfig.enable) {
47
+ return Promise.resolve();
48
+ }
49
+ return Promise.resolve(this.getCacheConfig()).then(function (cacheConfig) {
50
+ if (cacheConfig && cacheConfig.content) {
51
+ _this.currentConfig = _this.mergeRemoteCfg(cacheConfig.content);
52
+ }
53
+ if (_this.isCacheValid(cacheConfig, remoteConfig.cacheTimeout)) {
54
+ // 配置有效期内直接返回,不抓取远端配置
55
+ return;
56
+ }
57
+ var asyncConfigPromise = _this.fetchRemoteCfg(_this.currentConfig).then(function (resp) {
58
+ if (!resp) return;
59
+ var asyncRemoteConfig = _this.parseConfig(resp);
60
+ _this.currentConfig = _this.mergeRemoteCfg(asyncRemoteConfig);
61
+ _this.setCacheConfig({
62
+ timestamp: Date.now(),
63
+ content: resp
64
+ });
65
+ });
66
+ if (remoteConfig.mode === 'remote-first') {
67
+ // 远端配置优先情况下,要等待获取完成后返回
68
+ return asyncConfigPromise;
69
+ }
70
+ });
71
+ }
72
+
105
73
  /**
106
74
  * 获取配置
107
75
  * @returns 配置数据
108
- */
109
- ;
76
+ */;
110
77
  _proto.getConfig = function getConfig() {
111
78
  if (!this.currentConfig) {
112
79
  throw new Error('Config not initialized');
@@ -118,37 +85,16 @@ var ConfigManager = exports.ConfigManager = /*#__PURE__*/function () {
118
85
  * 更新配置
119
86
  * @param config 新配置
120
87
  */;
121
- _proto.setConfig =
122
- /*#__PURE__*/
123
- function () {
124
- var _setConfig = (0, _asyncToGenerator2["default"])(/*#__PURE__*/_regenerator["default"].mark(function _callee2(config) {
125
- return _regenerator["default"].wrap(function (_context2) {
126
- while (1) switch (_context2.prev = _context2.next) {
127
- case 0:
128
- if (this.currentConfig) {
129
- _context2.next = 1;
130
- break;
131
- }
132
- return _context2.abrupt("return");
133
- case 1:
134
- this.currentConfig = (0, _extends2["default"])({}, this.currentConfig, config);
135
- case 2:
136
- case "end":
137
- return _context2.stop();
138
- }
139
- }, _callee2, this);
140
- }));
141
- function setConfig(_x2) {
142
- return _setConfig.apply(this, arguments);
143
- }
144
- return setConfig;
145
- }()
88
+ _proto.setConfig = function setConfig(config) {
89
+ if (!this.currentConfig) return;
90
+ this.currentConfig = Object.assign({}, this.currentConfig, config);
91
+ }
92
+
146
93
  /**
147
94
  * 检查缓存是否有效
148
95
  * @param cachedData 缓存数据
149
96
  * @param cacheTimeout 缓存超时时间
150
- */
151
- ;
97
+ */;
152
98
  _proto.isCacheValid = function isCacheValid(cachedData, cacheTimeout) {
153
99
  if (!cachedData || !cachedData.timestamp) {
154
100
  return false;
@@ -170,7 +116,7 @@ var ConfigManager = exports.ConfigManager = /*#__PURE__*/function () {
170
116
  remoteConfig = _this$currentConfig.remoteConfig,
171
117
  beforeReport = _this$currentConfig.beforeReport,
172
118
  properties = _this$currentConfig.properties;
173
- return (0, _extends2["default"])({}, this.currentConfig, remoteCfg, {
119
+ return Object.assign({}, this.currentConfig, remoteCfg, {
174
120
  pid: pid,
175
121
  app: app,
176
122
  endpoint: endpoint,
@@ -1,11 +1,7 @@
1
1
  "use strict";
2
2
 
3
- var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
3
  exports.__esModule = true;
5
4
  exports["default"] = void 0;
6
- var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
7
- var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
8
- var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
9
5
  var _logger = require("../monitor/logger");
10
6
  var _rumEvent = require("../types/rum-event");
11
7
  var _exception = require("../utils/exception");
@@ -87,6 +83,7 @@ var Reporter = exports["default"] = /*#__PURE__*/function () {
87
83
  var sampled = session ? session.getSampled() : true;
88
84
  if (!sampled) return;
89
85
  var event = ctx.getRumEvent();
86
+ if (!event) return;
90
87
  var views = ctx.getViews();
91
88
  var curView = views[views.length - 1];
92
89
  if (((_event$view = event.view) === null || _event$view === void 0 ? void 0 : _event$view.id) === curView.id) {
@@ -98,6 +95,8 @@ var Reporter = exports["default"] = /*#__PURE__*/function () {
98
95
  var ctx = this.ctx,
99
96
  eventQueue = this.eventQueue;
100
97
  var event = ctx.getRumEvent();
98
+ // processor 返回 null/undefined 表示丢弃事件,直接跳过
99
+ if (!event) return;
101
100
 
102
101
  // 针对相同 event,做 times 合并
103
102
  // todo,支持其他类型 event 合并
@@ -209,7 +208,7 @@ var Reporter = exports["default"] = /*#__PURE__*/function () {
209
208
  */;
210
209
  _proto.buildAndSend = function buildAndSend(ctx, config, session, sessionId, events, view) {
211
210
  var extend = (0, _url.getUrlParams)(config.endpoint);
212
- var bundle = (0, _extends2["default"])({
211
+ var bundle = Object.assign({
213
212
  app: {
214
213
  id: config.pid,
215
214
  env: config.env || 'prod',
@@ -248,54 +247,32 @@ var Reporter = exports["default"] = /*#__PURE__*/function () {
248
247
  }
249
248
  this.tryRequest(bundle);
250
249
  };
251
- _proto.tryRequest = /*#__PURE__*/function () {
252
- var _tryRequest = (0, _asyncToGenerator2["default"])(/*#__PURE__*/_regenerator["default"].mark(function _callee(bundle, retry) {
253
- var _this$ctx$getConfig,
254
- _reportConfig$maxRetr,
255
- _reportConfig$retryDe,
256
- _this2 = this;
257
- var reportConfig, maxRetryCount, retryDelay, _t;
258
- return _regenerator["default"].wrap(function (_context) {
259
- while (1) switch (_context.prev = _context.next) {
260
- case 0:
261
- if (retry === void 0) {
262
- retry = 0;
263
- }
264
- reportConfig = (_this$ctx$getConfig = this.ctx.getConfig()) === null || _this$ctx$getConfig === void 0 ? void 0 : _this$ctx$getConfig.reportConfig;
265
- maxRetryCount = (_reportConfig$maxRetr = reportConfig === null || reportConfig === void 0 ? void 0 : reportConfig.maxRetryCount) !== null && _reportConfig$maxRetr !== void 0 ? _reportConfig$maxRetr : 0;
266
- retryDelay = (_reportConfig$retryDe = reportConfig === null || reportConfig === void 0 ? void 0 : reportConfig.retryDelay) !== null && _reportConfig$retryDe !== void 0 ? _reportConfig$retryDe : 3000;
267
- _context.prev = 1;
268
- bundle._retry = retry;
269
- _context.next = 2;
270
- return this.request(this.ctx, bundle);
271
- case 2:
272
- _context.next = 4;
273
- break;
274
- case 3:
275
- _context.prev = 3;
276
- _t = _context["catch"](1);
277
- if (retry < maxRetryCount - 1) {
278
- setTimeout(function () {
279
- return _this2.tryRequest(bundle, retry + 1);
280
- }, retryDelay);
281
- } else {
282
- _logger.logger.error('reporter', "Request failed after " + (retry + 1) + " attempts", _t);
283
- }
284
- case 4:
285
- case "end":
286
- return _context.stop();
287
- }
288
- }, _callee, this, [[1, 3]]);
289
- }));
290
- function tryRequest(_x, _x2) {
291
- return _tryRequest.apply(this, arguments);
250
+ _proto.tryRequest = function tryRequest(bundle, retry) {
251
+ var _this$ctx$getConfig,
252
+ _reportConfig$maxRetr,
253
+ _reportConfig$retryDe,
254
+ _this2 = this;
255
+ if (retry === void 0) {
256
+ retry = 0;
292
257
  }
293
- return tryRequest;
294
- }()
258
+ var reportConfig = (_this$ctx$getConfig = this.ctx.getConfig()) === null || _this$ctx$getConfig === void 0 ? void 0 : _this$ctx$getConfig.reportConfig;
259
+ var maxRetryCount = (_reportConfig$maxRetr = reportConfig === null || reportConfig === void 0 ? void 0 : reportConfig.maxRetryCount) !== null && _reportConfig$maxRetr !== void 0 ? _reportConfig$maxRetr : 0;
260
+ var retryDelay = (_reportConfig$retryDe = reportConfig === null || reportConfig === void 0 ? void 0 : reportConfig.retryDelay) !== null && _reportConfig$retryDe !== void 0 ? _reportConfig$retryDe : 3000;
261
+ bundle._retry = retry;
262
+ Promise.resolve(this.request(this.ctx, bundle))["catch"](function (e) {
263
+ if (retry < maxRetryCount - 1) {
264
+ setTimeout(function () {
265
+ return _this2.tryRequest(bundle, retry + 1);
266
+ }, retryDelay);
267
+ } else {
268
+ _logger.logger.error('reporter', "Request failed after " + (retry + 1) + " attempts", e);
269
+ }
270
+ });
271
+ }
272
+
295
273
  /**
296
274
  * 接口请求由各平台 sdk reporter 实现
297
- */
298
- ;
275
+ */;
299
276
  /**
300
277
  * 初始化时
301
278
  */
@@ -1,9 +1,31 @@
1
1
  import { IClient, IConfiguration } from '../types/client';
2
+ import { ICollector } from '../types/collector';
2
3
  import { RumCustomEvent, RumEvent, RumExceptionEvent, RumResourceEvent, RumViewEvent, SendEventOptions } from '../types/rum-event';
3
4
  import { IShell } from '../types/shell';
4
5
  export default abstract class Shell implements IShell {
5
6
  client: IClient;
7
+ protected initialized: boolean;
8
+ private extensionCollectors;
9
+ private static instances;
6
10
  constructor(config?: IConfiguration);
11
+ /**
12
+ * 注册扩展采集器(实例方法,支持 per-instance 隔离)
13
+ * 支持在 init 之前或之后调用;若已初始化则立即 setup
14
+ */
15
+ useCollectors(...collectors: ICollector[]): void;
16
+ /**
17
+ * 返回当前实例装载的全部采集器(client 内置通道 + 实例扩展通道)
18
+ */
19
+ getCollectors(): ICollector[];
20
+ /**
21
+ * 向所有已初始化的实例广播事件
22
+ */
23
+ static broadcastEvent(event: RumEvent, options?: SendEventOptions): void;
24
+ /**
25
+ * 统一初始化 Client,并在 init 完成后自动挂载扩展采集器。
26
+ * 由 client.init 拦截器自动调用,子包无需手动调用。
27
+ */
28
+ protected initClient(): void;
7
29
  /**
8
30
  * 初始化
9
31
  */