作好错误监控,将用户使用时的错误日志上报,能够帮助咱们更快的解决一些问题。目前开源的比较好的前端监控有html
那前端监控是怎么实现的呢?要想了解这个,须要知道前端错误大概分为哪些以及如何捕获处理。前端
前端错误分为JS运行时错误、资源加载错误和接口错误三种。vue
JS运行时错误通常使用window.onerror捕获,可是有一种特殊状况就是promise被reject而且错误信息没有被处理的时候抛出的错误webpack
使用window.onerror和window.addEventListener('error')捕获。其中window.onerror含有详细的error信息(error.stack),并且兼容性更好,因此通常JS运行时错误使用window.onerror捕获处理git
window.onerror = function (msg, url, lineNo, columnNo, error) { // 处理error信息 } window.addEventListener('error', event => { console.log('addEventListener error:' + event.target); }, true); // true表明在捕获阶段调用,false表明在冒泡阶段捕获。使用true或false均可以
例子: https://jsbin.com/lujahin/edit?html,console,output 点击button抛出错误,分别被window.onerror和window.addEventListener('error')捕获
当promise被reject而且错误信息没有被处理的时候,会抛出一个unhandledrejection,而且这个错误不会被window.onerror以及window.addEventListener('error')捕获,须要用专门的window.addEventListener('unhandledrejection')捕获处理github
window.addEventListener('unhandledrejection', event => { console.log('unhandledrejection:' + event.reason); // 捕获后自定义处理 });
https://developer.mozilla.org...
例子: https://jsbin.com/jofomob/edit?html,console,output 点击button抛出unhandledrejection错误,而且该错误仅能被window.addEventListener('unhandledrejection')捕获
一些特殊状况下,还须要捕获处理console.error,捕获方式就是重写window.console.errorweb
var consoleError = window.console.error; window.console.error = function () { alert(JSON.stringify(arguments)); // 自定义处理 consoleError && consoleError.apply(window, arguments); };
例子: https://jsbin.com/pemigew/edit?html,console,output
什么是跨域脚本error?api
https://developer.mozilla.org...
当加载自不一样域的脚本中发生语法错误时,为避免信息泄露(参见bug 363897),语法错误的细节将不会报告,而代之简单的"Script error."。在某些浏览器中,经过在<script>使用crossorigin属性并要求服务器发送适当的 CORS HTTP 响应头,该行为可被覆盖。一个变通方案是单独处理"Script error.",告知错误详情仅能经过浏览器控制台查看,没法经过JavaScript访问。例子: http://sandbox.runjs.cn/show/... 请打开页面打开控制台。该页面分别加载了两个不一样域的js脚本,配置了crossorigin的window.onerror能够报出详细的错误,没有配置crossorigin只能报出'script error',而且没有错误信息跨域
在线上因为JS通常都是被压缩或者打包(webpack)过,打包后的文件只有一行,所以报错会出现第一行第5000列出现JS错误,给排查带来困难。sourceMap存储打包前的JS文件和打包后的JS文件之间一个映射关系,能够根据打包后的位置快速解析出对应源文件的位置。promise
可是出于安全性考虑,线上设置sourceMap会存在不安全的问题,由于网站使用者能够轻易的看到网站源码,此时能够设置.map文件只能经过公司内网访问下降隐患
sourceMap配置devtool: 'inline-source-map'
若是使用了uglifyjs-webpack-plugin 必须把 sourceMap设置为true
https://doc.webpack-china.org...
1.6.1 sentry把全部的回调函数使用try catch封装一层
https://github.com/getsentry/raven-js/blob/master/src/raven.js
1.6.2 vue errorHandler
https://vuejs.org/v2/api/#errorHandler
其原理也是使用try catch封装了nextTick,$emit, watch,data等
https://github.com/vuejs/vue/blob/dev/dist/vue.runtime.js
使用window.addEventListener('error')捕获,window.onerror捕获不到资源加载错误
https://jsbin.com/rigasek/edit?html,console 图片资源加载错误。此时只有window.addEventListener('error')能够捕获到window.onerror和window.addEventListener('error')的异同:相同点是均可以捕获到window上的js运行时错误。区别是1.捕获到的错误参数不一样 2.window.addEventListener('error')能够捕获资源加载错误,可是window.onerror不能捕获到资源加载错误
全部http请求都是基于xmlHttpRequest或者fetch封装的。因此要捕获全局的接口错误,方法就是封装xmlHttpRequest或者fetch
if(!window.XMLHttpRequest) return; var xmlhttp = window.XMLHttpRequest; var _oldSend = xmlhttp.prototype.send; var _handleEvent = function (event) { if (event && event.currentTarget && event.currentTarget.status !== 200) { // 自定义错误上报 } } xmlhttp.prototype.send = function () { if (this['addEventListener']) { this['addEventListener']('error', _handleEvent); this['addEventListener']('load', _handleEvent); this['addEventListener']('abort', _handleEvent); } else { var _oldStateChange = this['onreadystatechange']; this['onreadystatechange'] = function (event) { if (this.readyState === 4) { _handleEvent(event); } _oldStateChange && _oldStateChange.apply(this, arguments); }; } return _oldSend.apply(this, arguments); }
if(!window.fetch) return; let _oldFetch = window.fetch; window.fetch = function () { return _oldFetch.apply(this, arguments) .then(res => { if (!res.ok) { // True if status is HTTP 2xx // 上报错误 } return res; }) .catch(error => { // 上报错误 throw error; }) }
利用以上原理,简单写了一个JS监控,只处理了一些JS错误,暂时没有作和性能相关的监控
https://github.com/Lie8466/better-js
若是发现文章有错误,欢迎指正。