monitor-track 1.11.0 → 1.12.1
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/CHANGELOG.md +36 -0
- package/README.md +15 -5
- package/cjs/handlers/pv.d.ts +1 -0
- package/cjs/index.d.ts +4 -0
- package/cjs/index.js +1102 -1060
- package/cjs/reporter.d.ts +18 -7
- package/cjs/types/global.d.ts +16 -2
- package/cjs/types/index.d.ts +10 -5
- package/esm/config/global.js +15 -5
- package/esm/handlers/error.js +3 -3
- package/esm/handlers/pv.d.ts +1 -0
- package/esm/handlers/pv.js +17 -7
- package/esm/handlers/user-activity.js +2 -2
- package/esm/index.d.ts +4 -0
- package/esm/index.js +20 -4
- package/esm/package.json.js +1 -1
- package/esm/reporter.d.ts +18 -7
- package/esm/reporter.js +20 -10
- package/esm/types/global.d.ts +16 -2
- package/esm/types/index.d.ts +10 -5
- package/esm/utils/index.js +8 -12
- package/index.js +1454 -1410
- package/package.json +1 -1
package/cjs/index.js
CHANGED
|
@@ -1,64 +1,36 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var axios = require('axios');
|
|
4
3
|
var uuid = require('uuid');
|
|
5
|
-
var uaParserJs = require('ua-parser-js');
|
|
6
|
-
var rrweb = require('rrweb');
|
|
7
4
|
var ErrorStackParser = require('error-stack-parser');
|
|
8
5
|
var html2canvas = require('html2canvas');
|
|
6
|
+
var rrweb = require('rrweb');
|
|
7
|
+
var axios = require('axios');
|
|
8
|
+
var uaParserJs = require('ua-parser-js');
|
|
9
9
|
|
|
10
10
|
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
11
11
|
|
|
12
12
|
function _interopNamespace(e) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
});
|
|
23
|
-
}
|
|
13
|
+
if (e && e.__esModule) return e;
|
|
14
|
+
var n = Object.create(null);
|
|
15
|
+
if (e) {
|
|
16
|
+
Object.keys(e).forEach(function (k) {
|
|
17
|
+
if (k !== 'default') {
|
|
18
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
19
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
20
|
+
enumerable: true,
|
|
21
|
+
get: function () { return e[k]; }
|
|
24
22
|
});
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
n["default"] = e;
|
|
27
|
+
return Object.freeze(n);
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
var axios__default = /*#__PURE__*/_interopDefaultLegacy(axios);
|
|
31
|
-
var rrweb__namespace = /*#__PURE__*/_interopNamespace(rrweb);
|
|
32
30
|
var ErrorStackParser__default = /*#__PURE__*/_interopDefaultLegacy(ErrorStackParser);
|
|
33
31
|
var html2canvas__default = /*#__PURE__*/_interopDefaultLegacy(html2canvas);
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
Copyright (c) Microsoft Corporation.
|
|
37
|
-
|
|
38
|
-
Permission to use, copy, modify, and/or distribute this software for any
|
|
39
|
-
purpose with or without fee is hereby granted.
|
|
40
|
-
|
|
41
|
-
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
42
|
-
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
43
|
-
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
44
|
-
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
45
|
-
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
46
|
-
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
47
|
-
PERFORMANCE OF THIS SOFTWARE.
|
|
48
|
-
***************************************************************************** */
|
|
49
|
-
|
|
50
|
-
function __awaiter(thisArg, _arguments, P, generator) {
|
|
51
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
52
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
53
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
54
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
55
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
56
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const monitorTrackId = 'monitor-track-id';
|
|
61
|
-
const monitorTrackSessionId = 'monitor-track-session-id';
|
|
32
|
+
var rrweb__namespace = /*#__PURE__*/_interopNamespace(rrweb);
|
|
33
|
+
var axios__default = /*#__PURE__*/_interopDefaultLegacy(axios);
|
|
62
34
|
|
|
63
35
|
/** */
|
|
64
36
|
/**
|
|
@@ -87,6 +59,8 @@ function setConfig(config) {
|
|
|
87
59
|
Object.assign(Config, config);
|
|
88
60
|
}
|
|
89
61
|
|
|
62
|
+
var version = "1.12.1";
|
|
63
|
+
|
|
90
64
|
const eventsMatrix = [[]];
|
|
91
65
|
rrweb__namespace.record({
|
|
92
66
|
emit(event, isCheckout) {
|
|
@@ -254,1118 +228,1182 @@ function getFullScreenShoot(filename) {
|
|
|
254
228
|
});
|
|
255
229
|
}
|
|
256
230
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
/** 屏幕宽高 */
|
|
280
|
-
screen: 'sc',
|
|
281
|
-
/** 页面title */
|
|
282
|
-
pageTitle: 'pt',
|
|
283
|
-
/** 页面referrer */
|
|
284
|
-
referrer: 'ref',
|
|
285
|
-
/** 页面编码 */
|
|
286
|
-
charset: 'char',
|
|
287
|
-
/** language */
|
|
288
|
-
language: 'lan',
|
|
289
|
-
/** vendor, 浏览器厂商 */
|
|
290
|
-
navigatorVendor: 'nV',
|
|
291
|
-
/** 网络连接类型 */
|
|
292
|
-
connectionType: 'cT',
|
|
293
|
-
/** 浏览器名称 */
|
|
294
|
-
browserName: 'bN',
|
|
295
|
-
/** 浏览器版本 */
|
|
296
|
-
browserVersion: 'bV',
|
|
297
|
-
/** 浏览器引擎名称 */
|
|
298
|
-
engineName: 'enN',
|
|
299
|
-
/** 浏览器引擎版本 */
|
|
300
|
-
engineVersion: 'enV',
|
|
301
|
-
/** 操作系统名称 */
|
|
302
|
-
osName: 'osN',
|
|
303
|
-
/** 操作系统版本 */
|
|
304
|
-
osVersion: 'osV',
|
|
305
|
-
pv: {
|
|
306
|
-
/** 子类型 */
|
|
307
|
-
subType: 'suT',
|
|
308
|
-
/** 停留时间 */
|
|
309
|
-
stayTime: 'sT',
|
|
310
|
-
},
|
|
311
|
-
error: {
|
|
312
|
-
/** 子类型 */
|
|
313
|
-
subType: 'suT',
|
|
314
|
-
/** 错误行 */
|
|
315
|
-
line: 'line',
|
|
316
|
-
/** 错误列 */
|
|
317
|
-
column: 'col',
|
|
318
|
-
/** 错误信息 */
|
|
319
|
-
message: 'msg',
|
|
320
|
-
/** 错误文件 */
|
|
321
|
-
filename: 'fil',
|
|
322
|
-
/** 错误函数名 */
|
|
323
|
-
functionName: 'fn',
|
|
324
|
-
/** 错误栈信息 */
|
|
325
|
-
stack: '_st',
|
|
326
|
-
/** 错误帧 */
|
|
327
|
-
stackFrame: '_sf',
|
|
328
|
-
/** 视频录制数据 */
|
|
329
|
-
events: 'evt',
|
|
330
|
-
/** 页面截图 */
|
|
331
|
-
picture: 'pic',
|
|
332
|
-
},
|
|
333
|
-
ua: {
|
|
334
|
-
/** 子类型 */
|
|
335
|
-
subType: 'suT',
|
|
336
|
-
/** x轴坐标 */
|
|
337
|
-
x: 'x',
|
|
338
|
-
/** y轴坐标 */
|
|
339
|
-
y: 'y',
|
|
340
|
-
/** 元素路径 */
|
|
341
|
-
path: 'path',
|
|
342
|
-
},
|
|
343
|
-
/** 性能数据 */
|
|
344
|
-
perf: {
|
|
345
|
-
navigation: {
|
|
346
|
-
/**
|
|
347
|
-
*一个无符号短整型,表示是如何导航到这个页面的。可能的值如下:
|
|
348
|
-
TYPE_NAVIGATE (0)
|
|
349
|
-
当前页面是通过点击链接,书签和表单提交,或者脚本操作,或者在url中直接输入地址,type值为0
|
|
350
|
-
TYPE_RELOAD (1)
|
|
351
|
-
点击刷新页面按钮或者通过Location.reload()方法显示的页面,type值为1
|
|
352
|
-
The page was accessed by clicking the Reload button or via the Location.reload() method.
|
|
353
|
-
TYPE_BACK_FORWARD (2)
|
|
354
|
-
页面通过历史记录和前进后退访问时。type值为2
|
|
355
|
-
TYPE_RESERVED (255)
|
|
356
|
-
任何其他方式,type值为255
|
|
357
|
-
*/
|
|
358
|
-
type: 't',
|
|
359
|
-
/** 无符号短整型,表示在到达这个页面之前重定向了多少次。 */
|
|
360
|
-
redirectCount: 'rc',
|
|
361
|
-
},
|
|
362
|
-
memory: {
|
|
363
|
-
/** 上下文内可用堆的最大体积,以字节计算。 */
|
|
364
|
-
jsHeapSizeLimit: 'hsl',
|
|
365
|
-
/** 已分配的堆体积,以字节计算。 */
|
|
366
|
-
totalJSHeapSize: 'ths',
|
|
367
|
-
/** 当前 JS 堆活跃段(segment)的体积,以字节计算。 */
|
|
368
|
-
usedJSHeapSize: 'uhs',
|
|
369
|
-
},
|
|
370
|
-
/** 返回性能测量开始时的时间的高精度时间戳。 */
|
|
371
|
-
timeOrigin: 'to',
|
|
372
|
-
timing: {
|
|
373
|
-
/** 是一个无符号long long 型的毫秒数,返回浏览器与服务器之间的连接建立时的Unix毫秒时间戳。如果建立的是持久连接,则返回值等同于fetchStart属性的值。连接建立指的是所有握手和认证过程全部结束。 */
|
|
374
|
-
connectEnd: 'ce',
|
|
375
|
-
/** 是一个无符号long long 型的毫秒数,返回HTTP请求开始向服务器发送时的Unix毫秒时间戳。如果使用持久连接(persistent connection),则返回值等同于fetchStart属性的值 */
|
|
376
|
-
connectStart: 'cs',
|
|
377
|
-
/** 是一个无符号long long 型的毫秒数,返回当前文档解析完成,即Document.readyState 变为 'complete'且相对应的readystatechange (en-US) 被触发时的Unix毫秒时间戳。 */
|
|
378
|
-
domComplete: 'dc',
|
|
379
|
-
/** 是一个无符号long long 型的毫秒数,返回当所有需要立即执行的脚本已经被执行(不论执行顺序)时的Unix毫秒时间戳。 */
|
|
380
|
-
domContentLoadedEventEnd: 'scLee',
|
|
381
|
-
/** 是一个无符号long long 型的毫秒数,返回当解析器发送DOMContentLoaded (en-US) 事件,即所有需要被执行的脚本已经被解析时的Unix毫秒时间戳。 */
|
|
382
|
-
domContentLoadedEventStart: 'dcLes',
|
|
383
|
-
/** 是一个无符号long long 型的毫秒数,返回当前网页DOM结构结束解析、开始加载内嵌资源时(即Document.readyState属性变为“interactive”、相应的readystatechange (en-US)事件触发时)的Unix毫秒时间戳。 */
|
|
384
|
-
domInteractive: 'di',
|
|
385
|
-
/** 是一个无符号long long 型的毫秒数,返回当前网页DOM结构开始解析时(即Document.readyState属性变为“loading”、相应的 readystatechange (en-US)事件触发时)的Unix毫秒时间戳。 */
|
|
386
|
-
domLoading: 'dl',
|
|
387
|
-
/** 是一个无符号long long 型的毫秒数,表征了域名查询结束的UNIX时间戳。如果使用了持续连接(persistent connection),或者这个信息存储到了缓存或者本地资源上,这个值将和 PerformanceTiming.fetchStart一致。 */
|
|
388
|
-
domainLookupEnd: 'dle',
|
|
389
|
-
/** 是一个无符号long long 型的毫秒数,表征了域名查询开始的UNIX时间戳。如果使用了持续连接(persistent connection),或者这个信息存储到了缓存或者本地资源上,这个值将和 PerformanceTiming.fetchStart一致。 */
|
|
390
|
-
domainLookupStart: 'dls',
|
|
391
|
-
/** 是一个无符号long long 型的毫秒数,表征了浏览器准备好使用HTTP请求来获取(fetch)文档的UNIX时间戳。这个时间点会在检查任何应用缓存之前。 */
|
|
392
|
-
fetchStart: 'fs',
|
|
393
|
-
/** 是一个无符号long long 型的毫秒数,返回当load (en-US)事件结束,即加载事件完成时的Unix毫秒时间戳。如果这个事件还未被发送,或者尚未完成,它的值将会是0. */
|
|
394
|
-
loadEventEnd: 'lee',
|
|
395
|
-
/** 是一个无符号long long 型的毫秒数,返回该文档下,load (en-US)事件被发送时的Unix毫秒时间戳。如果这个事件还未被发送,它的值将会是0。 */
|
|
396
|
-
loadEventStart: 'les',
|
|
397
|
-
/** 是一个无符号long long 型的毫秒数,表征了从同一个浏览器上下文的上一个文档卸载(unload)结束时的UNIX时间戳。如果没有上一个文档,这个值会和PerformanceTiming.fetchStart相同 */
|
|
398
|
-
navigationStart: 'ns',
|
|
399
|
-
/** 是一个无符号long long 型的毫秒数,表征了最后一个HTTP重定向完成时(也就是说是HTTP响应的最后一个比特直接被收到的时间)的UNIX时间戳。如果没有重定向,或者重定向中的一个不同源,这个值会返回0. */
|
|
400
|
-
redirectEnd: 're',
|
|
401
|
-
/** 是一个无符号long long 型的毫秒数,表征了第一个HTTP重定向开始时的UNIX时间戳。如果没有重定向,或者重定向中的一个不同源,这个值会返回0. */
|
|
402
|
-
redirectStart: 'redS',
|
|
403
|
-
/** 是一个无符号long long 型的毫秒数,返回浏览器向服务器发出HTTP请求时(或开始读取本地缓存时)的Unix毫秒时间戳。 */
|
|
404
|
-
requestStart: 'reqS',
|
|
405
|
-
/** 是一个无符号long long 型的毫秒数,返回浏览器从服务器收到(或从本地缓存读取,或从本地资源读取)最后一个字节时(如果在此之前HTTP连接已经关闭,则返回关闭时)的Unix毫秒时间戳。 */
|
|
406
|
-
responseEnd: 'resE',
|
|
407
|
-
/** 是一个无符号long long 型的毫秒数,返回浏览器从服务器收到(或从本地缓存读取)第一个字节时的Unix毫秒时间戳。如果传输层在开始请求之后失败并且连接被重开,该属性将会被数制成新的请求的相对应的发起时间。 */
|
|
408
|
-
responseStart: 'resS',
|
|
409
|
-
/** 是一个无符号long long 型的毫秒数,返回浏览器与服务器开始安全链接的握手时的Unix毫秒时间戳。如果当前网页不要求安全连接,则返回0。 */
|
|
410
|
-
secureConnectionStart: 'scs',
|
|
411
|
-
/** 是一个无符号long long 型的毫秒数,表征了unload (en-US)事件处理完成时的UNIX时间戳。如果没有上一个文档,or if the previous document, or one of the needed redirects, is not of the same origin, 这个值会返回0. */
|
|
412
|
-
unloadEventEnd: 'uee',
|
|
413
|
-
/** 是一个无符号long long 型的毫秒数,表征了unload (en-US)事件抛出时的UNIX时间戳。如果没有上一个文档,or if the previous document, or one of the needed redirects, is not of the same origin, 这个值会返回0. */
|
|
414
|
-
unloadEventStart: 'ees',
|
|
415
|
-
},
|
|
416
|
-
},
|
|
417
|
-
/** api请求相关 */
|
|
418
|
-
req: {
|
|
419
|
-
/** 请求类型,xhr或fetch */
|
|
420
|
-
requestType: 'rT',
|
|
421
|
-
/** 请求地址 */
|
|
422
|
-
responseURL: 'url',
|
|
423
|
-
/** 请求结果 */
|
|
424
|
-
status: 'stat',
|
|
425
|
-
/** 请求耗时 */
|
|
426
|
-
loadTime: 'lT',
|
|
427
|
-
/** 请求状态 */
|
|
428
|
-
statusText: 'sT',
|
|
429
|
-
/** 上报原因, slow或failed */
|
|
430
|
-
reason: 're',
|
|
431
|
-
/** 上报时带上的详细信息 */
|
|
432
|
-
detail: 'de',
|
|
433
|
-
},
|
|
434
|
-
vD: {
|
|
435
|
-
/** 埋点名称 */
|
|
436
|
-
trackName: 'tN',
|
|
437
|
-
/** 埋点参数 */
|
|
438
|
-
params: 'params',
|
|
439
|
-
},
|
|
440
|
-
manualReport: 'mR',
|
|
441
|
-
lifeCycleId: 'li',
|
|
442
|
-
sessionId: 'sI',
|
|
443
|
-
};
|
|
444
|
-
const xhrFunc = (filename, userId, asyncUpdateId, file, res, callback, rest) => {
|
|
445
|
-
const formData = new FormData();
|
|
446
|
-
for (const i in rest) {
|
|
447
|
-
if (Object.prototype.hasOwnProperty.call(rest, i)) {
|
|
448
|
-
formData.append(i, rest[i]);
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
formData.append('filename', filename);
|
|
452
|
-
formData.append('asyncUpdateId', asyncUpdateId); //用于上传到oss后异步更新到db的id
|
|
453
|
-
formData.append('userId', userId);
|
|
454
|
-
formData.append('files', file);
|
|
455
|
-
const xhr = new XMLHttpRequest();
|
|
456
|
-
xhr.addEventListener('error', () => res(), false);
|
|
457
|
-
xhr.open('POST', Config.reportUrl);
|
|
458
|
-
xhr.onreadystatechange = function () {
|
|
459
|
-
var _a;
|
|
460
|
-
if (xhr.readyState === 4) {
|
|
461
|
-
if (xhr.status === 200) {
|
|
462
|
-
try {
|
|
463
|
-
const response = xhr.responseText;
|
|
464
|
-
const resp = JSON.parse(response);
|
|
465
|
-
callback((_a = resp.result) === null || _a === void 0 ? void 0 : _a.response);
|
|
466
|
-
}
|
|
467
|
-
catch (_err) {
|
|
468
|
-
//
|
|
469
|
-
}
|
|
470
|
-
res();
|
|
471
|
-
}
|
|
472
|
-
else {
|
|
473
|
-
res();
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
};
|
|
477
|
-
xhr.send(formData);
|
|
478
|
-
};
|
|
479
|
-
const ERROR_MESSAGE_MAP = {};
|
|
480
|
-
function reportFunc(data) {
|
|
481
|
-
var _a, _b, _c;
|
|
482
|
-
const payload = convertToSchema(data, reportMap);
|
|
483
|
-
payload.time = new Date().getTime(); // 设置上报时间
|
|
484
|
-
let picturePromise = Promise.resolve();
|
|
485
|
-
let hasErrorEvent = false;
|
|
486
|
-
const filename = uuid.v4();
|
|
487
|
-
if (data.type === 'error') {
|
|
488
|
-
let message = ((_a = data.error) === null || _a === void 0 ? void 0 : _a.message) || 'default';
|
|
489
|
-
if (((_b = data.error) === null || _b === void 0 ? void 0 : _b.subType) === 'resource') {
|
|
490
|
-
message = 'resource';
|
|
491
|
-
}
|
|
492
|
-
if (ERROR_MESSAGE_MAP[message]) ;
|
|
493
|
-
else {
|
|
494
|
-
ERROR_MESSAGE_MAP[message] = true;
|
|
495
|
-
setTimeout(() => {
|
|
496
|
-
//节流效果,每隔3秒上报一次重复的错误录像和错误截图
|
|
497
|
-
delete ERROR_MESSAGE_MAP[message];
|
|
498
|
-
}, 3000);
|
|
499
|
-
const { p, pid, host, bN, pt } = payload;
|
|
500
|
-
if ((_c = data.error) === null || _c === void 0 ? void 0 : _c.events) {
|
|
501
|
-
hasErrorEvent = true;
|
|
502
|
-
const errorEvent = data.error.events;
|
|
503
|
-
const delayTime = Math.ceil(Math.random() * 10000);
|
|
504
|
-
setTimeout(() => {
|
|
505
|
-
const blob = new Blob([errorEvent], { type: 'text/json' });
|
|
506
|
-
return new Promise((res) => {
|
|
507
|
-
xhrFunc(`${filename}.json`, data.uuid, filename, blob, res, () => { }, { p, pid, host, bN, pt });
|
|
508
|
-
});
|
|
509
|
-
}, delayTime < 3500 ? 3500 : delayTime);
|
|
510
|
-
}
|
|
511
|
-
picturePromise = getFullScreenShoot(filename)
|
|
512
|
-
.then((file) => {
|
|
513
|
-
return new Promise((res) => {
|
|
514
|
-
xhrFunc(file.name, data.uuid, filename, file, res, (result) => {
|
|
515
|
-
payload.error.pic = result;
|
|
516
|
-
}, { p, pid, host, bN, pt });
|
|
517
|
-
});
|
|
518
|
-
})
|
|
519
|
-
.catch((err) => {
|
|
520
|
-
payload.error.picError = (err === null || err === void 0 ? void 0 : err.stack) || (err === null || err === void 0 ? void 0 : err.toString());
|
|
521
|
-
});
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
return picturePromise
|
|
525
|
-
.then(() => {
|
|
526
|
-
var _a;
|
|
527
|
-
if ((_a = payload.error) === null || _a === void 0 ? void 0 : _a.evt)
|
|
528
|
-
delete payload.error.evt;
|
|
529
|
-
if (window && window.navigator && typeof window.navigator.sendBeacon === 'function') {
|
|
530
|
-
const formData = new FormData();
|
|
531
|
-
Object.keys(payload).forEach((key) => {
|
|
532
|
-
let value = payload[key];
|
|
533
|
-
if (value !== null && value !== undefined) {
|
|
534
|
-
if (typeof value === 'object') {
|
|
535
|
-
value = JSON.stringify(value);
|
|
536
|
-
}
|
|
537
|
-
formData.append(key, value);
|
|
538
|
-
}
|
|
539
|
-
});
|
|
540
|
-
//eventErrorFilename,将错误录像的文件名附在错误上报的埋点里,便于将错误录像上报回调地址异步的更新到db
|
|
541
|
-
if (hasErrorEvent)
|
|
542
|
-
formData.append('eef', filename);
|
|
543
|
-
window.navigator.sendBeacon(Config.reportUrl, formData);
|
|
544
|
-
}
|
|
545
|
-
else {
|
|
546
|
-
if (hasErrorEvent)
|
|
547
|
-
payload.eef = filename;
|
|
548
|
-
new Image().src = `${Config.reportUrl}?${serialize(payload)}`;
|
|
549
|
-
}
|
|
550
|
-
})
|
|
551
|
-
.catch((_err) => {
|
|
552
|
-
//
|
|
553
|
-
});
|
|
554
|
-
}
|
|
555
|
-
// 上报
|
|
556
|
-
function report(data) {
|
|
557
|
-
return new Promise((res) => {
|
|
558
|
-
//在帧的空闲时间上报
|
|
559
|
-
const dataCopy = JSON.parse(JSON.stringify(data));
|
|
560
|
-
if (typeof window.requestIdleCallback === 'function') {
|
|
561
|
-
window.requestIdleCallback(() => reportFunc(dataCopy).then(res), { timeout: 2000 });
|
|
562
|
-
}
|
|
563
|
-
else {
|
|
564
|
-
setTimeout(() => reportFunc(dataCopy).then(res), 0);
|
|
565
|
-
}
|
|
231
|
+
/*! *****************************************************************************
|
|
232
|
+
Copyright (c) Microsoft Corporation.
|
|
233
|
+
|
|
234
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
235
|
+
purpose with or without fee is hereby granted.
|
|
236
|
+
|
|
237
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
238
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
239
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
240
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
241
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
242
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
243
|
+
PERFORMANCE OF THIS SOFTWARE.
|
|
244
|
+
***************************************************************************** */
|
|
245
|
+
|
|
246
|
+
function __awaiter(thisArg, _arguments, P, generator) {
|
|
247
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
248
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
249
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
250
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
251
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
252
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
566
253
|
});
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const monitorTrackId = 'monitor-track-id';
|
|
257
|
+
const monitorTrackSessionId = 'monitor-track-session-id';
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* @description 点击事件触发后的操作
|
|
261
|
+
*/
|
|
262
|
+
function handleClick(event) {
|
|
263
|
+
const target = event.target;
|
|
264
|
+
if (target.nodeName === 'INPUT' || target.nodeName === 'TEXTAREA') {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
const path = getElmPath(target);
|
|
268
|
+
if (path) {
|
|
269
|
+
setReportValue('type', 'ua');
|
|
270
|
+
setReportValue('ua', {
|
|
271
|
+
subType: 'ui.click',
|
|
272
|
+
x: event.x,
|
|
273
|
+
y: event.y,
|
|
274
|
+
path,
|
|
275
|
+
});
|
|
276
|
+
report(getReport());
|
|
277
|
+
}
|
|
567
278
|
}
|
|
568
279
|
/**
|
|
569
|
-
* @description
|
|
570
|
-
* @param data 要转换的数据
|
|
571
|
-
* @param map 要转换的数据的字段映射
|
|
572
|
-
* @param redundancyData 冗余数据
|
|
280
|
+
* @description 点击触发失焦后的操作
|
|
573
281
|
*/
|
|
574
|
-
function
|
|
575
|
-
const
|
|
576
|
-
if (
|
|
577
|
-
|
|
282
|
+
function handleBlur(event) {
|
|
283
|
+
const target = event.target;
|
|
284
|
+
if (target.nodeName !== 'INPUT' && target.nodeName !== 'TEXTAREA') {
|
|
285
|
+
return;
|
|
578
286
|
}
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
}
|
|
591
|
-
else {
|
|
592
|
-
reportData[key] = convertToSchema(data[key], map[key]);
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
else {
|
|
596
|
-
reportData[key] = data[key];
|
|
597
|
-
}
|
|
598
|
-
}
|
|
599
|
-
else {
|
|
600
|
-
// 排除undefined
|
|
601
|
-
if (data[key] !== undefined) {
|
|
602
|
-
reportData[mapKey] = data[key];
|
|
603
|
-
}
|
|
604
|
-
}
|
|
605
|
-
}
|
|
287
|
+
const path = getElmPath(target);
|
|
288
|
+
if (path) {
|
|
289
|
+
setReportValue('type', 'ua');
|
|
290
|
+
setReportValue('ua', {
|
|
291
|
+
subType: 'ui.blur',
|
|
292
|
+
x: event.x,
|
|
293
|
+
y: event.y,
|
|
294
|
+
path,
|
|
295
|
+
value: target.value,
|
|
296
|
+
});
|
|
297
|
+
report(getReport());
|
|
606
298
|
}
|
|
607
|
-
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* @description 滚动的操作
|
|
302
|
+
*/
|
|
303
|
+
let timeout$1;
|
|
304
|
+
function handleScroll(_event) {
|
|
305
|
+
clearTimeout(timeout$1);
|
|
306
|
+
timeout$1 = setTimeout(() => {
|
|
307
|
+
setReportValue('type', 'ua');
|
|
308
|
+
setReportValue('ua', {
|
|
309
|
+
subType: 'ui.scroll',
|
|
310
|
+
});
|
|
311
|
+
report(getReport());
|
|
312
|
+
}, 1000);
|
|
608
313
|
}
|
|
609
314
|
|
|
610
|
-
|
|
611
|
-
let routerStack = [];
|
|
612
|
-
/** 路由触发的时间 */
|
|
613
|
-
let time = 0;
|
|
315
|
+
const parser = new uaParserJs.UAParser();
|
|
614
316
|
/**
|
|
615
|
-
* @description
|
|
616
|
-
* @param type
|
|
317
|
+
* @description 获取uuid
|
|
617
318
|
*/
|
|
618
|
-
function
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
// @ts-ignore
|
|
626
|
-
// eslint-disable-next-line prefer-rest-params
|
|
627
|
-
e.arguments = arguments;
|
|
628
|
-
window.dispatchEvent(e);
|
|
629
|
-
return r;
|
|
630
|
-
};
|
|
319
|
+
function getUid() {
|
|
320
|
+
let uid = localStorage.getItem(monitorTrackId) || '';
|
|
321
|
+
if (!uid) {
|
|
322
|
+
uid = uuid.v4();
|
|
323
|
+
localStorage.setItem(monitorTrackId, uid);
|
|
324
|
+
}
|
|
325
|
+
return uid;
|
|
631
326
|
}
|
|
632
327
|
/**
|
|
633
|
-
* @description
|
|
634
|
-
* @param e history事件
|
|
328
|
+
* @description 获取session id
|
|
635
329
|
*/
|
|
636
|
-
function
|
|
637
|
-
|
|
638
|
-
|
|
330
|
+
function getSessionId() {
|
|
331
|
+
const sessionId = sessionStorage.getItem(monitorTrackSessionId);
|
|
332
|
+
if (sessionId) {
|
|
333
|
+
return sessionId;
|
|
334
|
+
}
|
|
335
|
+
else {
|
|
336
|
+
const id = uuid.v4();
|
|
337
|
+
sessionStorage.setItem(monitorTrackSessionId, id);
|
|
338
|
+
return id;
|
|
339
|
+
}
|
|
639
340
|
}
|
|
640
341
|
/**
|
|
641
|
-
*
|
|
342
|
+
* @description 获取浏览器信息
|
|
642
343
|
*/
|
|
643
|
-
function
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
else {
|
|
658
|
-
currentUrlPostfix = currentUrlSplit[1];
|
|
659
|
-
}
|
|
660
|
-
}
|
|
661
|
-
const currentUrlResult = `${currentUrlSplit[0]}#${currentUrlPostfix}`;
|
|
662
|
-
pageUrl = currentUrlResult;
|
|
663
|
-
}
|
|
664
|
-
const { originPage, page } = setRouteStack(pageUrl);
|
|
665
|
-
// 愿页面等于当前页面,说明无路由变更。只有spa且hash模式下才会出现
|
|
666
|
-
if (originPage === page) {
|
|
667
|
-
return;
|
|
668
|
-
}
|
|
669
|
-
const currentTime = new Date().getTime();
|
|
670
|
-
// 页面停留时间
|
|
671
|
-
const stayTime = currentTime - time;
|
|
672
|
-
// 设置触发路由变化的时间
|
|
673
|
-
time = currentTime;
|
|
674
|
-
// 设置信息
|
|
675
|
-
setReportValue('originPage', originPage);
|
|
676
|
-
setReportValue('page', page);
|
|
677
|
-
setReportValue('type', 'pv');
|
|
678
|
-
setReportValue('pv', { stayTime });
|
|
679
|
-
// 上报数据
|
|
680
|
-
report(getReport());
|
|
344
|
+
function getNavigator() {
|
|
345
|
+
var _a;
|
|
346
|
+
const uaResult = parser.getResult();
|
|
347
|
+
return {
|
|
348
|
+
language: navigator.language,
|
|
349
|
+
navigatorVendor: navigator.vendor,
|
|
350
|
+
connectionType: ((_a = navigator === null || navigator === void 0 ? void 0 : navigator.connection) === null || _a === void 0 ? void 0 : _a.effectiveType) || '2g',
|
|
351
|
+
browserName: uaResult.browser.name || '',
|
|
352
|
+
browserVersion: uaResult.browser.version || '',
|
|
353
|
+
engineName: uaResult.engine.name || '',
|
|
354
|
+
engineVersion: uaResult.engine.version || '',
|
|
355
|
+
osName: uaResult.os.name || '',
|
|
356
|
+
osVersion: uaResult.os.version || '',
|
|
357
|
+
};
|
|
681
358
|
}
|
|
682
|
-
|
|
683
|
-
|
|
359
|
+
/**
|
|
360
|
+
* @description 获取viewport的宽高
|
|
361
|
+
*/
|
|
362
|
+
function getViewport() {
|
|
363
|
+
const w = document.documentElement.clientWidth || document.body.clientWidth;
|
|
364
|
+
const h = document.documentElement.clientHeight || document.body.clientHeight;
|
|
365
|
+
return `${w} x ${h}`;
|
|
684
366
|
}
|
|
685
367
|
/**
|
|
686
|
-
* @description
|
|
687
|
-
* @param
|
|
368
|
+
* @description 获取元素路径,最多保留5层
|
|
369
|
+
* @param e
|
|
688
370
|
*/
|
|
689
|
-
function
|
|
690
|
-
if (
|
|
691
|
-
|
|
371
|
+
const getElmPath = function (target) {
|
|
372
|
+
if (!target || target.nodeType !== 1) {
|
|
373
|
+
return '';
|
|
692
374
|
}
|
|
693
|
-
|
|
694
|
-
|
|
375
|
+
const ret = [];
|
|
376
|
+
// 层数,最多5层
|
|
377
|
+
let deepLength = 0;
|
|
378
|
+
// 元素
|
|
379
|
+
let elm = '';
|
|
380
|
+
if (typeof target.innerText === 'string') {
|
|
381
|
+
ret.push(`(${target.innerText.substr(0, 50)})`);
|
|
695
382
|
}
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
};
|
|
702
|
-
}
|
|
703
|
-
|
|
704
|
-
var version = "1.11.0";
|
|
705
|
-
|
|
706
|
-
// 上报数据
|
|
707
|
-
let Report = {
|
|
708
|
-
uuid: '',
|
|
709
|
-
projectID: '',
|
|
710
|
-
host: '',
|
|
711
|
-
originPage: '',
|
|
712
|
-
page: '',
|
|
713
|
-
time: 0,
|
|
714
|
-
browserName: '',
|
|
715
|
-
browserVersion: '',
|
|
716
|
-
engineName: '',
|
|
717
|
-
engineVersion: '',
|
|
718
|
-
language: '',
|
|
719
|
-
navigatorVendor: '',
|
|
720
|
-
connectionType: '2g',
|
|
721
|
-
osName: '',
|
|
722
|
-
osVersion: '',
|
|
723
|
-
type: 'init',
|
|
724
|
-
viewport: '',
|
|
725
|
-
screen: '',
|
|
726
|
-
version: '',
|
|
727
|
-
charset: '',
|
|
728
|
-
pageTitle: '',
|
|
729
|
-
referrer: '',
|
|
730
|
-
pv: null,
|
|
731
|
-
ua: null,
|
|
732
|
-
error: null,
|
|
733
|
-
dpr: 1,
|
|
734
|
-
perf: null,
|
|
735
|
-
vD: undefined,
|
|
736
|
-
manualReport: undefined,
|
|
737
|
-
lifeCycleId: uuid.v4(),
|
|
738
|
-
sessionId: getSessionId(),
|
|
383
|
+
for (let t = target || null; t && deepLength++ < 5 && !((elm = normalTarget(t)) === 'html');) {
|
|
384
|
+
// eslint-disable-next-line no-sequences
|
|
385
|
+
ret.push(elm), (t = t.parentNode);
|
|
386
|
+
}
|
|
387
|
+
return ret.reverse().join(' > ');
|
|
739
388
|
};
|
|
740
389
|
/**
|
|
741
|
-
* @description
|
|
390
|
+
* @description 处理html node
|
|
391
|
+
* @param e
|
|
742
392
|
*/
|
|
743
|
-
function
|
|
744
|
-
|
|
745
|
-
|
|
393
|
+
const normalTarget = function (target) {
|
|
394
|
+
let t, n, r, a, i;
|
|
395
|
+
const o = [];
|
|
396
|
+
if (!target || !target.tagName) {
|
|
397
|
+
return '';
|
|
746
398
|
}
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
399
|
+
o.push(target.tagName.toLowerCase());
|
|
400
|
+
if (target.id) {
|
|
401
|
+
o.push('#'.concat(target.id));
|
|
750
402
|
}
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
}
|
|
403
|
+
if ((t = target.className) && Object.prototype.toString.call(t) === '[object String]') {
|
|
404
|
+
for (n = t.split(/\s+/), i = 0; i < n.length; i++) {
|
|
405
|
+
// className包含active的不加入路径
|
|
406
|
+
if (n[i].indexOf('active') < 0)
|
|
407
|
+
o.push('.'.concat(n[i]));
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
const s = ['type', 'name', 'title', 'alt'];
|
|
411
|
+
for (i = 0; i < s.length; i++) {
|
|
412
|
+
r = s[i];
|
|
413
|
+
if ((a = target.getAttribute(r))) {
|
|
414
|
+
o.push('['.concat(r, '="').concat(a, '"]'));
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
return o.join('');
|
|
418
|
+
};
|
|
765
419
|
/**
|
|
766
|
-
* @description
|
|
767
|
-
* @param
|
|
768
|
-
* @param value Report 值
|
|
420
|
+
* @description 序列化对象
|
|
421
|
+
* @param obj
|
|
769
422
|
*/
|
|
770
|
-
function
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
error: null,
|
|
777
|
-
req: null,
|
|
778
|
-
manualReport: undefined,
|
|
779
|
-
perf: null,
|
|
780
|
-
});
|
|
423
|
+
function serialize(obj) {
|
|
424
|
+
const str = [];
|
|
425
|
+
for (const p in obj) {
|
|
426
|
+
if (Object.prototype.hasOwnProperty.call(obj, p) && typeof obj[p] !== 'undefined') {
|
|
427
|
+
const value = typeof obj[p] === 'object' ? JSON.stringify(obj[p]) : obj[p];
|
|
428
|
+
str.push(encodeURIComponent(p) + '=' + encodeURIComponent(value));
|
|
781
429
|
}
|
|
782
|
-
Report[key] = value;
|
|
783
430
|
}
|
|
431
|
+
return str.join('&');
|
|
784
432
|
}
|
|
785
433
|
/**
|
|
786
|
-
* @description
|
|
434
|
+
* @description 注册事件监听
|
|
435
|
+
* @param event 事件
|
|
436
|
+
* @param fn 回调方法
|
|
787
437
|
*/
|
|
788
|
-
function
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
uuid,
|
|
797
|
-
viewport,
|
|
798
|
-
screen: `${screen.width} x ${screen.height}`,
|
|
799
|
-
pageTitle: document.title,
|
|
800
|
-
referrer: document.referrer,
|
|
801
|
-
charset: document.charset,
|
|
802
|
-
customPayload: Config.customPayload,
|
|
803
|
-
dpr: window.devicePixelRatio,
|
|
804
|
-
});
|
|
805
|
-
}
|
|
806
|
-
function ajaxEventTrigger(event) {
|
|
807
|
-
const ajaxEvent = new CustomEvent(event, {
|
|
808
|
-
detail: this,
|
|
809
|
-
});
|
|
810
|
-
window.dispatchEvent(ajaxEvent);
|
|
811
|
-
}
|
|
812
|
-
const OldXHR = window.XMLHttpRequest;
|
|
813
|
-
function newXHR() {
|
|
814
|
-
const realXHR = new OldXHR();
|
|
815
|
-
realXHR.addEventListener('loadstart', function () {
|
|
816
|
-
ajaxEventTrigger.call(this, 'ajaxLoadStart');
|
|
817
|
-
}, false);
|
|
818
|
-
realXHR.addEventListener('loadend', function () {
|
|
819
|
-
ajaxEventTrigger.call(this, 'ajaxLoadEnd');
|
|
820
|
-
}, false);
|
|
821
|
-
// 此处的捕获的异常会连日志接口也一起捕获,如果日志上报接口异常了,就会导致死循环了。
|
|
822
|
-
realXHR.onerror = function (e) {
|
|
823
|
-
// eslint-disable-next-line no-console
|
|
824
|
-
console.warn('realXHR.onerror, e', e);
|
|
825
|
-
};
|
|
826
|
-
return realXHR;
|
|
827
|
-
}
|
|
438
|
+
const on = function (event, fn) {
|
|
439
|
+
if (window.addEventListener) {
|
|
440
|
+
window.addEventListener(event, fn, true);
|
|
441
|
+
}
|
|
442
|
+
else {
|
|
443
|
+
window.attachEvent(`on${event}`, fn);
|
|
444
|
+
}
|
|
445
|
+
};
|
|
828
446
|
/**
|
|
829
|
-
*
|
|
447
|
+
* @description 移除事件监听
|
|
448
|
+
* @param event 事件
|
|
449
|
+
* @param fn 回调方法
|
|
830
450
|
*/
|
|
831
|
-
const
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
if (timeRecordArrayCopy[i].event.detail && timeRecordArrayCopy[i].event.detail.status > 0) {
|
|
848
|
-
const currentTime = new Date().getTime();
|
|
849
|
-
const { responseURL, status, statusText, timeStamp } = timeRecordArrayCopy[i].event.detail;
|
|
850
|
-
const previousTime = timeStamp || timeRecordArrayCopy[i].timeStamp;
|
|
851
|
-
const loadTime = currentTime - previousTime;
|
|
852
|
-
const request = {
|
|
853
|
-
requestType: 'xhr',
|
|
854
|
-
responseURL,
|
|
855
|
-
status,
|
|
856
|
-
loadTime,
|
|
857
|
-
statusText,
|
|
858
|
-
reason: '',
|
|
859
|
-
detail: '',
|
|
860
|
-
};
|
|
861
|
-
if (loadTime && loadTime > XMLHttpRequestTimeout) {
|
|
862
|
-
request.reason = 'slow';
|
|
863
|
-
request.detail = `request is too slow, XMLHttpRequestTimeout: ${XMLHttpRequestTimeout}`;
|
|
864
|
-
}
|
|
865
|
-
else if (status && status >= 300 && statusText && statusText.toLowerCase() !== 'ok') {
|
|
866
|
-
request.reason = 'failed';
|
|
867
|
-
request.detail = `request is failed, status: ${status}, statusText: ${statusText}`;
|
|
868
|
-
}
|
|
869
|
-
else ;
|
|
870
|
-
if (request.reason) {
|
|
871
|
-
if (!tempUrlInfo[responseURL]) {
|
|
872
|
-
tempUrlInfo[responseURL] = true;
|
|
873
|
-
setTimeout(() => {
|
|
874
|
-
delete tempUrlInfo[responseURL];
|
|
875
|
-
}, 10);
|
|
876
|
-
setReportValue('error', null);
|
|
877
|
-
report(Object.assign(Object.assign({}, getReport()), {
|
|
878
|
-
page: location.href,
|
|
879
|
-
originPage: location.href,
|
|
880
|
-
type: 'request',
|
|
881
|
-
req: request,
|
|
882
|
-
}));
|
|
883
|
-
}
|
|
884
|
-
}
|
|
451
|
+
const off = function (event, fn) {
|
|
452
|
+
if (window.removeEventListener) {
|
|
453
|
+
return window.removeEventListener(event, fn);
|
|
454
|
+
}
|
|
455
|
+
else {
|
|
456
|
+
return window.detachEvent(`on${event}`, fn);
|
|
457
|
+
}
|
|
458
|
+
};
|
|
459
|
+
const formatTrackElementXPath = (xPath) => {
|
|
460
|
+
const result = [];
|
|
461
|
+
const array = xPath.split('/');
|
|
462
|
+
array.forEach((item) => {
|
|
463
|
+
if (!['svg', 'path', 'g', 'image', 'text', 'line', 'rect', 'polygon', 'circle', 'ellipse'].some((child) => {
|
|
464
|
+
if (item.toLowerCase().split('[')[0] === child) {
|
|
465
|
+
result.push(`/*[name()="${child}"]`);
|
|
466
|
+
return true;
|
|
885
467
|
}
|
|
886
|
-
|
|
887
|
-
|
|
468
|
+
else {
|
|
469
|
+
return false;
|
|
470
|
+
}
|
|
471
|
+
})) {
|
|
472
|
+
result.push(item);
|
|
888
473
|
}
|
|
889
474
|
});
|
|
475
|
+
return result.join('/');
|
|
890
476
|
};
|
|
891
|
-
const
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
477
|
+
const formatElementXPath = (info) => {
|
|
478
|
+
var _a;
|
|
479
|
+
let elementXPathValue = '';
|
|
480
|
+
const elementXPath = info.elementXPath;
|
|
481
|
+
if (elementXPath) {
|
|
482
|
+
const xpathElement = document.evaluate(formatTrackElementXPath(elementXPath), document).iterateNext();
|
|
483
|
+
elementXPathValue = xpathElement ? (_a = (xpathElement.textContent || xpathElement.value)) === null || _a === void 0 ? void 0 : _a.trim() : '';
|
|
484
|
+
}
|
|
485
|
+
return {
|
|
486
|
+
elementXPath,
|
|
487
|
+
elementXPathValue,
|
|
488
|
+
};
|
|
489
|
+
};
|
|
490
|
+
let existTrackListenEvent = {};
|
|
491
|
+
const elements = [];
|
|
492
|
+
let timeout;
|
|
493
|
+
const visualTrackFunc = () => {
|
|
494
|
+
if (Config.enableVisualTrack) {
|
|
495
|
+
if (timeout)
|
|
496
|
+
clearTimeout(timeout);
|
|
497
|
+
timeout = setTimeout(() => {
|
|
498
|
+
timeout = null;
|
|
499
|
+
const url = location.href.split('?')[0];
|
|
500
|
+
axios__default["default"].get(`${Config.reportUrl.replace('/s/r', '')}/visual/get_data_list?url=${encodeURIComponent(url)}`).then((res) => {
|
|
501
|
+
var _a, _b;
|
|
502
|
+
const listData = (_b = (_a = res.data.result) === null || _a === void 0 ? void 0 : _a.result) === null || _b === void 0 ? void 0 : _b.dataList;
|
|
503
|
+
if (Array.isArray(listData)) {
|
|
504
|
+
try {
|
|
505
|
+
listData.forEach((item) => {
|
|
506
|
+
if (item.trackType === 'mount') {
|
|
507
|
+
visualReportEvent(item);
|
|
508
|
+
}
|
|
509
|
+
else {
|
|
510
|
+
const element = document.evaluate(formatTrackElementXPath(item.trackElementXPath), document).iterateNext();
|
|
511
|
+
const key = url + item.trackElementXPath;
|
|
512
|
+
if (element) {
|
|
513
|
+
if (!existTrackListenEvent[key]) {
|
|
514
|
+
existTrackListenEvent[key] = true;
|
|
515
|
+
elements.push(element);
|
|
516
|
+
//@ts-ignore
|
|
517
|
+
element.setAttribute('visual-track-data-attr', JSON.stringify(item));
|
|
518
|
+
element.addEventListener('click', clickEventFunc);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
else {
|
|
522
|
+
const func = () => {
|
|
523
|
+
var _a;
|
|
524
|
+
const { browserName, browserVersion } = getNavigator();
|
|
525
|
+
const payload = {
|
|
526
|
+
projectID: Config.projectID,
|
|
527
|
+
version,
|
|
528
|
+
url,
|
|
529
|
+
trackElementXPath: item.trackElementXPath,
|
|
530
|
+
trackNameInfo: item.trackNameInfo,
|
|
531
|
+
params: item.params,
|
|
532
|
+
customPayload: Config.customPayload,
|
|
533
|
+
type: 'visual_track_xpath_not_found',
|
|
534
|
+
browser: `${browserName} - ${browserVersion}`,
|
|
535
|
+
};
|
|
536
|
+
if (typeof ((_a = window.navigator) === null || _a === void 0 ? void 0 : _a.sendBeacon) === 'function') {
|
|
537
|
+
const formData = new FormData();
|
|
538
|
+
for (const i in payload) {
|
|
539
|
+
if (Object.prototype.hasOwnProperty.call(payload, i)) {
|
|
540
|
+
const value = payload[i];
|
|
541
|
+
formData.append(i, value && typeof value === 'object' ? JSON.stringify(value) : value);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
window.navigator.sendBeacon(Config.reportUrl, formData);
|
|
545
|
+
}
|
|
546
|
+
else {
|
|
547
|
+
new Image().src = `${Config.reportUrl}?${serialize(payload)}`;
|
|
548
|
+
}
|
|
549
|
+
};
|
|
550
|
+
if (typeof window.requestIdleCallback === 'function') {
|
|
551
|
+
window.requestIdleCallback(() => func(), { timeout: 2000 });
|
|
552
|
+
}
|
|
553
|
+
else {
|
|
554
|
+
setTimeout(() => func(), 0);
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
});
|
|
934
559
|
}
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
setTimeout(() => {
|
|
939
|
-
delete tempUrlInfo[url];
|
|
940
|
-
}, 10);
|
|
941
|
-
setReportValue('error', null);
|
|
942
|
-
report(Object.assign(Object.assign({}, getReport()), {
|
|
943
|
-
page: location.href,
|
|
944
|
-
originPage: location.href,
|
|
945
|
-
type: 'request',
|
|
946
|
-
req: request,
|
|
947
|
-
}));
|
|
948
|
-
}
|
|
560
|
+
catch (err) {
|
|
561
|
+
// eslint-disable-next-line no-console
|
|
562
|
+
console.log('visual track evaluate err', err);
|
|
949
563
|
}
|
|
950
|
-
}
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
564
|
+
}
|
|
565
|
+
});
|
|
566
|
+
}, 1000);
|
|
567
|
+
}
|
|
568
|
+
};
|
|
569
|
+
const visualReportEvent = (item) => {
|
|
570
|
+
var _a;
|
|
571
|
+
const { elementXPath, elementXPathValue } = formatElementXPath(item.trackNameInfo);
|
|
572
|
+
const visualTrackData = {
|
|
573
|
+
trackType: item.trackType,
|
|
574
|
+
trackName: elementXPath ? elementXPathValue : item.trackNameInfo.name,
|
|
575
|
+
params: (_a = item.params) === null || _a === void 0 ? void 0 : _a.map((child) => {
|
|
576
|
+
const { elementXPath, elementXPathValue } = formatElementXPath(child);
|
|
577
|
+
return {
|
|
578
|
+
name: child.name,
|
|
579
|
+
value: elementXPath ? elementXPathValue : child.value,
|
|
580
|
+
};
|
|
581
|
+
}),
|
|
582
|
+
};
|
|
583
|
+
setReportValue('type', 'visual');
|
|
584
|
+
setReportValue('vD', visualTrackData);
|
|
585
|
+
report(getReport());
|
|
586
|
+
setReportValue('vD', undefined);
|
|
587
|
+
};
|
|
588
|
+
const clickEventFunc = (e) => {
|
|
589
|
+
//@ts-ignore
|
|
590
|
+
const item = JSON.parse(e.target.getAttribute('visual-track-data-attr'));
|
|
591
|
+
visualReportEvent(item);
|
|
592
|
+
};
|
|
593
|
+
const initWindowObjectFunction = () => {
|
|
594
|
+
if (typeof window.manualReportTrackFunc === 'undefined') {
|
|
595
|
+
window.manualReportTrackFunc = (data) => {
|
|
596
|
+
if (Object.prototype.toString.call(data) !== '[object Object]') {
|
|
597
|
+
// eslint-disable-next-line no-console
|
|
598
|
+
console.warn('manualReportTrackFunc参数必须是一个对象!');
|
|
599
|
+
}
|
|
600
|
+
else {
|
|
601
|
+
setReportValue('type', 'manual');
|
|
602
|
+
setReportValue('manualReport', data);
|
|
603
|
+
report(getReport());
|
|
604
|
+
setReportValue('manualReport', undefined);
|
|
605
|
+
}
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
if (typeof window.getFullScreenShootFunc === 'undefined') {
|
|
609
|
+
//下载页面截图
|
|
610
|
+
window.getFullScreenShootFunc = (filename) => __awaiter(void 0, void 0, void 0, function* () {
|
|
611
|
+
const file = yield getFullScreenShoot(filename);
|
|
612
|
+
const url = window.URL.createObjectURL(file);
|
|
613
|
+
const tagA = document.createElement('a');
|
|
614
|
+
tagA.setAttribute('href', url);
|
|
615
|
+
tagA.setAttribute('download', file.name);
|
|
616
|
+
tagA.setAttribute('target', '_blank');
|
|
617
|
+
document.body.appendChild(tagA);
|
|
618
|
+
tagA.click();
|
|
619
|
+
document.body.removeChild(tagA);
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
if (typeof window.getRRWebUserEventsCaptureFunc === 'undefined') {
|
|
623
|
+
window.getRRWebUserEventsCaptureFunc = () => {
|
|
624
|
+
setReportValue('type', 'error');
|
|
625
|
+
setReportValue('error', {
|
|
626
|
+
subType: 'manual',
|
|
627
|
+
message: 'manual trigger',
|
|
628
|
+
events: getUserEvents(true),
|
|
629
|
+
});
|
|
630
|
+
report(getReport());
|
|
631
|
+
};
|
|
632
|
+
}
|
|
633
|
+
};
|
|
634
|
+
let prewHref = '';
|
|
635
|
+
const handleLocationChange = (e) => {
|
|
636
|
+
const curHref = location.href;
|
|
637
|
+
// href中`?`后面的值改变,不触发后续动作
|
|
638
|
+
if (prewHref.split('?')[0] === curHref.split('?')[0]) {
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
prewHref = curHref;
|
|
642
|
+
handleHistoryChange();
|
|
643
|
+
elements.forEach((element) => {
|
|
644
|
+
element === null || element === void 0 ? void 0 : element.removeEventListener('click', clickEventFunc);
|
|
645
|
+
});
|
|
646
|
+
//清空可视化埋点之前的数据
|
|
647
|
+
existTrackListenEvent = {};
|
|
648
|
+
elements.length = 0;
|
|
649
|
+
visualTrackFunc();
|
|
650
|
+
};
|
|
651
|
+
|
|
652
|
+
/** 上报数据映射 */
|
|
653
|
+
const reportMap = {
|
|
654
|
+
/** SDK 版本 */
|
|
655
|
+
version: '_v',
|
|
656
|
+
/** 项目ID */
|
|
657
|
+
projectID: 'pid',
|
|
658
|
+
/** location.host */
|
|
659
|
+
host: 'host',
|
|
660
|
+
/** 用户uuid */
|
|
661
|
+
uuid: '_id',
|
|
662
|
+
/** 上报日志的时间戳 */
|
|
663
|
+
time: 'time',
|
|
664
|
+
/** 源页面 */
|
|
665
|
+
originPage: 'o',
|
|
666
|
+
/** 当前页面 */
|
|
667
|
+
page: 'p',
|
|
668
|
+
/** 自定义payload */
|
|
669
|
+
customPayload: 'cP',
|
|
670
|
+
/** 信息类型 */
|
|
671
|
+
type: 't',
|
|
672
|
+
/** viewport的宽高 */
|
|
673
|
+
viewport: 'vp',
|
|
674
|
+
/** 屏幕宽高 */
|
|
675
|
+
screen: 'sc',
|
|
676
|
+
/** 页面title */
|
|
677
|
+
pageTitle: 'pt',
|
|
678
|
+
/** 页面referrer */
|
|
679
|
+
referrer: 'ref',
|
|
680
|
+
/** 页面编码 */
|
|
681
|
+
charset: 'char',
|
|
682
|
+
/** language */
|
|
683
|
+
language: 'lan',
|
|
684
|
+
/** vendor, 浏览器厂商 */
|
|
685
|
+
navigatorVendor: 'nV',
|
|
686
|
+
/** 网络连接类型 */
|
|
687
|
+
connectionType: 'cT',
|
|
688
|
+
/** 浏览器名称 */
|
|
689
|
+
browserName: 'bN',
|
|
690
|
+
/** 浏览器版本 */
|
|
691
|
+
browserVersion: 'bV',
|
|
692
|
+
/** 浏览器引擎名称 */
|
|
693
|
+
engineName: 'enN',
|
|
694
|
+
/** 浏览器引擎版本 */
|
|
695
|
+
engineVersion: 'enV',
|
|
696
|
+
/** 操作系统名称 */
|
|
697
|
+
osName: 'osN',
|
|
698
|
+
/** 操作系统版本 */
|
|
699
|
+
osVersion: 'osV',
|
|
700
|
+
pv: {
|
|
701
|
+
/** 停留时间 */
|
|
702
|
+
stayTime: 'sT',
|
|
703
|
+
},
|
|
704
|
+
lag: {
|
|
705
|
+
/** 页面卡顿时间 */
|
|
706
|
+
lagTime: 'lT',
|
|
707
|
+
},
|
|
708
|
+
error: {
|
|
709
|
+
/** 子类型 */
|
|
710
|
+
subType: 'suT',
|
|
711
|
+
/** 错误行 */
|
|
712
|
+
line: 'line',
|
|
713
|
+
/** 错误列 */
|
|
714
|
+
column: 'col',
|
|
715
|
+
/** 错误信息 */
|
|
716
|
+
message: 'msg',
|
|
717
|
+
/** 错误文件 */
|
|
718
|
+
filename: 'fil',
|
|
719
|
+
/** 错误函数名 */
|
|
720
|
+
functionName: 'fn',
|
|
721
|
+
/** 错误栈信息 */
|
|
722
|
+
stack: '_st',
|
|
723
|
+
/** 错误帧 */
|
|
724
|
+
stackFrame: '_sf',
|
|
725
|
+
/** 视频录制数据 */
|
|
726
|
+
events: 'evt',
|
|
727
|
+
/** 页面截图 */
|
|
728
|
+
picture: 'pic',
|
|
729
|
+
},
|
|
730
|
+
ua: {
|
|
731
|
+
/** 子类型 */
|
|
732
|
+
subType: 'suT',
|
|
733
|
+
/** x轴坐标 */
|
|
734
|
+
x: 'x',
|
|
735
|
+
/** y轴坐标 */
|
|
736
|
+
y: 'y',
|
|
737
|
+
/** 元素路径 */
|
|
738
|
+
path: 'path',
|
|
739
|
+
},
|
|
740
|
+
/** 性能数据 */
|
|
741
|
+
perf: {
|
|
742
|
+
navigation: {
|
|
743
|
+
/**
|
|
744
|
+
*一个无符号短整型,表示是如何导航到这个页面的。可能的值如下:
|
|
745
|
+
TYPE_NAVIGATE (0)
|
|
746
|
+
当前页面是通过点击链接,书签和表单提交,或者脚本操作,或者在url中直接输入地址,type值为0
|
|
747
|
+
TYPE_RELOAD (1)
|
|
748
|
+
点击刷新页面按钮或者通过Location.reload()方法显示的页面,type值为1
|
|
749
|
+
The page was accessed by clicking the Reload button or via the Location.reload() method.
|
|
750
|
+
TYPE_BACK_FORWARD (2)
|
|
751
|
+
页面通过历史记录和前进后退访问时。type值为2
|
|
752
|
+
TYPE_RESERVED (255)
|
|
753
|
+
任何其他方式,type值为255
|
|
754
|
+
*/
|
|
755
|
+
type: 't',
|
|
756
|
+
/** 无符号短整型,表示在到达这个页面之前重定向了多少次。 */
|
|
757
|
+
redirectCount: 'rc',
|
|
758
|
+
},
|
|
759
|
+
memory: {
|
|
760
|
+
/** 上下文内可用堆的最大体积,以字节计算。 */
|
|
761
|
+
jsHeapSizeLimit: 'hsl',
|
|
762
|
+
/** 已分配的堆体积,以字节计算。 */
|
|
763
|
+
totalJSHeapSize: 'ths',
|
|
764
|
+
/** 当前 JS 堆活跃段(segment)的体积,以字节计算。 */
|
|
765
|
+
usedJSHeapSize: 'uhs',
|
|
766
|
+
},
|
|
767
|
+
/** 返回性能测量开始时的时间的高精度时间戳。 */
|
|
768
|
+
timeOrigin: 'to',
|
|
769
|
+
timing: {
|
|
770
|
+
/** 是一个无符号long long 型的毫秒数,返回浏览器与服务器之间的连接建立时的Unix毫秒时间戳。如果建立的是持久连接,则返回值等同于fetchStart属性的值。连接建立指的是所有握手和认证过程全部结束。 */
|
|
771
|
+
connectEnd: 'ce',
|
|
772
|
+
/** 是一个无符号long long 型的毫秒数,返回HTTP请求开始向服务器发送时的Unix毫秒时间戳。如果使用持久连接(persistent connection),则返回值等同于fetchStart属性的值 */
|
|
773
|
+
connectStart: 'cs',
|
|
774
|
+
/** 是一个无符号long long 型的毫秒数,返回当前文档解析完成,即Document.readyState 变为 'complete'且相对应的readystatechange (en-US) 被触发时的Unix毫秒时间戳。 */
|
|
775
|
+
domComplete: 'dc',
|
|
776
|
+
/** 是一个无符号long long 型的毫秒数,返回当所有需要立即执行的脚本已经被执行(不论执行顺序)时的Unix毫秒时间戳。 */
|
|
777
|
+
domContentLoadedEventEnd: 'scLee',
|
|
778
|
+
/** 是一个无符号long long 型的毫秒数,返回当解析器发送DOMContentLoaded (en-US) 事件,即所有需要被执行的脚本已经被解析时的Unix毫秒时间戳。 */
|
|
779
|
+
domContentLoadedEventStart: 'dcLes',
|
|
780
|
+
/** 是一个无符号long long 型的毫秒数,返回当前网页DOM结构结束解析、开始加载内嵌资源时(即Document.readyState属性变为“interactive”、
|
|
781
|
+
* 相应的readystatechange (en-US)事件触发时)的Unix毫秒时间戳。 */
|
|
782
|
+
domInteractive: 'di',
|
|
783
|
+
/** 是一个无符号long long 型的毫秒数,返回当前网页DOM结构开始解析时(即Document.readyState属性变为“loading”、相应的 readystatechange (en-US)事件触发时)的Unix毫秒时间戳。 */
|
|
784
|
+
domLoading: 'dl',
|
|
785
|
+
/** 是一个无符号long long 型的毫秒数,表征了域名查询结束的UNIX时间戳。如果使用了持续连接(persistent connection),
|
|
786
|
+
* 或者这个信息存储到了缓存或者本地资源上,这个值将和 PerformanceTiming.fetchStart一致。 */
|
|
787
|
+
domainLookupEnd: 'dle',
|
|
788
|
+
/** 是一个无符号long long 型的毫秒数,表征了域名查询开始的UNIX时间戳。如果使用了持续连接(persistent connection),
|
|
789
|
+
* 或者这个信息存储到了缓存或者本地资源上,这个值将和 PerformanceTiming.fetchStart一致。 */
|
|
790
|
+
domainLookupStart: 'dls',
|
|
791
|
+
/** 是一个无符号long long 型的毫秒数,表征了浏览器准备好使用HTTP请求来获取(fetch)文档的UNIX时间戳。这个时间点会在检查任何应用缓存之前。 */
|
|
792
|
+
fetchStart: 'fs',
|
|
793
|
+
/** 是一个无符号long long 型的毫秒数,返回当load (en-US)事件结束,即加载事件完成时的Unix毫秒时间戳。如果这个事件还未被发送,或者尚未完成,它的值将会是0. */
|
|
794
|
+
loadEventEnd: 'lee',
|
|
795
|
+
/** 是一个无符号long long 型的毫秒数,返回该文档下,load (en-US)事件被发送时的Unix毫秒时间戳。如果这个事件还未被发送,它的值将会是0。 */
|
|
796
|
+
loadEventStart: 'les',
|
|
797
|
+
/** 是一个无符号long long 型的毫秒数,表征了从同一个浏览器上下文的上一个文档卸载(unload)结束时的UNIX时间戳。如果没有上一个文档,这个值会和PerformanceTiming.fetchStart相同 */
|
|
798
|
+
navigationStart: 'ns',
|
|
799
|
+
/** 是一个无符号long long 型的毫秒数,表征了最后一个HTTP重定向完成时(也就是说是HTTP响应的最后一个比特直接被收到的时间)的UNIX时间戳。如果没有重定向,或者重定向中的一个不同源,这个值会返回0. */
|
|
800
|
+
redirectEnd: 're',
|
|
801
|
+
/** 是一个无符号long long 型的毫秒数,表征了第一个HTTP重定向开始时的UNIX时间戳。如果没有重定向,或者重定向中的一个不同源,这个值会返回0. */
|
|
802
|
+
redirectStart: 'redS',
|
|
803
|
+
/** 是一个无符号long long 型的毫秒数,返回浏览器向服务器发出HTTP请求时(或开始读取本地缓存时)的Unix毫秒时间戳。 */
|
|
804
|
+
requestStart: 'reqS',
|
|
805
|
+
/** 是一个无符号long long 型的毫秒数,返回浏览器从服务器收到(或从本地缓存读取,或从本地资源读取)最后一个字节时(如果在此之前HTTP连接已经关闭,则返回关闭时)的Unix毫秒时间戳。 */
|
|
806
|
+
responseEnd: 'resE',
|
|
807
|
+
/** 是一个无符号long long 型的毫秒数,返回浏览器从服务器收到(或从本地缓存读取)第一个字节时的Unix毫秒时间戳。如果传输层在开始请求之后失败并且连接被重开,该属性将会被数制成新的请求的相对应的发起时间。 */
|
|
808
|
+
responseStart: 'resS',
|
|
809
|
+
/** 是一个无符号long long 型的毫秒数,返回浏览器与服务器开始安全链接的握手时的Unix毫秒时间戳。如果当前网页不要求安全连接,则返回0。 */
|
|
810
|
+
secureConnectionStart: 'scs',
|
|
811
|
+
/** 是一个无符号long long 型的毫秒数,表征了unload (en-US)事件处理完成时的UNIX时间戳。如果没有上一个文档,
|
|
812
|
+
* or if the previous document, or one of the needed redirects, is not of the same origin, 这个值会返回0. */
|
|
813
|
+
unloadEventEnd: 'uee',
|
|
814
|
+
/** 是一个无符号long long 型的毫秒数,表征了unload (en-US)事件抛出时的UNIX时间戳。如果没有上一个文档,
|
|
815
|
+
* or if the previous document, or one of the needed redirects, is not of the same origin, 这个值会返回0. */
|
|
816
|
+
unloadEventStart: 'ees',
|
|
817
|
+
},
|
|
818
|
+
},
|
|
819
|
+
/** api请求相关 */
|
|
820
|
+
req: {
|
|
821
|
+
/** 请求类型,xhr或fetch */
|
|
822
|
+
requestType: 'rT',
|
|
823
|
+
/** 请求地址 */
|
|
824
|
+
responseURL: 'url',
|
|
825
|
+
/** 请求结果 */
|
|
826
|
+
status: 'stat',
|
|
827
|
+
/** 请求耗时 */
|
|
828
|
+
loadTime: 'lT',
|
|
829
|
+
/** 请求状态 */
|
|
830
|
+
statusText: 'sT',
|
|
831
|
+
/** 上报原因, slow或failed */
|
|
832
|
+
reason: 're',
|
|
833
|
+
/** 上报时带上的详细信息 */
|
|
834
|
+
detail: 'de',
|
|
835
|
+
/** 上报时带上的请求参数 */
|
|
836
|
+
requestData: 'rD',
|
|
837
|
+
/** 上报时带上的请求方法 */
|
|
838
|
+
method: 'rM',
|
|
839
|
+
},
|
|
840
|
+
vD: {
|
|
841
|
+
/** 埋点名称 */
|
|
842
|
+
trackName: 'tN',
|
|
843
|
+
/** 埋点参数 */
|
|
844
|
+
params: 'params',
|
|
845
|
+
},
|
|
846
|
+
manualReport: 'mR',
|
|
847
|
+
lifeCycleId: 'li',
|
|
848
|
+
sessionId: 'sI',
|
|
849
|
+
};
|
|
850
|
+
const xhrFunc = (filename, userId, asyncUpdateId, file, res, callback, rest) => {
|
|
851
|
+
const formData = new FormData();
|
|
852
|
+
for (const i in rest) {
|
|
853
|
+
if (Object.prototype.hasOwnProperty.call(rest, i)) {
|
|
854
|
+
formData.append(i, rest[i]);
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
formData.append('filename', filename);
|
|
858
|
+
formData.append('asyncUpdateId', asyncUpdateId); //用于上传到oss后异步更新到db的id
|
|
859
|
+
formData.append('userId', userId);
|
|
860
|
+
formData.append('files', file);
|
|
861
|
+
const xhr = new XMLHttpRequest();
|
|
862
|
+
xhr.addEventListener('error', () => res(), false);
|
|
863
|
+
xhr.open('POST', Config.reportUrl);
|
|
864
|
+
xhr.onreadystatechange = function () {
|
|
865
|
+
var _a;
|
|
866
|
+
if (xhr.readyState === 4) {
|
|
867
|
+
if (xhr.status === 200) {
|
|
868
|
+
try {
|
|
869
|
+
const response = xhr.responseText;
|
|
870
|
+
const resp = JSON.parse(response);
|
|
871
|
+
callback((_a = resp.result) === null || _a === void 0 ? void 0 : _a.response);
|
|
872
|
+
}
|
|
873
|
+
catch (_err) {
|
|
874
|
+
}
|
|
875
|
+
res();
|
|
876
|
+
}
|
|
877
|
+
else {
|
|
878
|
+
res();
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
};
|
|
882
|
+
xhr.send(formData);
|
|
883
|
+
};
|
|
884
|
+
const ERROR_MESSAGE_MAP = {};
|
|
885
|
+
function reportFunc(data) {
|
|
886
|
+
var _a, _b, _c;
|
|
887
|
+
const payload = convertToSchema(data, reportMap);
|
|
888
|
+
payload.time = new Date().getTime(); // 设置上报时间
|
|
889
|
+
let picturePromise = Promise.resolve();
|
|
890
|
+
let hasErrorEvent = false;
|
|
891
|
+
const filename = uuid.v4();
|
|
892
|
+
if (data.type === 'error') {
|
|
893
|
+
let message = ((_a = data.error) === null || _a === void 0 ? void 0 : _a.message) || 'default';
|
|
894
|
+
if (((_b = data.error) === null || _b === void 0 ? void 0 : _b.subType) === 'resource') {
|
|
895
|
+
message = 'resource';
|
|
896
|
+
}
|
|
897
|
+
if (ERROR_MESSAGE_MAP[message]) ;
|
|
898
|
+
else {
|
|
899
|
+
ERROR_MESSAGE_MAP[message] = true;
|
|
900
|
+
setTimeout(() => {
|
|
901
|
+
//节流效果,每隔3秒上报一次重复的错误录像和错误截图
|
|
902
|
+
delete ERROR_MESSAGE_MAP[message];
|
|
903
|
+
}, 3000);
|
|
904
|
+
const { p, pid, host, bN, pt } = payload;
|
|
905
|
+
if ((_c = data.error) === null || _c === void 0 ? void 0 : _c.events) {
|
|
906
|
+
hasErrorEvent = true;
|
|
907
|
+
const errorEvent = data.error.events;
|
|
908
|
+
const delayTime = Math.ceil(Math.random() * 10000);
|
|
909
|
+
setTimeout(() => {
|
|
910
|
+
const blob = new Blob([errorEvent], { type: 'text/json' });
|
|
911
|
+
return new Promise((res) => {
|
|
912
|
+
xhrFunc(`${filename}.json`, data.uuid, filename, blob, res, () => { }, { p, pid, host, bN, pt });
|
|
913
|
+
});
|
|
914
|
+
}, delayTime < 3500 ? 3500 : delayTime);
|
|
915
|
+
}
|
|
916
|
+
picturePromise = getFullScreenShoot(filename)
|
|
917
|
+
.then((file) => {
|
|
918
|
+
return new Promise((res) => {
|
|
919
|
+
xhrFunc(file.name, data.uuid, filename, file, res, (result) => {
|
|
920
|
+
payload.error.pic = result;
|
|
921
|
+
}, { p, pid, host, bN, pt });
|
|
954
922
|
});
|
|
955
|
-
return res;
|
|
956
923
|
})
|
|
957
924
|
.catch((err) => {
|
|
958
|
-
|
|
959
|
-
console.log('hackFetch err', err);
|
|
925
|
+
payload.error.picError = (err === null || err === void 0 ? void 0 : err.stack) || (err === null || err === void 0 ? void 0 : err.toString());
|
|
960
926
|
});
|
|
961
|
-
}
|
|
962
|
-
}
|
|
963
|
-
};
|
|
964
|
-
|
|
965
|
-
/**
|
|
966
|
-
* @description 点击事件触发后的操作
|
|
967
|
-
*/
|
|
968
|
-
function handleClick(event) {
|
|
969
|
-
const target = event.target;
|
|
970
|
-
if (target.nodeName === 'INPUT' || target.nodeName === 'TEXTAREA') {
|
|
971
|
-
return;
|
|
972
|
-
}
|
|
973
|
-
const path = getElmPath(target);
|
|
974
|
-
if (path) {
|
|
975
|
-
setReportValue('type', 'ua');
|
|
976
|
-
setReportValue('ua', {
|
|
977
|
-
subType: 'ui.click',
|
|
978
|
-
x: event.x,
|
|
979
|
-
y: event.y,
|
|
980
|
-
path,
|
|
981
|
-
});
|
|
982
|
-
report(getReport());
|
|
927
|
+
}
|
|
983
928
|
}
|
|
929
|
+
return picturePromise
|
|
930
|
+
.then(() => {
|
|
931
|
+
var _a;
|
|
932
|
+
if ((_a = payload.error) === null || _a === void 0 ? void 0 : _a.evt)
|
|
933
|
+
delete payload.error.evt;
|
|
934
|
+
if (window && window.navigator && typeof window.navigator.sendBeacon === 'function') {
|
|
935
|
+
const formData = new FormData();
|
|
936
|
+
Object.keys(payload).forEach((key) => {
|
|
937
|
+
let value = payload[key];
|
|
938
|
+
if (value !== null && value !== undefined) {
|
|
939
|
+
if (typeof value === 'object') {
|
|
940
|
+
value = JSON.stringify(value);
|
|
941
|
+
}
|
|
942
|
+
formData.append(key, value);
|
|
943
|
+
}
|
|
944
|
+
});
|
|
945
|
+
//eventErrorFilename,将错误录像的文件名附在错误上报的埋点里,便于将错误录像上报回调地址异步的更新到db
|
|
946
|
+
if (hasErrorEvent)
|
|
947
|
+
formData.append('eef', filename);
|
|
948
|
+
window.navigator.sendBeacon(Config.reportUrl, formData);
|
|
949
|
+
}
|
|
950
|
+
else {
|
|
951
|
+
if (hasErrorEvent)
|
|
952
|
+
payload.eef = filename;
|
|
953
|
+
new Image().src = `${Config.reportUrl}?${serialize(payload)}`;
|
|
954
|
+
}
|
|
955
|
+
})
|
|
956
|
+
.catch((_err) => {
|
|
957
|
+
//
|
|
958
|
+
});
|
|
959
|
+
}
|
|
960
|
+
// 上报
|
|
961
|
+
function report(data) {
|
|
962
|
+
return new Promise((res) => {
|
|
963
|
+
//在帧的空闲时间上报
|
|
964
|
+
const dataCopy = JSON.parse(JSON.stringify(data));
|
|
965
|
+
if (typeof window.requestIdleCallback === 'function') {
|
|
966
|
+
window.requestIdleCallback(() => reportFunc(dataCopy).then(res), { timeout: 2000 });
|
|
967
|
+
}
|
|
968
|
+
else {
|
|
969
|
+
setTimeout(() => reportFunc(dataCopy).then(res), 0);
|
|
970
|
+
}
|
|
971
|
+
});
|
|
984
972
|
}
|
|
985
973
|
/**
|
|
986
|
-
* @description
|
|
974
|
+
* @description 将数据转换成最终提交的数据,主要目的是简化数据的key的长度,从而降低荷载大小
|
|
975
|
+
* @param data 要转换的数据
|
|
976
|
+
* @param map 要转换的数据的字段映射
|
|
977
|
+
* @param redundancyData 冗余数据
|
|
987
978
|
*/
|
|
988
|
-
function
|
|
989
|
-
const
|
|
990
|
-
if (
|
|
991
|
-
|
|
979
|
+
function convertToSchema(data, map, redundancyData) {
|
|
980
|
+
const reportData = {};
|
|
981
|
+
if (redundancyData) {
|
|
982
|
+
map = Object.assign(Object.assign({}, map), redundancyData);
|
|
992
983
|
}
|
|
993
|
-
const
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
984
|
+
for (const result of Object.entries(map)) {
|
|
985
|
+
const [key, mapKey] = result;
|
|
986
|
+
if (key === 'manualReport') {
|
|
987
|
+
reportData[mapKey] = data[key];
|
|
988
|
+
}
|
|
989
|
+
else {
|
|
990
|
+
//字段值为数组的场景,直接转换
|
|
991
|
+
if (typeof data[key] === 'object' && !Array.isArray(data[key])) {
|
|
992
|
+
if (data[key]) {
|
|
993
|
+
if (redundancyData && Object.prototype.hasOwnProperty.call(redundancyData, key)) {
|
|
994
|
+
reportData[key] = data[key];
|
|
995
|
+
}
|
|
996
|
+
else {
|
|
997
|
+
reportData[key] = convertToSchema(data[key], map[key]);
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
else {
|
|
1001
|
+
reportData[key] = data[key];
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
else {
|
|
1005
|
+
// 排除undefined
|
|
1006
|
+
if (data[key] !== undefined) {
|
|
1007
|
+
reportData[mapKey] = data[key];
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1004
1011
|
}
|
|
1005
|
-
|
|
1006
|
-
/**
|
|
1007
|
-
* @description 滚动的操作
|
|
1008
|
-
*/
|
|
1009
|
-
let timeout$1;
|
|
1010
|
-
function handleScroll(_event) {
|
|
1011
|
-
clearTimeout(timeout$1);
|
|
1012
|
-
timeout$1 = setTimeout(() => {
|
|
1013
|
-
setReportValue('type', 'ua');
|
|
1014
|
-
setReportValue('ua', {
|
|
1015
|
-
subType: 'ui.scroll',
|
|
1016
|
-
});
|
|
1017
|
-
report(getReport());
|
|
1018
|
-
}, 1000);
|
|
1012
|
+
return reportData;
|
|
1019
1013
|
}
|
|
1020
1014
|
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
function getUid() {
|
|
1026
|
-
let uid = localStorage.getItem(monitorTrackId) || '';
|
|
1027
|
-
if (!uid) {
|
|
1028
|
-
uid = uuid.v4();
|
|
1029
|
-
localStorage.setItem(monitorTrackId, uid);
|
|
1030
|
-
}
|
|
1031
|
-
return uid;
|
|
1032
|
-
}
|
|
1015
|
+
/** 路由栈数组,存储路由变化信息 */
|
|
1016
|
+
let routerStack = [];
|
|
1017
|
+
/** 路由触发的时间 */
|
|
1018
|
+
let time = 0;
|
|
1033
1019
|
/**
|
|
1034
|
-
* @description
|
|
1020
|
+
* @description 派发pushState, replaceState 的监听
|
|
1021
|
+
* @param type
|
|
1035
1022
|
*/
|
|
1036
|
-
function
|
|
1037
|
-
const
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
const
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1023
|
+
function _history(type) {
|
|
1024
|
+
const origin = history[type];
|
|
1025
|
+
return function () {
|
|
1026
|
+
// @ts-ignore
|
|
1027
|
+
// eslint-disable-next-line prefer-rest-params
|
|
1028
|
+
const r = origin.apply(this, arguments);
|
|
1029
|
+
const e = new Event(type);
|
|
1030
|
+
// @ts-ignore
|
|
1031
|
+
// eslint-disable-next-line prefer-rest-params
|
|
1032
|
+
e.arguments = arguments;
|
|
1033
|
+
window.dispatchEvent(e);
|
|
1034
|
+
return r;
|
|
1035
|
+
};
|
|
1046
1036
|
}
|
|
1047
1037
|
/**
|
|
1048
|
-
* @description
|
|
1038
|
+
* @description 处理history变化
|
|
1039
|
+
* @param e history事件
|
|
1049
1040
|
*/
|
|
1050
|
-
function
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
return {
|
|
1054
|
-
language: navigator.language,
|
|
1055
|
-
navigatorVendor: navigator.vendor,
|
|
1056
|
-
connectionType: ((_a = navigator === null || navigator === void 0 ? void 0 : navigator.connection) === null || _a === void 0 ? void 0 : _a.effectiveType) || '2g',
|
|
1057
|
-
browserName: uaResult.browser.name || '',
|
|
1058
|
-
browserVersion: uaResult.browser.version || '',
|
|
1059
|
-
engineName: uaResult.engine.name || '',
|
|
1060
|
-
engineVersion: uaResult.engine.version || '',
|
|
1061
|
-
osName: uaResult.os.name || '',
|
|
1062
|
-
osVersion: uaResult.os.version || '',
|
|
1063
|
-
};
|
|
1041
|
+
function handleHistoryChange(__e) {
|
|
1042
|
+
visualTrackFunc();
|
|
1043
|
+
setReport({ type: 'pv' });
|
|
1064
1044
|
}
|
|
1065
1045
|
/**
|
|
1066
|
-
*
|
|
1046
|
+
* 设置信息并触发上报
|
|
1067
1047
|
*/
|
|
1068
|
-
function
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1048
|
+
function setReport({ type, lagTime }) {
|
|
1049
|
+
let pageUrl = location.href;
|
|
1050
|
+
/**
|
|
1051
|
+
* 此判断的逻辑是因为spa且hash模式下,路由搜索参数更改也会触发handleHistoryChange事件
|
|
1052
|
+
* 所以路由栈中的信息只保存无搜索参数的地址
|
|
1053
|
+
*/
|
|
1054
|
+
if (Config.hash && Config.spa) {
|
|
1055
|
+
const currentUrlSplit = location.href.split('#');
|
|
1056
|
+
let currentUrlPostfix = '';
|
|
1057
|
+
if (currentUrlSplit[1]) {
|
|
1058
|
+
const index = currentUrlSplit[1].indexOf('?');
|
|
1059
|
+
if (index > -1) {
|
|
1060
|
+
currentUrlPostfix = currentUrlSplit[1].substring(0, index);
|
|
1061
|
+
}
|
|
1062
|
+
else {
|
|
1063
|
+
currentUrlPostfix = currentUrlSplit[1];
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
const currentUrlResult = `${currentUrlSplit[0]}#${currentUrlPostfix}`;
|
|
1067
|
+
pageUrl = currentUrlResult;
|
|
1068
|
+
}
|
|
1069
|
+
const { originPage, page } = setRouteStack(pageUrl);
|
|
1070
|
+
// 愿页面等于当前页面,说明无路由变更。只有spa且hash模式下才会出现
|
|
1071
|
+
if (originPage === page && type === 'pv') {
|
|
1072
|
+
return;
|
|
1073
|
+
}
|
|
1074
|
+
const currentTime = new Date().getTime();
|
|
1075
|
+
// 页面停留时间
|
|
1076
|
+
const stayTime = currentTime - time;
|
|
1077
|
+
// 设置触发路由变化的时间
|
|
1078
|
+
time = currentTime;
|
|
1079
|
+
// 设置信息
|
|
1080
|
+
setReportValue('originPage', originPage);
|
|
1081
|
+
setReportValue('page', page);
|
|
1082
|
+
if (type === 'pv') {
|
|
1083
|
+
setReportValue('type', 'pv');
|
|
1084
|
+
setReportValue('pv', { stayTime });
|
|
1085
|
+
}
|
|
1086
|
+
else if (type === 'lag') {
|
|
1087
|
+
setReportValue('type', 'lag');
|
|
1088
|
+
setReportValue('lag', { lagTime: lagTime });
|
|
1089
|
+
}
|
|
1090
|
+
// 上报数据
|
|
1091
|
+
report(getReport());
|
|
1092
|
+
}
|
|
1093
|
+
function setPVTime() {
|
|
1094
|
+
time = new Date().getTime();
|
|
1072
1095
|
}
|
|
1073
1096
|
/**
|
|
1074
|
-
* @description
|
|
1075
|
-
* @param
|
|
1097
|
+
* @description 添加路由栈信息
|
|
1098
|
+
* @param page 页面信息
|
|
1076
1099
|
*/
|
|
1077
|
-
|
|
1078
|
-
if (
|
|
1079
|
-
|
|
1080
|
-
}
|
|
1081
|
-
const ret = [];
|
|
1082
|
-
// 层数,最多5层
|
|
1083
|
-
let deepLength = 0;
|
|
1084
|
-
// 元素
|
|
1085
|
-
let elm = '';
|
|
1086
|
-
if (typeof target.innerText === 'string') {
|
|
1087
|
-
ret.push(`(${target.innerText.substr(0, 50)})`);
|
|
1100
|
+
function setRouteStack(page) {
|
|
1101
|
+
if (typeof page === 'string') {
|
|
1102
|
+
routerStack.push(page);
|
|
1088
1103
|
}
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
ret.push(elm), (t = t.parentNode);
|
|
1104
|
+
else {
|
|
1105
|
+
routerStack = routerStack.concat(page);
|
|
1092
1106
|
}
|
|
1093
|
-
|
|
1107
|
+
// 路由栈只需保留两个来作为源页面和当前页面
|
|
1108
|
+
routerStack = routerStack.slice(-2);
|
|
1109
|
+
return {
|
|
1110
|
+
originPage: routerStack[0],
|
|
1111
|
+
page: routerStack[1] || routerStack[0],
|
|
1112
|
+
};
|
|
1113
|
+
}
|
|
1114
|
+
function handlePageLag(lagTime) {
|
|
1115
|
+
// console.log('检测到页面卡顿', lagTime);
|
|
1116
|
+
setReport({ type: 'lag', lagTime });
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
// 上报数据
|
|
1120
|
+
let Report = {
|
|
1121
|
+
uuid: '',
|
|
1122
|
+
projectID: '',
|
|
1123
|
+
host: '',
|
|
1124
|
+
originPage: '',
|
|
1125
|
+
page: '',
|
|
1126
|
+
time: 0,
|
|
1127
|
+
browserName: '',
|
|
1128
|
+
browserVersion: '',
|
|
1129
|
+
engineName: '',
|
|
1130
|
+
engineVersion: '',
|
|
1131
|
+
language: '',
|
|
1132
|
+
navigatorVendor: '',
|
|
1133
|
+
connectionType: '2g',
|
|
1134
|
+
osName: '',
|
|
1135
|
+
osVersion: '',
|
|
1136
|
+
type: 'init',
|
|
1137
|
+
viewport: '',
|
|
1138
|
+
screen: '',
|
|
1139
|
+
version: '',
|
|
1140
|
+
charset: '',
|
|
1141
|
+
pageTitle: '',
|
|
1142
|
+
referrer: '',
|
|
1143
|
+
pv: null,
|
|
1144
|
+
lag: null,
|
|
1145
|
+
ua: null,
|
|
1146
|
+
error: null,
|
|
1147
|
+
req: null,
|
|
1148
|
+
dpr: 1,
|
|
1149
|
+
perf: null,
|
|
1150
|
+
vD: undefined,
|
|
1151
|
+
manualReport: undefined,
|
|
1152
|
+
lifeCycleId: uuid.v4(),
|
|
1153
|
+
sessionId: getSessionId(),
|
|
1094
1154
|
};
|
|
1095
1155
|
/**
|
|
1096
|
-
* @description
|
|
1097
|
-
* @param e
|
|
1156
|
+
* @description 初始化上报数据并上报
|
|
1098
1157
|
*/
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
if (!target || !target.tagName) {
|
|
1103
|
-
return '';
|
|
1104
|
-
}
|
|
1105
|
-
o.push(target.tagName.toLowerCase());
|
|
1106
|
-
if (target.id) {
|
|
1107
|
-
o.push('#'.concat(target.id));
|
|
1108
|
-
}
|
|
1109
|
-
if ((t = target.className) && Object.prototype.toString.call(t) === '[object String]') {
|
|
1110
|
-
for (n = t.split(/\s+/), i = 0; i < n.length; i++) {
|
|
1111
|
-
// className包含active的不加入路径
|
|
1112
|
-
if (n[i].indexOf('active') < 0)
|
|
1113
|
-
o.push('.'.concat(n[i]));
|
|
1114
|
-
}
|
|
1158
|
+
function initReport() {
|
|
1159
|
+
if (document.readyState === 'complete') {
|
|
1160
|
+
initReportFunc();
|
|
1115
1161
|
}
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
if ((a = target.getAttribute(r))) {
|
|
1120
|
-
o.push('['.concat(r, '="').concat(a, '"]'));
|
|
1121
|
-
}
|
|
1162
|
+
else {
|
|
1163
|
+
// 注意:这里不要使用window.onload,因为一个项目window.onload只能用一次,如果这里用了就会影响宿主项目的功能
|
|
1164
|
+
window.addEventListener('load', initReportFunc);
|
|
1122
1165
|
}
|
|
1123
|
-
|
|
1124
|
-
|
|
1166
|
+
}
|
|
1167
|
+
function initReportFunc() {
|
|
1168
|
+
setRouteStack([location.href, location.href]);
|
|
1169
|
+
setPVTime();
|
|
1170
|
+
Report = Object.assign(Object.assign({}, getReport()), {
|
|
1171
|
+
page: location.href,
|
|
1172
|
+
originPage: location.href,
|
|
1173
|
+
type: 'init',
|
|
1174
|
+
perf: performance,
|
|
1175
|
+
sessionId: getSessionId(),
|
|
1176
|
+
lifeCycleId: uuid.v4(),
|
|
1177
|
+
});
|
|
1178
|
+
report(Report);
|
|
1179
|
+
}
|
|
1125
1180
|
/**
|
|
1126
|
-
* @description
|
|
1127
|
-
* @param
|
|
1181
|
+
* @description 设置Report的值
|
|
1182
|
+
* @param key Report key
|
|
1183
|
+
* @param value Report 值
|
|
1128
1184
|
*/
|
|
1129
|
-
function
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1185
|
+
function setReportValue(key, value) {
|
|
1186
|
+
if (Object.prototype.hasOwnProperty.call(Report, key)) {
|
|
1187
|
+
if (['pv', 'ua', 'error', 'request', 'lag'].includes(key)) {
|
|
1188
|
+
Report = Object.assign(Object.assign({}, Report), {
|
|
1189
|
+
pv: null,
|
|
1190
|
+
lag: null,
|
|
1191
|
+
ua: null,
|
|
1192
|
+
error: null,
|
|
1193
|
+
req: null,
|
|
1194
|
+
manualReport: undefined,
|
|
1195
|
+
perf: null,
|
|
1196
|
+
});
|
|
1135
1197
|
}
|
|
1198
|
+
Report[key] = value;
|
|
1136
1199
|
}
|
|
1137
|
-
return str.join('&');
|
|
1138
1200
|
}
|
|
1139
1201
|
/**
|
|
1140
|
-
* @description
|
|
1141
|
-
* @param event 事件
|
|
1142
|
-
* @param fn 回调方法
|
|
1202
|
+
* @description 获取上报数据
|
|
1143
1203
|
*/
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1204
|
+
function getReport() {
|
|
1205
|
+
const nav = getNavigator();
|
|
1206
|
+
const viewport = getViewport();
|
|
1207
|
+
const uuid = localStorage.getItem('username') || getUid();
|
|
1208
|
+
return Object.assign(Object.assign(Object.assign({}, Report), nav), {
|
|
1209
|
+
version,
|
|
1210
|
+
projectID: Config.projectID,
|
|
1211
|
+
host: location.host,
|
|
1212
|
+
uuid,
|
|
1213
|
+
viewport,
|
|
1214
|
+
screen: `${screen.width} x ${screen.height}`,
|
|
1215
|
+
pageTitle: document.title,
|
|
1216
|
+
referrer: document.referrer,
|
|
1217
|
+
charset: document.charset,
|
|
1218
|
+
customPayload: Config.customPayload,
|
|
1219
|
+
dpr: window.devicePixelRatio,
|
|
1220
|
+
});
|
|
1221
|
+
}
|
|
1222
|
+
function ajaxEventTrigger(event) {
|
|
1223
|
+
const ajaxEvent = new CustomEvent(event, {
|
|
1224
|
+
detail: this,
|
|
1225
|
+
});
|
|
1226
|
+
window.dispatchEvent(ajaxEvent);
|
|
1227
|
+
}
|
|
1228
|
+
const OldXHR = window.XMLHttpRequest;
|
|
1229
|
+
function newXHR() {
|
|
1230
|
+
const realXHR = new OldXHR();
|
|
1231
|
+
realXHR.addEventListener('loadstart', function () {
|
|
1232
|
+
ajaxEventTrigger.call(this, 'ajaxLoadStart');
|
|
1233
|
+
}, false);
|
|
1234
|
+
realXHR.addEventListener('loadend', function () {
|
|
1235
|
+
ajaxEventTrigger.call(this, 'ajaxLoadEnd');
|
|
1236
|
+
}, false);
|
|
1237
|
+
// 此处的捕获的异常会连日志接口也一起捕获,如果日志上报接口异常了,就会导致死循环了。
|
|
1238
|
+
realXHR.onerror = function (e) {
|
|
1239
|
+
// eslint-disable-next-line no-console
|
|
1240
|
+
console.warn('realXHR.onerror, e', e);
|
|
1241
|
+
};
|
|
1242
|
+
return realXHR;
|
|
1243
|
+
}
|
|
1152
1244
|
/**
|
|
1153
|
-
*
|
|
1154
|
-
* @param event 事件
|
|
1155
|
-
* @param fn 回调方法
|
|
1245
|
+
* 页面接口请求监控
|
|
1156
1246
|
*/
|
|
1157
|
-
const
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1247
|
+
const tempUrlInfo = {};
|
|
1248
|
+
const recordXMLHttpRequestLog = (XMLHttpRequestTimeout) => {
|
|
1249
|
+
XMLHttpRequestTimeout = typeof XMLHttpRequestTimeout === 'number' ? XMLHttpRequestTimeout : 1000;
|
|
1250
|
+
const timeRecordArray = [];
|
|
1251
|
+
//@ts-ignore
|
|
1252
|
+
window.__XMLHttpRequest__ = window.XMLHttpRequest;
|
|
1253
|
+
//@ts-ignore
|
|
1254
|
+
window.XMLHttpRequest = newXHR;
|
|
1255
|
+
window.addEventListener('ajaxLoadStart', function (e) {
|
|
1256
|
+
const tempObj = {
|
|
1257
|
+
timeStamp: new Date().getTime(),
|
|
1258
|
+
event: e,
|
|
1259
|
+
};
|
|
1260
|
+
timeRecordArray.push(tempObj);
|
|
1261
|
+
});
|
|
1262
|
+
window.addEventListener('ajaxLoadEnd', function () {
|
|
1263
|
+
const timeRecordArrayCopy = [].concat(timeRecordArray);
|
|
1264
|
+
for (let i = 0; i < timeRecordArrayCopy.length; i++) {
|
|
1265
|
+
if (timeRecordArrayCopy[i].event.detail && timeRecordArrayCopy[i].event.detail.status > 0) {
|
|
1266
|
+
const currentTime = new Date().getTime();
|
|
1267
|
+
const { responseURL, status, statusText, timeStamp } = timeRecordArrayCopy[i].event.detail;
|
|
1268
|
+
const previousTime = timeStamp || timeRecordArrayCopy[i].timeStamp;
|
|
1269
|
+
const loadTime = currentTime - previousTime;
|
|
1270
|
+
const request = {
|
|
1271
|
+
requestType: 'xhr',
|
|
1272
|
+
responseURL,
|
|
1273
|
+
status,
|
|
1274
|
+
loadTime,
|
|
1275
|
+
statusText,
|
|
1276
|
+
reason: '',
|
|
1277
|
+
detail: '',
|
|
1278
|
+
requestData: '',
|
|
1279
|
+
method: '',
|
|
1280
|
+
};
|
|
1281
|
+
if (loadTime && loadTime > XMLHttpRequestTimeout) {
|
|
1282
|
+
request.reason = 'slow';
|
|
1283
|
+
request.detail = `request is too slow, XMLHttpRequestTimeout: ${loadTime}`;
|
|
1284
|
+
}
|
|
1285
|
+
else if (status && status >= 300 && statusText && statusText.toLowerCase() !== 'ok') {
|
|
1286
|
+
request.reason = 'failed';
|
|
1287
|
+
request.detail = `request is failed, status: ${status}, statusText: ${statusText}`;
|
|
1288
|
+
}
|
|
1289
|
+
else ;
|
|
1290
|
+
if (request.reason) {
|
|
1291
|
+
if (!tempUrlInfo[responseURL]) {
|
|
1292
|
+
tempUrlInfo[responseURL] = true;
|
|
1293
|
+
setTimeout(() => {
|
|
1294
|
+
delete tempUrlInfo[responseURL];
|
|
1295
|
+
}, 10);
|
|
1296
|
+
setReportValue('error', null);
|
|
1297
|
+
report(Object.assign(Object.assign({}, getReport()), {
|
|
1298
|
+
page: location.href,
|
|
1299
|
+
originPage: location.href,
|
|
1300
|
+
type: 'request',
|
|
1301
|
+
req: request,
|
|
1302
|
+
}));
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1176
1305
|
}
|
|
1177
|
-
|
|
1178
|
-
|
|
1306
|
+
// 当前请求成功后就在数组中移除掉
|
|
1307
|
+
timeRecordArray.splice(i, 1);
|
|
1179
1308
|
}
|
|
1180
1309
|
});
|
|
1181
|
-
return result.join('/');
|
|
1182
|
-
};
|
|
1183
|
-
const formatElementXPath = (info) => {
|
|
1184
|
-
var _a;
|
|
1185
|
-
let elementXPathValue = '';
|
|
1186
|
-
const elementXPath = info.elementXPath;
|
|
1187
|
-
if (elementXPath) {
|
|
1188
|
-
const xpathElement = document.evaluate(formatTrackElementXPath(elementXPath), document).iterateNext();
|
|
1189
|
-
elementXPathValue = xpathElement ? (_a = (xpathElement.textContent || xpathElement.value)) === null || _a === void 0 ? void 0 : _a.trim() : '';
|
|
1190
|
-
}
|
|
1191
|
-
return {
|
|
1192
|
-
elementXPath,
|
|
1193
|
-
elementXPathValue,
|
|
1194
|
-
};
|
|
1195
1310
|
};
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
const
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
else {
|
|
1218
|
-
const element = document
|
|
1219
|
-
.evaluate(formatTrackElementXPath(item.trackElementXPath), document)
|
|
1220
|
-
.iterateNext();
|
|
1221
|
-
const key = url + item.trackElementXPath;
|
|
1222
|
-
if (element) {
|
|
1223
|
-
if (!existTrackListenEvent[key]) {
|
|
1224
|
-
existTrackListenEvent[key] = true;
|
|
1225
|
-
elements.push(element);
|
|
1226
|
-
//@ts-ignore
|
|
1227
|
-
element.setAttribute('visual-track-data-attr', JSON.stringify(item));
|
|
1228
|
-
element.addEventListener('click', clickEventFunc);
|
|
1229
|
-
}
|
|
1230
|
-
}
|
|
1231
|
-
else {
|
|
1232
|
-
const func = () => {
|
|
1233
|
-
var _a;
|
|
1234
|
-
const { browserName, browserVersion } = getNavigator();
|
|
1235
|
-
const payload = {
|
|
1236
|
-
projectID: Config.projectID,
|
|
1237
|
-
version,
|
|
1238
|
-
url,
|
|
1239
|
-
trackElementXPath: item.trackElementXPath,
|
|
1240
|
-
trackNameInfo: item.trackNameInfo,
|
|
1241
|
-
params: item.params,
|
|
1242
|
-
customPayload: Config.customPayload,
|
|
1243
|
-
type: 'visual_track_xpath_not_found',
|
|
1244
|
-
browser: `${browserName} - ${browserVersion}`,
|
|
1245
|
-
};
|
|
1246
|
-
if (typeof ((_a = window.navigator) === null || _a === void 0 ? void 0 : _a.sendBeacon) === 'function') {
|
|
1247
|
-
const formData = new FormData();
|
|
1248
|
-
for (const i in payload) {
|
|
1249
|
-
if (Object.prototype.hasOwnProperty.call(payload, i)) {
|
|
1250
|
-
const value = payload[i];
|
|
1251
|
-
formData.append(i, value && typeof value === 'object' ? JSON.stringify(value) : value);
|
|
1252
|
-
}
|
|
1253
|
-
}
|
|
1254
|
-
window.navigator.sendBeacon(Config.reportUrl, formData);
|
|
1255
|
-
}
|
|
1256
|
-
else {
|
|
1257
|
-
new Image().src = `${Config.reportUrl}?${serialize(payload)}`;
|
|
1258
|
-
}
|
|
1259
|
-
};
|
|
1260
|
-
if (typeof window.requestIdleCallback === 'function') {
|
|
1261
|
-
window.requestIdleCallback(() => func(), { timeout: 2000 });
|
|
1262
|
-
}
|
|
1263
|
-
else {
|
|
1264
|
-
setTimeout(() => func(), 0);
|
|
1265
|
-
}
|
|
1266
|
-
}
|
|
1267
|
-
}
|
|
1268
|
-
});
|
|
1269
|
-
}
|
|
1270
|
-
catch (err) {
|
|
1271
|
-
// eslint-disable-next-line no-console
|
|
1272
|
-
console.log('visual track evaluate err', err);
|
|
1311
|
+
const hackFetch = (XMLHttpRequestTimeout) => {
|
|
1312
|
+
XMLHttpRequestTimeout = typeof XMLHttpRequestTimeout === 'number' ? XMLHttpRequestTimeout : 1000;
|
|
1313
|
+
if (typeof window.fetch === 'function') {
|
|
1314
|
+
const __fetch__ = window.fetch;
|
|
1315
|
+
window.__fetch__ = __fetch__;
|
|
1316
|
+
//@ts-ignore
|
|
1317
|
+
window.fetch = function (t, ...args) {
|
|
1318
|
+
const begin = Date.now();
|
|
1319
|
+
//禁用数组的扩展运算符,否则portal的生产环境会报Uncaught TypeError: Object(...) is not a function,
|
|
1320
|
+
//编译后的__spreadArray函数有问题,而这个函数来自于tslib.具体报错原因不知.
|
|
1321
|
+
//其他平台使用数组的扩展运算符没有问题,比如前端监控平台的管理界面
|
|
1322
|
+
const params = [].concat(t).concat(args);
|
|
1323
|
+
return __fetch__
|
|
1324
|
+
.apply(window, params)
|
|
1325
|
+
.then(function (res) {
|
|
1326
|
+
const response = res.clone();
|
|
1327
|
+
const headers = response.headers;
|
|
1328
|
+
if (headers && typeof headers.get === 'function') {
|
|
1329
|
+
const ct = headers.get('content-type');
|
|
1330
|
+
if (ct && !/(text)|(json)/.test(ct)) {
|
|
1331
|
+
return res;
|
|
1273
1332
|
}
|
|
1274
1333
|
}
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
const
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
}
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1334
|
+
const loadTime = Date.now() - begin;
|
|
1335
|
+
response
|
|
1336
|
+
.text()
|
|
1337
|
+
.then(function (result) {
|
|
1338
|
+
const { url, status, statusText, ok } = response;
|
|
1339
|
+
const request = {
|
|
1340
|
+
requestType: 'fetch',
|
|
1341
|
+
responseURL: url,
|
|
1342
|
+
status,
|
|
1343
|
+
loadTime,
|
|
1344
|
+
statusText,
|
|
1345
|
+
reason: '',
|
|
1346
|
+
detail: '',
|
|
1347
|
+
requestData: '',
|
|
1348
|
+
method: '',
|
|
1349
|
+
};
|
|
1350
|
+
if (!ok || status >= 300) {
|
|
1351
|
+
request.reason = 'failed';
|
|
1352
|
+
request.detail = `request is failed, status: ${status}, statusText: ${statusText}`;
|
|
1353
|
+
}
|
|
1354
|
+
else if (loadTime > XMLHttpRequestTimeout) {
|
|
1355
|
+
request.reason = 'slow';
|
|
1356
|
+
request.detail = `request is too slow, XMLHttpRequestTimeout: ${loadTime}, result: ${result === null || result === void 0 ? void 0 : result.slice(0, 500)}`;
|
|
1357
|
+
}
|
|
1358
|
+
if (request.reason) {
|
|
1359
|
+
if (!tempUrlInfo[url]) {
|
|
1360
|
+
tempUrlInfo[url] = true;
|
|
1361
|
+
setTimeout(() => {
|
|
1362
|
+
delete tempUrlInfo[url];
|
|
1363
|
+
}, 10);
|
|
1364
|
+
setReportValue('error', null);
|
|
1365
|
+
report(Object.assign(Object.assign({}, getReport()), {
|
|
1366
|
+
page: location.href,
|
|
1367
|
+
originPage: location.href,
|
|
1368
|
+
type: 'request',
|
|
1369
|
+
req: request,
|
|
1370
|
+
}));
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
})
|
|
1374
|
+
.catch((err) => {
|
|
1375
|
+
// eslint-disable-next-line no-console
|
|
1376
|
+
console.log('hackFetch response.text() err', err);
|
|
1377
|
+
});
|
|
1378
|
+
return res;
|
|
1379
|
+
})
|
|
1380
|
+
.catch((err) => {
|
|
1307
1381
|
// eslint-disable-next-line no-console
|
|
1308
|
-
console.
|
|
1309
|
-
}
|
|
1310
|
-
else {
|
|
1311
|
-
setReportValue('type', 'manual');
|
|
1312
|
-
setReportValue('manualReport', data);
|
|
1313
|
-
report(getReport());
|
|
1314
|
-
setReportValue('manualReport', undefined);
|
|
1315
|
-
}
|
|
1316
|
-
};
|
|
1317
|
-
}
|
|
1318
|
-
if (typeof window.getFullScreenShootFunc === 'undefined') {
|
|
1319
|
-
//下载页面截图
|
|
1320
|
-
window.getFullScreenShootFunc = (filename) => __awaiter(void 0, void 0, void 0, function* () {
|
|
1321
|
-
const file = yield getFullScreenShoot(filename);
|
|
1322
|
-
const url = window.URL.createObjectURL(file);
|
|
1323
|
-
const tagA = document.createElement('a');
|
|
1324
|
-
tagA.setAttribute('href', url);
|
|
1325
|
-
tagA.setAttribute('download', file.name);
|
|
1326
|
-
tagA.setAttribute('target', '_blank');
|
|
1327
|
-
document.body.appendChild(tagA);
|
|
1328
|
-
tagA.click();
|
|
1329
|
-
document.body.removeChild(tagA);
|
|
1330
|
-
});
|
|
1331
|
-
}
|
|
1332
|
-
if (typeof window.getRRWebUserEventsCaptureFunc === 'undefined') {
|
|
1333
|
-
window.getRRWebUserEventsCaptureFunc = () => {
|
|
1334
|
-
setReportValue('type', 'error');
|
|
1335
|
-
setReportValue('error', {
|
|
1336
|
-
subType: 'manual',
|
|
1337
|
-
message: 'manual trigger',
|
|
1338
|
-
events: getUserEvents(true),
|
|
1382
|
+
console.log('hackFetch err', err);
|
|
1339
1383
|
});
|
|
1340
|
-
report(getReport());
|
|
1341
1384
|
};
|
|
1342
1385
|
}
|
|
1343
|
-
};
|
|
1344
|
-
let prewHref = '';
|
|
1345
|
-
const handleLocationChange = (e) => {
|
|
1346
|
-
const curHref = location.href;
|
|
1347
|
-
// href中`?`后面的值改变,不触发后续动作
|
|
1348
|
-
if (prewHref.split('?')[0] === curHref.split('?')[0]) {
|
|
1349
|
-
return;
|
|
1350
|
-
}
|
|
1351
|
-
prewHref = curHref;
|
|
1352
|
-
handleHistoryChange();
|
|
1353
|
-
elements.forEach((element) => {
|
|
1354
|
-
element === null || element === void 0 ? void 0 : element.removeEventListener('click', clickEventFunc);
|
|
1355
|
-
});
|
|
1356
|
-
//清空可视化埋点之前的数据
|
|
1357
|
-
existTrackListenEvent = {};
|
|
1358
|
-
elements.length = 0;
|
|
1359
|
-
visualTrackFunc();
|
|
1360
1386
|
};
|
|
1361
1387
|
|
|
1362
1388
|
class Track {
|
|
1363
1389
|
constructor() {
|
|
1390
|
+
this.lagTimer = null;
|
|
1391
|
+
this.observer = null;
|
|
1364
1392
|
this.visualTrack = () => {
|
|
1365
1393
|
window.onload = () => {
|
|
1366
1394
|
visualTrackFunc();
|
|
1367
1395
|
};
|
|
1368
1396
|
};
|
|
1397
|
+
this.listenPageLag = () => {
|
|
1398
|
+
this.observer = new PerformanceObserver((list) => {
|
|
1399
|
+
list.getEntries().forEach((entry) => {
|
|
1400
|
+
if (entry.duration > 400) {
|
|
1401
|
+
handlePageLag(entry.duration);
|
|
1402
|
+
}
|
|
1403
|
+
});
|
|
1404
|
+
});
|
|
1405
|
+
this.observer.observe({ entryTypes: ['longtask'] });
|
|
1406
|
+
};
|
|
1369
1407
|
}
|
|
1370
1408
|
init(config) {
|
|
1371
1409
|
var _a;
|
|
@@ -1376,7 +1414,7 @@ class Track {
|
|
|
1376
1414
|
// 没有项目ID,则不监听任何事件
|
|
1377
1415
|
if (!config.projectID) {
|
|
1378
1416
|
// eslint-disable-next-line no-console
|
|
1379
|
-
console.warn('缺少项目ID或
|
|
1417
|
+
console.warn('缺少项目ID或projectID!');
|
|
1380
1418
|
return;
|
|
1381
1419
|
}
|
|
1382
1420
|
// 没有reportUrl
|
|
@@ -1396,6 +1434,7 @@ class Track {
|
|
|
1396
1434
|
Config.enableBehavior && this.addListenUserActivity();
|
|
1397
1435
|
Config.enableError && this.addListenJSUncaught();
|
|
1398
1436
|
this.visualTrack();
|
|
1437
|
+
this.listenPageLag();
|
|
1399
1438
|
this.addListenUnload();
|
|
1400
1439
|
initWindowObjectFunction();
|
|
1401
1440
|
}
|
|
@@ -1447,6 +1486,7 @@ class Track {
|
|
|
1447
1486
|
* @description 销毁监听器
|
|
1448
1487
|
*/
|
|
1449
1488
|
destroy() {
|
|
1489
|
+
var _a;
|
|
1450
1490
|
if (Config.spa) {
|
|
1451
1491
|
// off('hashchange', pv.handleHashChange);
|
|
1452
1492
|
off('pushState', handleHistoryChange);
|
|
@@ -1461,6 +1501,8 @@ class Track {
|
|
|
1461
1501
|
if (Config.enableError) {
|
|
1462
1502
|
off('error', handleError);
|
|
1463
1503
|
}
|
|
1504
|
+
this.lagTimer && clearInterval(this.lagTimer);
|
|
1505
|
+
(_a = this.observer) === null || _a === void 0 ? void 0 : _a.disconnect();
|
|
1464
1506
|
sessionStorage.removeItem(monitorTrackSessionId);
|
|
1465
1507
|
}
|
|
1466
1508
|
}
|