monitor-track 1.13.0 → 1.15.0
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 +10 -0
- package/README.md +4 -0
- package/cjs/config/global.d.ts +0 -2
- package/cjs/config/request.d.ts +15 -0
- package/cjs/handlers/error.d.ts +1 -0
- package/cjs/handlers/websocket.d.ts +16 -0
- package/cjs/index.d.ts +0 -5
- package/cjs/index.js +473 -198
- package/cjs/reporter.d.ts +2 -0
- package/cjs/types/config.d.ts +9 -1
- package/cjs/types/global.d.ts +2 -1
- package/esm/config/global.d.ts +0 -2
- package/esm/config/global.js +2 -167
- package/esm/config/index.js +17 -3
- package/esm/config/request.d.ts +15 -0
- package/esm/config/request.js +158 -0
- package/esm/handlers/error.d.ts +1 -0
- package/esm/handlers/error.js +18 -16
- package/esm/handlers/websocket.d.ts +16 -0
- package/esm/handlers/websocket.js +277 -0
- package/esm/index.d.ts +0 -5
- package/esm/index.js +12 -17
- package/esm/package.json.js +1 -1
- package/esm/reporter.d.ts +2 -0
- package/esm/reporter.js +19 -11
- package/esm/types/config.d.ts +9 -1
- package/esm/types/global.d.ts +2 -1
- package/index.js +507 -193
- package/package.json +1 -1
package/cjs/index.js
CHANGED
|
@@ -40,12 +40,16 @@ const Config = {
|
|
|
40
40
|
reportUrl: '',
|
|
41
41
|
projectID: '',
|
|
42
42
|
maxLength: 1000,
|
|
43
|
-
spa:
|
|
44
|
-
hash:
|
|
43
|
+
spa: true,
|
|
44
|
+
hash: true,
|
|
45
45
|
enableBehavior: true,
|
|
46
46
|
enableError: true,
|
|
47
|
+
enableErrorScreenshot: false,
|
|
48
|
+
enableErrorEvent: false,
|
|
47
49
|
enableVisualTrack: false,
|
|
48
|
-
enableLagTrack:
|
|
50
|
+
enableLagTrack: true,
|
|
51
|
+
enableRecord: false,
|
|
52
|
+
enableOnlinePersons: true,
|
|
49
53
|
ignore: {
|
|
50
54
|
urls: [],
|
|
51
55
|
errors: [],
|
|
@@ -57,27 +61,39 @@ const Config = {
|
|
|
57
61
|
* @param config 配置项
|
|
58
62
|
*/
|
|
59
63
|
function setConfig(config) {
|
|
64
|
+
var _a;
|
|
60
65
|
Object.assign(Config, config);
|
|
66
|
+
if (!Config.ignore)
|
|
67
|
+
Config.ignore = {};
|
|
68
|
+
if (Array.isArray((_a = Config.ignore) === null || _a === void 0 ? void 0 : _a.errors)) {
|
|
69
|
+
['ResizeObserver loop limit exceeded', 'ResizeObserver loop completed with undelivered notifications.', 'Cancel'].forEach((item) => {
|
|
70
|
+
if (!Config.ignore.errors.includes(item)) {
|
|
71
|
+
Config.ignore.errors.push(item);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
}
|
|
61
75
|
}
|
|
62
76
|
|
|
63
|
-
var version = "1.
|
|
77
|
+
var version = "1.15.0";
|
|
64
78
|
|
|
65
79
|
const eventsMatrix = [[]];
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
eventsMatrix.
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
80
|
+
function enableRecordFunc() {
|
|
81
|
+
rrweb__namespace.record({
|
|
82
|
+
emit(event, isCheckout) {
|
|
83
|
+
// isCheckout 是一个标识,告诉你重新制作了快照
|
|
84
|
+
if (isCheckout) {
|
|
85
|
+
eventsMatrix.push([]);
|
|
86
|
+
}
|
|
87
|
+
if (eventsMatrix.length > 2) {
|
|
88
|
+
eventsMatrix.shift();
|
|
89
|
+
}
|
|
90
|
+
const lastEvents = eventsMatrix[eventsMatrix.length - 1];
|
|
91
|
+
lastEvents.push(event);
|
|
92
|
+
},
|
|
93
|
+
// 每30秒重新制作快照
|
|
94
|
+
checkoutEveryNms: 30 * 1000,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
81
97
|
/**
|
|
82
98
|
* @description 错误事件触发后的操作
|
|
83
99
|
*/
|
|
@@ -835,6 +851,8 @@ const reportMap = {
|
|
|
835
851
|
detail: 'de',
|
|
836
852
|
/** 上报时带上的请求参数 */
|
|
837
853
|
requestData: 'rD',
|
|
854
|
+
/** 上报时带上的响应结果 */
|
|
855
|
+
responseBody: 'rB',
|
|
838
856
|
/** 上报时带上的请求方法 */
|
|
839
857
|
method: 'rM',
|
|
840
858
|
},
|
|
@@ -903,7 +921,7 @@ function reportFunc(data) {
|
|
|
903
921
|
delete ERROR_MESSAGE_MAP[message];
|
|
904
922
|
}, 3000);
|
|
905
923
|
const { p, pid, host, bN, pt } = payload;
|
|
906
|
-
if ((_c = data.error) === null || _c === void 0 ? void 0 : _c.events) {
|
|
924
|
+
if (((_c = data.error) === null || _c === void 0 ? void 0 : _c.events) && Config.enableErrorEvent) {
|
|
907
925
|
hasErrorEvent = true;
|
|
908
926
|
const errorEvent = data.error.events;
|
|
909
927
|
const delayTime = Math.ceil(Math.random() * 10000);
|
|
@@ -914,17 +932,19 @@ function reportFunc(data) {
|
|
|
914
932
|
});
|
|
915
933
|
}, delayTime < 3500 ? 3500 : delayTime);
|
|
916
934
|
}
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
935
|
+
if (Config.enableErrorScreenshot) {
|
|
936
|
+
picturePromise = getFullScreenShoot(filename)
|
|
937
|
+
.then((file) => {
|
|
938
|
+
return new Promise((res) => {
|
|
939
|
+
xhrFunc(file.name, data.uuid, filename, file, res, (result) => {
|
|
940
|
+
payload.error.pic = result;
|
|
941
|
+
}, { p, pid, host, bN, pt });
|
|
942
|
+
});
|
|
943
|
+
})
|
|
944
|
+
.catch((err) => {
|
|
945
|
+
payload.error.picError = (err === null || err === void 0 ? void 0 : err.stack) || (err === null || err === void 0 ? void 0 : err.toString());
|
|
923
946
|
});
|
|
924
|
-
}
|
|
925
|
-
.catch((err) => {
|
|
926
|
-
payload.error.picError = (err === null || err === void 0 ? void 0 : err.stack) || (err === null || err === void 0 ? void 0 : err.toString());
|
|
927
|
-
});
|
|
947
|
+
}
|
|
928
948
|
}
|
|
929
949
|
}
|
|
930
950
|
return picturePromise
|
|
@@ -961,6 +981,10 @@ function reportFunc(data) {
|
|
|
961
981
|
// 上报
|
|
962
982
|
function report(data) {
|
|
963
983
|
return new Promise((res) => {
|
|
984
|
+
var _a;
|
|
985
|
+
if ((((_a = Config.ignore) === null || _a === void 0 ? void 0 : _a.urls) || []).some((url) => location.href.includes(url))) {
|
|
986
|
+
return;
|
|
987
|
+
}
|
|
964
988
|
//在帧的空闲时间上报
|
|
965
989
|
const dataCopy = JSON.parse(JSON.stringify(data));
|
|
966
990
|
if (typeof window.requestIdleCallback === 'function') {
|
|
@@ -1219,171 +1243,429 @@ function getReport() {
|
|
|
1219
1243
|
customPayload: Config.customPayload,
|
|
1220
1244
|
dpr: window.devicePixelRatio,
|
|
1221
1245
|
});
|
|
1222
|
-
}
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
let reconnectAndSendTimeout, websocketHeartBeatInterval, ws, userId = '';
|
|
1249
|
+
let remoteDebugEnable = false;
|
|
1250
|
+
let targetPlatform = '';
|
|
1251
|
+
let targetUserId = '';
|
|
1252
|
+
const originConsole = Object.assign({}, console);
|
|
1253
|
+
const formatConsole = () => {
|
|
1254
|
+
if (remoteDebugEnable)
|
|
1255
|
+
return;
|
|
1256
|
+
remoteDebugEnable = true;
|
|
1257
|
+
window.console = Object.assign({}, originConsole);
|
|
1258
|
+
const arr = ['debug', 'log', 'info', 'warn', 'error'];
|
|
1259
|
+
arr.forEach((item) => {
|
|
1260
|
+
//@ts-ignore
|
|
1261
|
+
window.console[item] = function (...args) {
|
|
1262
|
+
//@ts-ignore
|
|
1263
|
+
originConsole[item](...args);
|
|
1264
|
+
const data = {
|
|
1265
|
+
origin: 'console',
|
|
1266
|
+
level: item,
|
|
1267
|
+
userId,
|
|
1268
|
+
time: new Date().toLocaleString(),
|
|
1269
|
+
page: location.href,
|
|
1270
|
+
content: '',
|
|
1271
|
+
targetPlatform,
|
|
1272
|
+
targetUserId,
|
|
1273
|
+
};
|
|
1274
|
+
try {
|
|
1275
|
+
const content = JSON.stringify(args);
|
|
1276
|
+
data.content = content;
|
|
1277
|
+
}
|
|
1278
|
+
catch (err) {
|
|
1279
|
+
data.content = 'console内容解析错误: ' + err.stack || err.message || err.toString();
|
|
1280
|
+
}
|
|
1281
|
+
const msg = {
|
|
1282
|
+
type: WEBSOCKET_TYPE.DEBUG_INFO_UPLOAD,
|
|
1283
|
+
userId,
|
|
1284
|
+
platform: 'monitor-track-sdk',
|
|
1285
|
+
data,
|
|
1286
|
+
};
|
|
1287
|
+
wsSendFunc(msg);
|
|
1288
|
+
};
|
|
1226
1289
|
});
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1290
|
+
};
|
|
1291
|
+
const resetConsole = () => {
|
|
1292
|
+
window.console = originConsole;
|
|
1293
|
+
remoteDebugEnable = false;
|
|
1294
|
+
};
|
|
1295
|
+
const uploadRequest = (requestInfo) => {
|
|
1296
|
+
// console.log('monitor-track-sdk: uploadRequest remoteDebugEnable', remoteDebugEnable);
|
|
1297
|
+
if (remoteDebugEnable) {
|
|
1298
|
+
const data = {
|
|
1299
|
+
origin: 'request',
|
|
1300
|
+
userId,
|
|
1301
|
+
time: new Date().toLocaleString(),
|
|
1302
|
+
page: location.href,
|
|
1303
|
+
content: requestInfo,
|
|
1304
|
+
targetPlatform,
|
|
1305
|
+
targetUserId,
|
|
1306
|
+
};
|
|
1307
|
+
const msg = {
|
|
1308
|
+
type: WEBSOCKET_TYPE.DEBUG_INFO_UPLOAD,
|
|
1309
|
+
userId,
|
|
1310
|
+
platform: 'monitor-track-sdk',
|
|
1311
|
+
data,
|
|
1312
|
+
};
|
|
1313
|
+
// console.log('monitor-track-sdk: request-upload sent');
|
|
1314
|
+
wsSendFunc(msg);
|
|
1315
|
+
}
|
|
1316
|
+
};
|
|
1317
|
+
function enableOnlinePersonsFunc() {
|
|
1318
|
+
initWebsocket();
|
|
1244
1319
|
}
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1320
|
+
const WEBSOCKET_TYPE = {
|
|
1321
|
+
TRY_CONNECT: 'try-connect',
|
|
1322
|
+
CHECK_CONNECT: 'check-connect',
|
|
1323
|
+
PING: 'ping',
|
|
1324
|
+
PONG: 'pong',
|
|
1325
|
+
REPLY_SERVER_HEART_BEAT: 'reply-server-heart-beat',
|
|
1326
|
+
SOCKET_HEART_BEAT: 'socket-heart-beat',
|
|
1327
|
+
RESPONSE_DATE: 'response-date',
|
|
1328
|
+
REMOTE_DEBUG_ENABLE: 'remote-debug-enable',
|
|
1329
|
+
DEBUG_INFO_UPLOAD: 'debug-info-upload',
|
|
1330
|
+
REPlY_MESSAGE: 'reply-message',
|
|
1331
|
+
};
|
|
1332
|
+
let mounted = false;
|
|
1333
|
+
const initWebsocket = () => {
|
|
1334
|
+
if (window.WebSocket) {
|
|
1335
|
+
userId = localStorage.getItem('username') || getUid();
|
|
1336
|
+
closeWebsocket('');
|
|
1337
|
+
websocketHeartBeatInterval = setInterval(reconnectAndSend, 30000);
|
|
1338
|
+
const isLocal = Config.reportUrl.includes('http://');
|
|
1339
|
+
ws = new WebSocket(`ws${isLocal ? '' : 's'}://${Config.reportUrl.replace(isLocal ? 'http://' : 'https://', '').replace('/s/r', '')}?type=monitor-dsk`);
|
|
1340
|
+
ws.onopen = () => openWS();
|
|
1341
|
+
ws.onmessage = (data) => incomingMessage(data);
|
|
1342
|
+
ws.onerror = () => {
|
|
1343
|
+
ws.close(1000);
|
|
1344
|
+
if (websocketHeartBeatInterval)
|
|
1345
|
+
clearInterval(websocketHeartBeatInterval);
|
|
1346
|
+
websocketHeartBeatInterval = setInterval(reconnectAndSend, 10000);
|
|
1260
1347
|
};
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1348
|
+
ws.onclose = () => {
|
|
1349
|
+
ws.close(1000);
|
|
1350
|
+
if (websocketHeartBeatInterval)
|
|
1351
|
+
clearInterval(websocketHeartBeatInterval);
|
|
1352
|
+
websocketHeartBeatInterval = setInterval(reconnectAndSend, 10000);
|
|
1353
|
+
};
|
|
1354
|
+
if (!mounted) {
|
|
1355
|
+
reconnectNetwork();
|
|
1356
|
+
mounted = true;
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
};
|
|
1360
|
+
const openWS = () => {
|
|
1361
|
+
const data = getReport();
|
|
1362
|
+
const { page, projectID, host, pageTitle } = data;
|
|
1363
|
+
const msg = {
|
|
1364
|
+
type: WEBSOCKET_TYPE.TRY_CONNECT,
|
|
1365
|
+
userId,
|
|
1366
|
+
platform: 'monitor-track-sdk',
|
|
1367
|
+
data: {
|
|
1368
|
+
p: page,
|
|
1369
|
+
pid: projectID,
|
|
1370
|
+
host,
|
|
1371
|
+
pt: pageTitle,
|
|
1372
|
+
platform: 'web',
|
|
1373
|
+
},
|
|
1374
|
+
};
|
|
1375
|
+
wsSendFunc(msg);
|
|
1376
|
+
};
|
|
1377
|
+
const reconnectAndSend = () => {
|
|
1378
|
+
try {
|
|
1379
|
+
if (ws.readyState !== 1) {
|
|
1380
|
+
reconnectSocket();
|
|
1381
|
+
}
|
|
1382
|
+
else {
|
|
1383
|
+
const msg = {
|
|
1384
|
+
type: WEBSOCKET_TYPE.CHECK_CONNECT,
|
|
1385
|
+
userId,
|
|
1386
|
+
data: WEBSOCKET_TYPE.PING,
|
|
1387
|
+
date: Date.now(),
|
|
1388
|
+
};
|
|
1389
|
+
wsSendFunc(msg, 'reconnectAndSend');
|
|
1390
|
+
// 20秒超时,如果收不到服务端pong响应则表示服务端已主动断开连接,此时需客户端重新开启websocket连接
|
|
1391
|
+
reconnectAndSendTimeout = setTimeout(() => {
|
|
1392
|
+
reconnectSocket();
|
|
1393
|
+
}, 20000);
|
|
1394
|
+
}
|
|
1395
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1396
|
+
}
|
|
1397
|
+
catch (_err) {
|
|
1398
|
+
// console.error('monitor-track-sdk: reconnectAndSend err', err);
|
|
1399
|
+
}
|
|
1400
|
+
};
|
|
1401
|
+
const closeWebsocket = (info) => {
|
|
1402
|
+
if (ws) {
|
|
1403
|
+
ws.onopen = null;
|
|
1404
|
+
ws.onmessage = null;
|
|
1405
|
+
ws.onerror = null;
|
|
1406
|
+
ws.onclose = null;
|
|
1407
|
+
if (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING) {
|
|
1408
|
+
ws.close(1000, info);
|
|
1409
|
+
}
|
|
1410
|
+
//@ts-ignore
|
|
1411
|
+
ws = null; // 释放引用
|
|
1412
|
+
}
|
|
1413
|
+
if (websocketHeartBeatInterval)
|
|
1414
|
+
clearInterval(websocketHeartBeatInterval);
|
|
1415
|
+
};
|
|
1416
|
+
const reconnectSocket = () => {
|
|
1417
|
+
// console.warn('monitor-track-sdk: 正在重新建立websocket连接...');
|
|
1418
|
+
initWebsocket();
|
|
1419
|
+
};
|
|
1420
|
+
const incomingMessage = (e) => __awaiter(void 0, void 0, void 0, function* () {
|
|
1421
|
+
let data = {};
|
|
1422
|
+
try {
|
|
1423
|
+
data = JSON.parse(e.data);
|
|
1424
|
+
// console.info('incomingMessage data', data);
|
|
1425
|
+
switch (data.type) {
|
|
1426
|
+
case WEBSOCKET_TYPE.PONG:
|
|
1427
|
+
// console.info('monitor-track-sdk: incomingMessage 响应客户端的心跳: ping => pong');
|
|
1428
|
+
clearTimeout(reconnectAndSendTimeout);
|
|
1429
|
+
break;
|
|
1430
|
+
case WEBSOCKET_TYPE.SOCKET_HEART_BEAT: {
|
|
1431
|
+
// console.info('monitor-track-sdk: incomingMessage 响应服务端的心跳: heart beat => ', new Date().toLocaleString());
|
|
1432
|
+
const msg = {
|
|
1433
|
+
type: WEBSOCKET_TYPE.CHECK_CONNECT,
|
|
1434
|
+
userId,
|
|
1435
|
+
data: WEBSOCKET_TYPE.REPLY_SERVER_HEART_BEAT,
|
|
1436
|
+
date: Date.now(),
|
|
1281
1437
|
};
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1438
|
+
wsSendFunc(msg, 'socket-heart-beat reply-server-heart-beat');
|
|
1439
|
+
break;
|
|
1440
|
+
}
|
|
1441
|
+
case WEBSOCKET_TYPE.RESPONSE_DATE:
|
|
1442
|
+
// console.info(`monitor-track-sdk: incomingMessage WS connected, Roundtrip time: ${Date.now() - data.data} ms`);
|
|
1443
|
+
break;
|
|
1444
|
+
case WEBSOCKET_TYPE.REMOTE_DEBUG_ENABLE: {
|
|
1445
|
+
// console.log('monitor-track-sdk REMOTE_DEBUG_ENABLE data.data', data.data);
|
|
1446
|
+
const { enable, platform, userId } = data.data;
|
|
1447
|
+
if (enable && platform && userId) {
|
|
1448
|
+
targetPlatform = platform;
|
|
1449
|
+
targetUserId = userId;
|
|
1450
|
+
formatConsole();
|
|
1289
1451
|
}
|
|
1290
|
-
else
|
|
1291
|
-
|
|
1292
|
-
if (!tempUrlInfo[responseURL]) {
|
|
1293
|
-
tempUrlInfo[responseURL] = true;
|
|
1294
|
-
setTimeout(() => {
|
|
1295
|
-
delete tempUrlInfo[responseURL];
|
|
1296
|
-
}, 10);
|
|
1297
|
-
setReportValue('error', null);
|
|
1298
|
-
report(Object.assign(Object.assign({}, getReport()), {
|
|
1299
|
-
page: location.href,
|
|
1300
|
-
originPage: location.href,
|
|
1301
|
-
type: 'request',
|
|
1302
|
-
req: request,
|
|
1303
|
-
}));
|
|
1304
|
-
}
|
|
1452
|
+
else {
|
|
1453
|
+
resetConsole();
|
|
1305
1454
|
}
|
|
1455
|
+
break;
|
|
1456
|
+
}
|
|
1457
|
+
case WEBSOCKET_TYPE.REPlY_MESSAGE: {
|
|
1458
|
+
// eslint-disable-next-line no-console
|
|
1459
|
+
console.info('incomingMessage REPlY_MESSAGE, data: ', data.data);
|
|
1460
|
+
break;
|
|
1306
1461
|
}
|
|
1307
|
-
|
|
1308
|
-
|
|
1462
|
+
default:
|
|
1463
|
+
// console.error('incomingMessage default error', data);
|
|
1464
|
+
break;
|
|
1309
1465
|
}
|
|
1310
|
-
}
|
|
1466
|
+
}
|
|
1467
|
+
catch (err) {
|
|
1468
|
+
// eslint-disable-next-line no-console
|
|
1469
|
+
console.error('monitor-track-sdk incomingMessage JSON.parse', err);
|
|
1470
|
+
}
|
|
1471
|
+
});
|
|
1472
|
+
const wsSendFunc = (msg, _info) => {
|
|
1473
|
+
if (ws.readyState === 1) {
|
|
1474
|
+
msg.platform = 'monitor-track-sdk';
|
|
1475
|
+
const message = JSON.stringify(msg);
|
|
1476
|
+
ws.send(message);
|
|
1477
|
+
}
|
|
1478
|
+
else {
|
|
1479
|
+
// console.warn('monitor-track-sdk: window.ws.readyState === 1 info', info);
|
|
1480
|
+
reconnectSocket();
|
|
1481
|
+
}
|
|
1311
1482
|
};
|
|
1312
|
-
const
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1483
|
+
const reconnectNetwork = () => {
|
|
1484
|
+
let networkFlag = false;
|
|
1485
|
+
let debounceTimeout = null;
|
|
1486
|
+
document.addEventListener('offline', function () {
|
|
1487
|
+
// 断开网络
|
|
1488
|
+
try {
|
|
1489
|
+
// console.info('monitor-track-sdk: 客户端网络已断开,正在关闭websocket');
|
|
1490
|
+
ws.close(1000);
|
|
1491
|
+
networkFlag = true;
|
|
1492
|
+
debounceTimeout = setTimeout(() => {
|
|
1493
|
+
// console.warn('monitor-track-sdk: reconnectNetwork offline 无法访问网络');
|
|
1494
|
+
// message.error('无法访问网络');
|
|
1495
|
+
}, 5000);
|
|
1496
|
+
}
|
|
1497
|
+
catch (err) {
|
|
1498
|
+
// console.error('monitor-track-sdk: addEventListener offline after setTimeout', err);
|
|
1499
|
+
}
|
|
1500
|
+
}, false);
|
|
1501
|
+
document.addEventListener('online', function () {
|
|
1502
|
+
// 连接到网络
|
|
1503
|
+
try {
|
|
1504
|
+
if (networkFlag === true) {
|
|
1505
|
+
clearTimeout(debounceTimeout);
|
|
1506
|
+
// console.info('monitor-track-sdk: addEventListener reconnectNetwork online');
|
|
1507
|
+
// message.success('网络已恢复');
|
|
1508
|
+
networkFlag = false;
|
|
1509
|
+
// console.warn('monitor-track-sdk: addEventListener online websocket 正在重新建立连接...');
|
|
1510
|
+
initWebsocket();
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
catch (err) {
|
|
1514
|
+
// console.error('monitor-track-sdk: document.addEventListener online', err);
|
|
1515
|
+
}
|
|
1516
|
+
}, false);
|
|
1517
|
+
};
|
|
1518
|
+
|
|
1519
|
+
let xhrTimeout = 2500;
|
|
1520
|
+
let fetchTimeout = 2500;
|
|
1521
|
+
const maxContentLength = 1000;
|
|
1522
|
+
// 保存原始 XMLHttpRequest
|
|
1523
|
+
const OriginalXHR = window.XMLHttpRequest;
|
|
1524
|
+
// 创建增强版的 XMLHttpRequest 类
|
|
1525
|
+
class InterceptedXHR extends OriginalXHR {
|
|
1526
|
+
constructor() {
|
|
1527
|
+
super();
|
|
1528
|
+
this.requestInfo = {
|
|
1529
|
+
requestType: 'xhr',
|
|
1530
|
+
method: '',
|
|
1531
|
+
responseURL: '',
|
|
1532
|
+
requestData: null,
|
|
1533
|
+
responseBody: null,
|
|
1534
|
+
status: 0,
|
|
1535
|
+
startTime: Date.now(),
|
|
1536
|
+
loadTime: 0,
|
|
1537
|
+
statusText: '',
|
|
1538
|
+
reason: '',
|
|
1539
|
+
detail: '',
|
|
1540
|
+
};
|
|
1541
|
+
this.setupInterceptors();
|
|
1542
|
+
}
|
|
1543
|
+
open(method, url, async = true, username, password) {
|
|
1544
|
+
super.open(method, url, async, username, password);
|
|
1545
|
+
this.requestInfo.method = method;
|
|
1546
|
+
this.requestInfo.responseURL = url;
|
|
1547
|
+
}
|
|
1548
|
+
// eslint-disable-next-line no-undef
|
|
1549
|
+
send(body) {
|
|
1550
|
+
super.send(body);
|
|
1551
|
+
try {
|
|
1552
|
+
this.requestInfo.requestData =
|
|
1553
|
+
typeof body === 'object' && body
|
|
1554
|
+
? JSON.stringify(body).slice(0, maxContentLength)
|
|
1555
|
+
: typeof body === 'string'
|
|
1556
|
+
? body.slice(0, maxContentLength)
|
|
1557
|
+
: body || '';
|
|
1558
|
+
}
|
|
1559
|
+
catch (err) {
|
|
1560
|
+
// eslint-disable-next-line no-console
|
|
1561
|
+
console.error('xhr send err: ', err, 'body', body);
|
|
1562
|
+
}
|
|
1563
|
+
this.requestInfo.startTime = Date.now();
|
|
1564
|
+
}
|
|
1565
|
+
setupInterceptors() {
|
|
1566
|
+
this.addEventListener('load', () => {
|
|
1567
|
+
try {
|
|
1568
|
+
this.requestInfo.status = this.status;
|
|
1569
|
+
this.requestInfo.responseBody = this.responseText.slice(0, maxContentLength);
|
|
1570
|
+
this.requestInfo.loadTime = Date.now() - this.requestInfo.startTime;
|
|
1571
|
+
this.requestInfo.statusText = this.statusText;
|
|
1572
|
+
if (this.requestInfo.loadTime > xhrTimeout) {
|
|
1573
|
+
this.requestInfo.reason = 'slow';
|
|
1574
|
+
this.requestInfo.detail = `request is too slow, XMLHttpRequestTimeout: ${this.requestInfo.loadTime}`;
|
|
1334
1575
|
}
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
.
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
if (!ok || status >= 300) {
|
|
1352
|
-
request.reason = 'failed';
|
|
1353
|
-
request.detail = `request is failed, status: ${status}, statusText: ${statusText}`;
|
|
1354
|
-
}
|
|
1355
|
-
else if (loadTime > XMLHttpRequestTimeout) {
|
|
1356
|
-
request.reason = 'slow';
|
|
1357
|
-
request.detail = `request is too slow, XMLHttpRequestTimeout: ${loadTime}, result: ${result === null || result === void 0 ? void 0 : result.slice(0, 500)}`;
|
|
1358
|
-
}
|
|
1359
|
-
if (request.reason) {
|
|
1360
|
-
if (!tempUrlInfo[url]) {
|
|
1361
|
-
tempUrlInfo[url] = true;
|
|
1362
|
-
setTimeout(() => {
|
|
1363
|
-
delete tempUrlInfo[url];
|
|
1364
|
-
}, 10);
|
|
1365
|
-
setReportValue('error', null);
|
|
1366
|
-
report(Object.assign(Object.assign({}, getReport()), {
|
|
1367
|
-
page: location.href,
|
|
1368
|
-
originPage: location.href,
|
|
1369
|
-
type: 'request',
|
|
1370
|
-
req: request,
|
|
1371
|
-
}));
|
|
1372
|
-
}
|
|
1373
|
-
}
|
|
1374
|
-
})
|
|
1375
|
-
.catch((err) => {
|
|
1376
|
-
// eslint-disable-next-line no-console
|
|
1377
|
-
console.log('hackFetch response.text() err', err);
|
|
1378
|
-
});
|
|
1379
|
-
return res;
|
|
1380
|
-
})
|
|
1381
|
-
.catch((err) => {
|
|
1576
|
+
if (this.status >= 400) {
|
|
1577
|
+
this.requestInfo.reason = 'failed';
|
|
1578
|
+
this.requestInfo.detail = `request is failed, status: ${this.status}, statusText: ${this.statusText}`;
|
|
1579
|
+
}
|
|
1580
|
+
if (this.requestInfo.reason) {
|
|
1581
|
+
setReportValue('error', null);
|
|
1582
|
+
report(Object.assign(Object.assign({}, getReport()), {
|
|
1583
|
+
page: location.href,
|
|
1584
|
+
originPage: location.href,
|
|
1585
|
+
type: 'request',
|
|
1586
|
+
req: this.requestInfo,
|
|
1587
|
+
}));
|
|
1588
|
+
}
|
|
1589
|
+
uploadRequest(this.requestInfo);
|
|
1590
|
+
}
|
|
1591
|
+
catch (err) {
|
|
1382
1592
|
// eslint-disable-next-line no-console
|
|
1383
|
-
console.
|
|
1384
|
-
}
|
|
1385
|
-
};
|
|
1593
|
+
console.error('xhr load err: ', err, 'this.requestInfo', this.requestInfo);
|
|
1594
|
+
}
|
|
1595
|
+
});
|
|
1596
|
+
this.addEventListener('error', () => {
|
|
1597
|
+
// console.error('❌ 请求失败:', this.requestInfo);
|
|
1598
|
+
});
|
|
1386
1599
|
}
|
|
1600
|
+
}
|
|
1601
|
+
const recordXMLHttpRequest = (XMLHttpRequestTimeout) => {
|
|
1602
|
+
xhrTimeout = XMLHttpRequestTimeout;
|
|
1603
|
+
// 重写全局 XMLHttpRequest
|
|
1604
|
+
window.XMLHttpRequest = InterceptedXHR;
|
|
1605
|
+
};
|
|
1606
|
+
const originalFetch = window.fetch;
|
|
1607
|
+
// eslint-disable-next-line no-undef
|
|
1608
|
+
const fetchFUnc = function (input, init) {
|
|
1609
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1610
|
+
const url = typeof input === 'string' ? input : input.toString();
|
|
1611
|
+
const options = init || {};
|
|
1612
|
+
const requestInfo = {
|
|
1613
|
+
requestType: 'fetch',
|
|
1614
|
+
method: options.method || 'GET',
|
|
1615
|
+
responseURL: url,
|
|
1616
|
+
requestData: typeof options.body === 'object' && options.body ? JSON.stringify(options.body).slice(0, maxContentLength) : options.body || '',
|
|
1617
|
+
responseBody: '',
|
|
1618
|
+
status: 0,
|
|
1619
|
+
startTime: Date.now(),
|
|
1620
|
+
loadTime: 0,
|
|
1621
|
+
statusText: '',
|
|
1622
|
+
reason: '',
|
|
1623
|
+
detail: '',
|
|
1624
|
+
};
|
|
1625
|
+
const response = yield originalFetch(input, init);
|
|
1626
|
+
try {
|
|
1627
|
+
const clonedResponse = response.clone();
|
|
1628
|
+
requestInfo.status = response.status;
|
|
1629
|
+
requestInfo.loadTime = Date.now() - requestInfo.startTime;
|
|
1630
|
+
requestInfo.statusText = response.statusText;
|
|
1631
|
+
if (!response.ok) {
|
|
1632
|
+
requestInfo.reason = 'failed';
|
|
1633
|
+
requestInfo.detail = `request is failed, status: ${response.status}, statusText: ${response.statusText}`;
|
|
1634
|
+
}
|
|
1635
|
+
else {
|
|
1636
|
+
if (requestInfo.loadTime > fetchTimeout) {
|
|
1637
|
+
requestInfo.reason = 'slow';
|
|
1638
|
+
requestInfo.detail = `request is too slow, XMLHttpRequestTimeout: ${requestInfo.loadTime}`;
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1641
|
+
try {
|
|
1642
|
+
const responseBody = yield clonedResponse.text();
|
|
1643
|
+
requestInfo.responseBody = responseBody.slice(0, maxContentLength);
|
|
1644
|
+
}
|
|
1645
|
+
catch (e) {
|
|
1646
|
+
requestInfo.responseBody = (e === null || e === void 0 ? void 0 : e.message) || (e === null || e === void 0 ? void 0 : e.stack) || (e === null || e === void 0 ? void 0 : e.toString());
|
|
1647
|
+
}
|
|
1648
|
+
if (requestInfo.reason) {
|
|
1649
|
+
setReportValue('error', null);
|
|
1650
|
+
report(Object.assign(Object.assign({}, getReport()), {
|
|
1651
|
+
page: location.href,
|
|
1652
|
+
originPage: location.href,
|
|
1653
|
+
type: 'request',
|
|
1654
|
+
req: requestInfo,
|
|
1655
|
+
}));
|
|
1656
|
+
}
|
|
1657
|
+
uploadRequest(requestInfo);
|
|
1658
|
+
}
|
|
1659
|
+
catch (err) {
|
|
1660
|
+
// eslint-disable-next-line no-console
|
|
1661
|
+
console.error('❌ 获取响应数据失败, requestInfo: ', requestInfo, 'err: ', err);
|
|
1662
|
+
}
|
|
1663
|
+
return response;
|
|
1664
|
+
});
|
|
1665
|
+
};
|
|
1666
|
+
const recordFetch = (XMLHttpRequestTimeout) => {
|
|
1667
|
+
fetchTimeout = XMLHttpRequestTimeout;
|
|
1668
|
+
window.fetch = fetchFUnc;
|
|
1387
1669
|
};
|
|
1388
1670
|
|
|
1389
1671
|
class Track {
|
|
@@ -1398,8 +1680,10 @@ class Track {
|
|
|
1398
1680
|
this.listenPageLag = () => {
|
|
1399
1681
|
this.observer = new PerformanceObserver((list) => {
|
|
1400
1682
|
list.getEntries().forEach((entry) => {
|
|
1401
|
-
if (entry.duration > (Config.lagTimeout ||
|
|
1683
|
+
if (entry.duration > (Config.lagTimeout || 800)) {
|
|
1402
1684
|
handlePageLag(entry.duration);
|
|
1685
|
+
// eslint-disable-next-line no-console
|
|
1686
|
+
console.log('监测到页面卡顿:', entry.duration, entry, location.href);
|
|
1403
1687
|
}
|
|
1404
1688
|
});
|
|
1405
1689
|
});
|
|
@@ -1407,7 +1691,6 @@ class Track {
|
|
|
1407
1691
|
};
|
|
1408
1692
|
}
|
|
1409
1693
|
init(config) {
|
|
1410
|
-
var _a;
|
|
1411
1694
|
// 是否开启日志收集
|
|
1412
1695
|
if (!config || !config.enable) {
|
|
1413
1696
|
return;
|
|
@@ -1424,18 +1707,17 @@ class Track {
|
|
|
1424
1707
|
console.warn('缺少上报地址!');
|
|
1425
1708
|
return;
|
|
1426
1709
|
}
|
|
1427
|
-
if (this.ignoreUrl(((_a = config === null || config === void 0 ? void 0 : config.ignore) === null || _a === void 0 ? void 0 : _a.urls) || [])) {
|
|
1428
|
-
return;
|
|
1429
|
-
}
|
|
1430
1710
|
setConfig(config);
|
|
1431
1711
|
initReport();
|
|
1432
|
-
|
|
1433
|
-
|
|
1712
|
+
recordXMLHttpRequest(config.XMLHttpRequestTimeout || 2500);
|
|
1713
|
+
recordFetch(config.XMLHttpRequestTimeout || 2500);
|
|
1434
1714
|
Config.spa && this.addListenRouterChange();
|
|
1435
1715
|
Config.enableBehavior && this.addListenUserActivity();
|
|
1436
1716
|
Config.enableError && this.addListenJSUncaught();
|
|
1437
1717
|
Config.enableVisualTrack && this.visualTrack();
|
|
1438
1718
|
Config.enableLagTrack && this.listenPageLag();
|
|
1719
|
+
Config.enableRecord && enableRecordFunc();
|
|
1720
|
+
Config.enableOnlinePersons && enableOnlinePersonsFunc();
|
|
1439
1721
|
this.addListenUnload();
|
|
1440
1722
|
initWindowObjectFunction();
|
|
1441
1723
|
}
|
|
@@ -1475,14 +1757,6 @@ class Track {
|
|
|
1475
1757
|
this.destroy();
|
|
1476
1758
|
});
|
|
1477
1759
|
}
|
|
1478
|
-
/**
|
|
1479
|
-
* 忽略的url
|
|
1480
|
-
* @param urls
|
|
1481
|
-
*/
|
|
1482
|
-
ignoreUrl(urls) {
|
|
1483
|
-
const someUrl = urls.some((url) => location.href.includes(url));
|
|
1484
|
-
return someUrl;
|
|
1485
|
-
}
|
|
1486
1760
|
/**
|
|
1487
1761
|
* @description 销毁监听器
|
|
1488
1762
|
*/
|
|
@@ -1505,6 +1779,7 @@ class Track {
|
|
|
1505
1779
|
this.lagTimer && clearInterval(this.lagTimer);
|
|
1506
1780
|
(_a = this.observer) === null || _a === void 0 ? void 0 : _a.disconnect();
|
|
1507
1781
|
sessionStorage.removeItem(monitorTrackSessionId);
|
|
1782
|
+
closeWebsocket('destroy');
|
|
1508
1783
|
}
|
|
1509
1784
|
}
|
|
1510
1785
|
|