监控方案

通过监听全局 window.onerror 事件捕获运行时错误,上报采集端,展示结果

采集

Script Error

Script Error,浏览器对跨域错误处于安全机制考虑的一种处理方式

  • 跨域

    页面和页面中应用的javascript文件不同源(协议、域名、端口不一致)

  • 避免Script Error

    crossorigin 属性,<script>标签属性
    crossorigin 生效需要服务端、浏览器同时支持

突破跨域报错限制

服务端:

CROS响应头 —— Access-Control-Allow-Origin CDN服务器

浏览器:

通过 Patch 原生方法捕获错误信息
1
2
3
4
5
6
7
8
9
10
11
const prevSetTimeout = window.setTimeout
window.setTimeout = (callback, timeout) => {
const self = this
return prevSetTimeout(() => {
try {
callback.call(this)
} catch (e) {
throw e
}
}, timeout)
}

框架层解决方案

AngularJS 的 ErrorHandler
Vue 的 Vue.config.errorHandler

埋点方法

手动埋点

优点:
1. 设置自定义属性、事件
2. 精细化自定义分析

缺点:
1. 工作量大,埋点位置多
2. 容错率低
3. 埋点成本高,数据收集周期长,效率低

可视化埋点

Mixpanel、TalkingData、MTA

原理:Xpath

无埋点

前端自动采集全部事件,上报服务端处理

优点:
1. 一次性加载埋点脚本
缺点:
1. 流量、采集数据量大
2. 服务器压力大

GrowingIo,神策

数据处理

日志服务进入数据处理流程前进行采样率控制

  1. 日志写入成本低
  2. rotate 机制保证存储不会浪费
  3. 了解真实打点请求数据量
  4. 避免采集端绕过采样率限制

分析

故障发生时能够分析原因
报错数高不一定是不稳定
异常波动一定有元凶

报警

规则报警,阙值配置不合理解决办法:数学模型

监控分类

数据监控

  1. PV/UV:页面点击量,访问站点或信息的不同IP地址人数
  2. 用户在页面停留时间
  3. 用户通过什么入口访问
  4. 用户在页面触发的行为

性能监控

  1. (用户、机型、系统)首屏加载时间
  2. 白屏时间
  3. http 请求响应时间
  4. 静态资源整体下载时间
  5. 页面渲染时间
  6. 页面交互时间

异常监控

  1. javascript 异常
  2. 样式丢失异常

收集用户信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
(function () {
let params = {};
// document
if (document) {
params.domain = document.domain || ''; // 域名
params.url = document.URL || ''; // 当前 URL 地址
params.title = document.title || ''; // 当前页面标题
params.referrer = document.referrer || ''; // 上一个访问页面 URL 地址
}
// window
if(window && window.screen) {
params.sh = window.screen.height || 0; // 屏幕高度
params.sw = window.screen.width || 0; // 屏幕宽度
params.cd = window.screen.colorDepth || 0; // 屏幕颜色深度
}
// navigator
if(navigator) {
params.lang = navigator.language || ''; // 语言
}
// 拼接参数
let args = '';
for(let i in params) {
if(args !== '') {
args += '&';
}
args += `${i}=${params[i]}`
}
// 通过伪装成 Image 对象,传递给后端
let img = new Image(1, 1);
let src = `http://www.funlee.cn/api/test.jpg?args=${encodeURIComponent(args)}`;
img.src = src;
})()

script 引入埋点

1
2
3
4
5
6
7
8
(function() {
let hm = document.createElement("script");
hm.type = "text/javascript";
hm.async = true;
hm.src = "http://www.funlee.cn/testAnalyze.js";
let s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(hm, s);
})();

web 阶段响应时间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
let timing = performance.timing,
start = timing.navigationStart,
dnsTime = 0,
tcpTime = 0,
firstPaintTime = 0,
domRenderTime = 0,
loadTime = 0;

dnsTime = timing.domainLookupEnd - timing.domainLookupStart;
tcpTime = timing.connectEnd - timing.connectStart;
firstPaintTime = timing.responseStart - start;
domRenderTime = timing.domContentLoadedEventEnd - start;
loadTime = timing.loadEventEnd - start;

console.log('DNS解析时间:', dnsTime,
'\nTCP建立时间:', tcpTime,
'\n首屏时间:', firstPaintTime,
'\ndom渲染完成时间:', domRenderTime,
'\n页面onload时间:', loadTime);

css 埋点

1
2
3
4
.link:active::after{
color: red;
content: url("http://192.168.1.110:3000/someapi?params=someInfo");
}

参考资料