基本上服务器端的代码都是处于 7x24 小时的实时监控状态的,一旦有任何异常对应的开发同学就马上收到报警,并且第一时间处理。 但是对于前端来说,往往是实际用户那里的脚本报错后才知道页面出现异常,这时候已经是故障了。
为了让前端也能和后端一样,需要将线上的 JavaScript 代码监控起来,当用户端浏览器出现异前端第一时间被通知到。于是便有了淘宝前端的监控平台:JSTracker。
在解决怎么采集之前,还需要解决采集哪些数据,哪些数据是有用的?
主要原则就是避开用户敏感字段,采集浏览器版本、操作系统版本、报错的 msg 信息等。
主动捕获异常方案主要是 onError 和 addEventListener,
onError 在 IE6 开始就支持了,所以 JSTracker 的主动采集是使用的 onError。
onError 可以采集到 file、line、col 等信息,但是实际情况中却大部分收集到的是 script error。
原因是浏览器的同源性策略(CORS),在高级浏览器中如果浏览器捕获到了错误信息,如果 JS 文件所在的域名(如:g.alicdn.com)和当前的页面地址(如:www.taobao.com)是跨域的,那么浏览器会把 onError 中的 msg 替换为 script error,其余字段也会做替换。
webkit 的源代码:
Script error 这个问题也是目前大家最诟病的一个问题:当报警发生之后,我们只知道有问题,但是很难知道哪里出了问题。
其实是有解决方案的:
<script src="http://somremotesite.example/script.js" crossorigin></script>
这个解决方式看起来完美,但是要做的改造实在是太多了,目前还是主动上报为主。
onError 的方案会采集到全面的浏览器报错,但是太全了,出了刚才的 script error 问题,还会采集到。
为了让采集到的错误更有价值,需要暴露接口给让前端门自己在代码中上报异常(作为一名合格的代码工程师,一定要有抛异常的习惯呢)。
现在我们提供了自定义上报的接口,没错就是这么简单,你不用担心 JSTracker 脚本有没有加载,只要在你的代码中直接用就行了。
window.JSTracker2 = window.JSTracker2 || [];
try{
//your code
}catch(e){
JSTracker2.push({
msg: "xx_api_failed"
});
}
自己上报的错误对于定位也是很容易的:
try catch 对性能的影响微乎其微,但是一些用法会让性能受很大的影响, 参考这个实验:http://taobaofed.org/blog/2015/10/28/try-catch-runing-problem/
总结下来就是:在 try 语句块中不要定义太多的变量,最好是只写一个函数调用,避免 try 运行中变量拷贝造成的性能损耗。类似的不只是 try,定义 function 也是一样的。
JSTracker 的数据发送了非常大,这块都交给后端帮我们处理,采集部分只需要关注准确无误的把数据发送出去。
最初的简单的发送方案,直接用 GET 请求,将参数拼接在 URL 后面:
var url = 'xxx';
new Image().src = url;
后来发现这样发送有一定概率丢失数据,当浏览器回收内存的时候这个请求是发不出去的,所以要将这个变量 hold住:
var win = window;
var n = 'jsFeImage_' + _make_rnd(),
img = win[n] = new Image();
img.onload = img.onerror = function () {
win[n] = null;
};
img.src = src;
我们为了防止缓存,经常会用毫秒的时间作为随机数(如:+new Date()),但是在极端情况下可能1ms就会发出两条 log,这样第二条 log 就会丢失。
var _make_rnd = function(){
return (+new Date()) + '.r' + Math.floor(Math.random() * 1000);
};
为了能更好的统计这个浏览器,需要识别360SE、360EE。众所周知 3Q 大战之后360的 UA 就和 Chrome 的一样,所以我们要用一些特别的方法识别它们。目前为止能够识别的方案,这个方案会随时更新,适应360的变化。
var is360 = function(){
try {
if(/UBrowser/i.test(navigator.userAgent)){
return '';
}
if (typeof window.scrollMaxX !== 'undefined') {
return '';
}
var _track = 'track' in document.createElement('track');
var webstoreKeysLength = window.chrome && window.chrome.webstore ? Object.keys(window.chrome.webstore).length : 0;
if (window.clientInformation && window.clientInformation.languages && window.clientInformation.languages.length > 2) {
return '';
}
if (_track) {
return webstoreKeysLength > 1 ? ' QIHU 360 EE' : ' QIHU 360 SE';
}
return '';
}catch(e){
return '';
}
}();
← 淘宝首页兜底容灾方案
try catch 对代码运行的性能影响 →