@arms/rum-core 0.1.7 → 0.1.8
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/model/client.js +3 -3
- package/es/model/reporter.js +3 -0
- package/es/utils/base.js +68 -78
- package/lib/model/client.js +3 -3
- package/lib/model/reporter.js +3 -0
- package/lib/utils/base.js +67 -77
- package/package.json +1 -1
package/es/model/client.js
CHANGED
|
@@ -68,9 +68,9 @@ var Client = /*#__PURE__*/function () {
|
|
|
68
68
|
}
|
|
69
69
|
if (processor.match(ctx)) {
|
|
70
70
|
var res = processor.process(ctx);
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
71
|
+
// 无论返回值是否为 falsy 都写回 context,
|
|
72
|
+
// 确保 processor 返回 null/undefined(丢弃事件)时能正确生效
|
|
73
|
+
ctx.setRumEvent(res);
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
76
|
reporter.report(ctx, options);
|
package/es/model/reporter.js
CHANGED
|
@@ -82,6 +82,7 @@ var Reporter = /*#__PURE__*/function () {
|
|
|
82
82
|
var sampled = session ? session.getSampled() : true;
|
|
83
83
|
if (!sampled) return;
|
|
84
84
|
var event = ctx.getRumEvent();
|
|
85
|
+
if (!event) return;
|
|
85
86
|
var views = ctx.getViews();
|
|
86
87
|
var curView = views[views.length - 1];
|
|
87
88
|
if (((_event$view = event.view) === null || _event$view === void 0 ? void 0 : _event$view.id) === curView.id) {
|
|
@@ -93,6 +94,8 @@ var Reporter = /*#__PURE__*/function () {
|
|
|
93
94
|
var ctx = this.ctx,
|
|
94
95
|
eventQueue = this.eventQueue;
|
|
95
96
|
var event = ctx.getRumEvent();
|
|
97
|
+
// processor 返回 null/undefined 表示丢弃事件,直接跳过
|
|
98
|
+
if (!event) return;
|
|
96
99
|
|
|
97
100
|
// 针对相同 event,做 times 合并
|
|
98
101
|
// todo,支持其他类型 event 合并
|
package/es/utils/base.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { isFunction } from
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
var
|
|
13
|
-
|
|
14
|
-
args
|
|
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
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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/model/client.js
CHANGED
|
@@ -73,9 +73,9 @@ var Client = /*#__PURE__*/function () {
|
|
|
73
73
|
}
|
|
74
74
|
if (processor.match(ctx)) {
|
|
75
75
|
var res = processor.process(ctx);
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
76
|
+
// 无论返回值是否为 falsy 都写回 context,
|
|
77
|
+
// 确保 processor 返回 null/undefined(丢弃事件)时能正确生效
|
|
78
|
+
ctx.setRumEvent(res);
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
81
|
reporter.report(ctx, options);
|
package/lib/model/reporter.js
CHANGED
|
@@ -87,6 +87,7 @@ var Reporter = exports["default"] = /*#__PURE__*/function () {
|
|
|
87
87
|
var sampled = session ? session.getSampled() : true;
|
|
88
88
|
if (!sampled) return;
|
|
89
89
|
var event = ctx.getRumEvent();
|
|
90
|
+
if (!event) return;
|
|
90
91
|
var views = ctx.getViews();
|
|
91
92
|
var curView = views[views.length - 1];
|
|
92
93
|
if (((_event$view = event.view) === null || _event$view === void 0 ? void 0 : _event$view.id) === curView.id) {
|
|
@@ -98,6 +99,8 @@ var Reporter = exports["default"] = /*#__PURE__*/function () {
|
|
|
98
99
|
var ctx = this.ctx,
|
|
99
100
|
eventQueue = this.eventQueue;
|
|
100
101
|
var event = ctx.getRumEvent();
|
|
102
|
+
// processor 返回 null/undefined 表示丢弃事件,直接跳过
|
|
103
|
+
if (!event) return;
|
|
101
104
|
|
|
102
105
|
// 针对相同 event,做 times 合并
|
|
103
106
|
// todo,支持其他类型 event 合并
|
package/lib/utils/base.js
CHANGED
|
@@ -11,6 +11,7 @@ exports.interceptFunction = interceptFunction;
|
|
|
11
11
|
exports.parseRegExpString = parseRegExpString;
|
|
12
12
|
exports.restoreFunction = restoreFunction;
|
|
13
13
|
var _is = require("./is");
|
|
14
|
+
var _logger = require("../monitor/logger");
|
|
14
15
|
/**
|
|
15
16
|
* 劫持函数或原型
|
|
16
17
|
*/
|
|
@@ -18,19 +19,74 @@ function interceptFunction(target, name, callback, isPrototype) {
|
|
|
18
19
|
if (isPrototype === void 0) {
|
|
19
20
|
isPrototype = false;
|
|
20
21
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
var
|
|
24
|
-
|
|
25
|
-
args
|
|
22
|
+
try {
|
|
23
|
+
var registeredMethod = target[name];
|
|
24
|
+
var proxyMethod = function proxyMethod() {
|
|
25
|
+
var ctx = isPrototype ? this : target;
|
|
26
|
+
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
|
|
27
|
+
args[_key] = arguments[_key];
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
// callback 异常隔离:SDK 采集逻辑出错不能中断客户原函数执行
|
|
31
|
+
callback.apply(ctx, args);
|
|
32
|
+
} catch (e) {
|
|
33
|
+
_logger.logger.warn('interceptFunction', "callback error in \"" + name + "\"", e);
|
|
34
|
+
}
|
|
35
|
+
if ((0, _is.isFunction)(registeredMethod)) {
|
|
36
|
+
return registeredMethod.apply(ctx, args);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
disguiseProxy(proxyMethod, registeredMethod);
|
|
40
|
+
target[name] = proxyMethod;
|
|
41
|
+
} catch (e) {
|
|
42
|
+
// 劫持安装保护:target 属性只读 accessor 或被 freeze 时(strict mode 下抛 TypeError),
|
|
43
|
+
// 上报后静默返回,不中断 SDK 初始化
|
|
44
|
+
_logger.logger.warn('interceptFunction', "install proxy failed for \"" + name + "\"", e);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* 伪装代理函数,降低劫持对客户代码的感知:
|
|
50
|
+
* 1. toString 返回原函数源码(String()、模板字符串、console.log、日志序列化等路径可见原函数)
|
|
51
|
+
* 2. name/length 与原函数对齐
|
|
52
|
+
* 3. _rum_intercepted 不可枚举,避免污染 Object.keys / for...in
|
|
53
|
+
* 注意:defineProperty 在部分小程序运行时可能失败,需降级为直接挂载,不能中断劫持主流程
|
|
54
|
+
*
|
|
55
|
+
*/
|
|
56
|
+
function disguiseProxy(proxyMethod, originalMethod) {
|
|
57
|
+
try {
|
|
58
|
+
if ((0, _is.isFunction)(originalMethod)) {
|
|
59
|
+
Object.defineProperty(proxyMethod, 'toString', {
|
|
60
|
+
value: function value() {
|
|
61
|
+
return originalMethod.toString();
|
|
62
|
+
},
|
|
63
|
+
writable: true,
|
|
64
|
+
configurable: true
|
|
65
|
+
});
|
|
66
|
+
Object.defineProperty(proxyMethod, 'name', {
|
|
67
|
+
value: originalMethod.name,
|
|
68
|
+
configurable: true
|
|
69
|
+
});
|
|
70
|
+
Object.defineProperty(proxyMethod, 'length', {
|
|
71
|
+
value: originalMethod.length,
|
|
72
|
+
configurable: true
|
|
73
|
+
});
|
|
26
74
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
75
|
+
Object.defineProperty(proxyMethod, '_rum_intercepted', {
|
|
76
|
+
value: originalMethod,
|
|
77
|
+
enumerable: false,
|
|
78
|
+
writable: true,
|
|
79
|
+
configurable: true
|
|
80
|
+
});
|
|
81
|
+
} catch (e) {
|
|
82
|
+
// 降级:defineProperty 不可用时直接挂载,保证 restoreFunction 仍可用
|
|
83
|
+
try {
|
|
84
|
+
proxyMethod._rum_intercepted = originalMethod;
|
|
85
|
+
} catch (err) {
|
|
86
|
+
// 二层保护:strict mode 下对不可扩展对象直接赋值也会抛 TypeError,上报后放弃挂载
|
|
87
|
+
_logger.logger.warn('interceptFunction', 'disguiseProxy fallback failed', err);
|
|
30
88
|
}
|
|
31
|
-
}
|
|
32
|
-
proxyMethod._rum_intercepted = registeredMethod;
|
|
33
|
-
target[name] = proxyMethod;
|
|
89
|
+
}
|
|
34
90
|
}
|
|
35
91
|
|
|
36
92
|
/**
|
|
@@ -158,72 +214,6 @@ function delay(func, wait) {
|
|
|
158
214
|
return setTimeout.apply(void 0, [func, +wait || 0].concat(args));
|
|
159
215
|
}
|
|
160
216
|
|
|
161
|
-
// // 类型匹配正则
|
|
162
|
-
// const TYPE_REG = /^\[object ([a-z]*)\]$/;
|
|
163
|
-
// /**
|
|
164
|
-
// * @desc 需要严格区分object和array
|
|
165
|
-
// * @param obj 任意对象
|
|
166
|
-
// */
|
|
167
|
-
// export function getType(obj: any) {
|
|
168
|
-
// let type = Object.prototype.toString.call(obj);
|
|
169
|
-
// type = type.toLowerCase() || '';
|
|
170
|
-
// const arr = type.match(TYPE_REG);
|
|
171
|
-
// return arr?.[1];
|
|
172
|
-
// }
|
|
173
|
-
//
|
|
174
|
-
// /**
|
|
175
|
-
// * @desc 将字符串转换成正则表达式
|
|
176
|
-
// * @param str 任意字符串
|
|
177
|
-
// * 注意,这里只支持 /xxx/ 这种格式,不支持修饰符,例如 /xxx/ig
|
|
178
|
-
// */
|
|
179
|
-
// export function transStrToReg(str: string) {
|
|
180
|
-
// if (getType(str) !== 'string') return str;
|
|
181
|
-
// if (str.length > 2 && str[0] === '/' && str[str.length - 1] === '/') {
|
|
182
|
-
// return new RegExp(str.substr(1, str.length - 2));
|
|
183
|
-
// }
|
|
184
|
-
// return str;
|
|
185
|
-
// }
|
|
186
|
-
//
|
|
187
|
-
// /**
|
|
188
|
-
// *
|
|
189
|
-
// * @param config 配置
|
|
190
|
-
// * @param keys 需要转换的字段
|
|
191
|
-
// */
|
|
192
|
-
// export function toRegFormat(config: any, keys: any[]) {
|
|
193
|
-
// if (getType(keys) === 'string') {
|
|
194
|
-
// keys = [keys];
|
|
195
|
-
// }
|
|
196
|
-
// for (let i = 0, len = keys.length; i < len; i++) {
|
|
197
|
-
// const paths = keys[i].split('.');
|
|
198
|
-
// const lastIndex = paths.length - 1;
|
|
199
|
-
// if (!config) break;
|
|
200
|
-
// let tmp = config;
|
|
201
|
-
// for (let j = 0, jlen = paths.length; j < jlen; j++) {
|
|
202
|
-
// if (paths[j] === '[]') {
|
|
203
|
-
// if (getType(tmp) === 'array') {
|
|
204
|
-
// let lastPath = paths.splice(j + 1);
|
|
205
|
-
// lastPath = lastPath.join('.');
|
|
206
|
-
// for (let x = 0, xlen = tmp.length; x < xlen; x++) {
|
|
207
|
-
// toRegFormat(tmp[x], lastPath);
|
|
208
|
-
// }
|
|
209
|
-
// }
|
|
210
|
-
// break;
|
|
211
|
-
// }
|
|
212
|
-
// // 最后一层了,需要往前一层才能修改对象
|
|
213
|
-
// if (lastIndex === j && getType(tmp[paths[j]]) === 'string') {
|
|
214
|
-
// tmp[paths[j]] = transStrToReg(tmp[paths[j]]);
|
|
215
|
-
// break;
|
|
216
|
-
// }
|
|
217
|
-
// tmp = tmp[paths[j]];
|
|
218
|
-
// if (!tmp) break;
|
|
219
|
-
// }
|
|
220
|
-
// if (!tmp || getType(tmp) !== 'array') continue;
|
|
221
|
-
// for (let j = 0, jlen = tmp.length; j < jlen; j++) {
|
|
222
|
-
// tmp[j] = transStrToReg(tmp[j]);
|
|
223
|
-
// }
|
|
224
|
-
// }
|
|
225
|
-
// }
|
|
226
|
-
|
|
227
217
|
/**
|
|
228
218
|
* 解析正则表达式字符串,支持 /pattern/flags 格式
|
|
229
219
|
* @param str 正则表达式字符串,如 "/a/ig" 或 "a"
|