最近一直在做前端js错误监控的工作,在不断的打磨和完善中,发现里面还是知识点不少,现在就前端js错误监控做一些笔记和总结
为什么要做前端错误监控?
-
为了保证产品的质量
-
有些问题只存在于线上特定的环境
-
后端错误有监控,前端错误没有监控
下面会讲:
- 前端错误的分类
- 错误的捕获方式
- 上报错误的基本原理
一. 前端错误的分类
- 即时运行错误:代码错误
- 资源加载错误:(js、css、图片加载失败)
二. 错误的捕获方式
- 即时运行错误的捕获方式
- try....catch
- window.onerror
- 资源加载错误的捕获方式
- object.onerror
- performance.getEntries
- Error事件捕获
1.即时运行错误的捕获方式
1.try...catch
通过try...catch我们能够知道出错的信息,并且也有堆栈信息可以知道在哪个文件第几行第几列发生错误。
try {
// 代码段
} catch (err) {
console.log(err.message)
}
缺点:
- 没法捕捉try,catch块,当前代码块有语法错误,JS解释器压根都不会执行当前这个代码块,所以也就没办法被catch住;
- 没法捕捉到全局的错误事件,也即是只有try,catch的块里边运行出错才会被你捕捉到,这里的块你要理解成一个函数块
2.window.onerror
全局捕获。window.onerror
会在页面发生js错误时被调用可以收集到错误字符串信息、发生错误的js文件,错误所在的行数、列数、和Error对象(里面会有调用堆栈信息等),还可以在window.onerror最后return true让浏览器不输出错误信息到控制台
/*
* @param message{String}:错误消息
* @param source{String}:引发错误的脚本的URL
* @param lineno{Number}:发生错误的代码行
* @param colno{Number}:发生错误的代码列
* @param error{object}:错误对象
*/
window.onerror = function (msg, url, line, colunm, error) {
// 返回 true 则错误消息不显示在控制台,返回 false,则错误消息将会展示在控制台
return true;
}
3.object.onerror
img、script标签都可以添加onerror事件,当资源请求失败的时候,都会触发该事件。
var img = document.getElementById('img');
img.onerror=function(){
console.log("出错啦");
}
4.performance.getEntries
performance是h5的新特性之一,使用该方法能获取到当前页面已经加载到的资源,返回的是一个数组对象。
例子获取页面中没有成功加载的图片资源?
let arr = [],
imgList=null,
num=0,
reg = (/.jpg$|.jpeg$|.png$|.gif$/i);
// 1.通过performance.getEntries()获取已经加载了的图片资源
performance.getEntries().forEach(item => {
if (reg.test(item.name)) {
arr.push(item.name)
}
})
// 2.获取页面中所有的img标签
imgList=document.getElementsByTagName('img');
// 3.利用获取到的img的长度减去已经加载到的长度,如果大于0的部分,就是加载失败的
num=imgList.length-arr.length;
控制台使用: var arr=[]; performance.getEntries().forEach((item,i)=>{console.log(arr[i]=item.name)})
打印出网站中成功加载的所有资源。
通过 document.getElementsByTagName('img').length
减去上面的成功加载就能得出报错的图片资源数量了。
// 返回网站内所有成功加载的png文件数量
var arr=[];
performance.getEntries().forEach((item,i)=>{console.log(arr[i]=item.name)})
arr.filter(function(item, index, arr){
var search1 = arr[index];
return search1.includes('.png',search1.length-4) != false;
}).length;
5. Error事件捕获
资源加载错误,虽然会阻止冒泡,但是不会阻止捕获。true:捕获,false:冒泡
// window.addEventListener第三个参数是true的时候是捕获的过程,false是冒泡的过程
window.addEventListener('error',function(e){
console.log("捕获",e)
},true)
6.延伸
为了提升web性能,大部分web产品都有CDN部署,将资源部署到不同的域名上,但是我们都知道浏览器是有同源策略的,当加载不同域名的脚本发生错误时,语法错误的细节不会报告,仅返回"Script error"
既然跨域JS运行错误可以捕获,错误提示是什么,应该怎么处理?
必须做俩件事:
- 客户端:在请求资源的
script
标签中增加crossorigin
属性 - 服务端:设置
js
资源响应头Access-Control-Origin:*
<script type="text/javascript" src="http://domain.com/a.js" crossorigin></script>
三. 上报错误的方式
开发 Web 应用程序过程中的一种常见的做法,就是集中保存错误日志,以便查找重要错误的原因。那么我们怎么将js的错误信息记录到服务器数据库库中呢。
- 采用Ajax通信的方式上报 (所有的错误监控都不是通过这种方式来做的;)
- 利用Image对象上报(所有的监控体系都是这样做的,如谷歌)
1. 采用Ajax通信的方式上报
ajax来实现的弊端
-
不支持跨域操作,因为很多情况下是一台服务器要负责处理多台服务器的错误;
-
大多数Ajax通信都是通过javascript库提供的包装函数来处理,如果库代码本身就有问题, 而你还在依赖该库记录信息,可想而知,错误消息是不肯能得到记录的。
怎么办?我们可以使用Image对象巧妙的解决这个问题
2.利用Image对象上报
Image对象的优点:
-
所有浏览器都支持 Image 对象,包括那些不支持
XMLHttpRequest
对象的浏览器。 -
可以避免跨域限制。通常都是一台服务器要负责处理多台服务器的错误,而这种情况下使用
XMLHttpRequest
是不行的。 -
在记录错误的过程中出问题的概率比较低。大多数 Ajax 通信都是由 JavaScript 库提供的包装函 数来处理的,如果库代码本身有问题,而你还在依赖该库记录错误,可想而知,错误消息是不 可能得到记录的。
/* 方法1 */
//利用这种方式发送一个请求非常简单,比Ajax简单,不需要借助任何第三方库;
(new Image()).src="http://baidu.com/test?r=error";
//一行代码实现一个资源向上报;/test?这个是上报路径;r=error加信息
/* 方法2 配合try...catch 更佳*/
function logError(sev, msg){
var img = new Image();
img.src = "log.php?sev=" + encodeURIComponent(sev) + "&msg=" + encodeURIComponent(msg);
}
for (var i=0, len=mods.length; i < len; i++){
try {
mods[i].init();
} catch (ex){
logError("nonfatal", "Module init failed: " + ex.message);
}
}