前端监控是个很庞大丰富的内容,其包含页面UI监控,网络监控,性能监控,错误监控等内容,今天来简单谈谈前端错误监控。
错误种类
这里的前端错误指的是会在控制台抛出的异常,不包括业务逻辑及UI错误。
- JS语法错误
- JS运行错误(分为同步错误,异步错误)
- 网络请求错误(分为静态资源加载,异步请求)
- promise未处理异常
- 跨域脚本异常(script error)
错误收集方法
前端错误的种类不多,但是不同的错误往往需要不同的收集方法,所以完整的错误日志需要不同的收集方法的结合才能全部收集。
try-catch
try-catch
能捕获代码块中的JS运行错误
try { a } catch (err) { // ReferenceError: a is not defined // at err.html:12 } 复制代码
try-catch
无法捕获异步错误
try { setTimeout(() => { a }) } catch (err) { // 无法捕获错误 } 复制代码
try-catch
无法捕获语法错误
try { , } catch (err) { // 无法捕获错误 } 复制代码
window.onerror
onerror
可以捕获语法错误和运行时错误
语法错误
/** * @param {String} msg 错误信息 * @param {String} url 出错文件 * @param {Number} row 行号 * @param {Number} col 列号 * @param {Object} error 错误详细信息 */ window.onerror = (msg, url, row, column, error) => { // Uncaught SyntaxError: Invalid or unexpected token return true; // 控制台不再输出错误 } , 复制代码
运行错误
window.onerror = (msg, url, row, column, error) => { // Uncaught ReferenceError: a is not defined } a 复制代码
异步错误
window.onerror = (msg, url, row, column, error) => { // Uncaught ReferenceError: a is not defined } setTimeout(() => { a }) 复制代码
errorEvent
对于静态资源的加载错误我们可以用 window.addEventListener('error')
来捕获
<img src="./404.png" alt=""> 复制代码
能得到的错误信息较少,类型为 error
的事件对象
window.addEventListener('error', (e) => { // type 为 error // 可以通过 e.target 来判断是否为静态资源请求错误,避免和 window.onerror 重复收集 }, true); 复制代码
对于接口的异步请求则需要监听 xhr 实例的方法,例如 load
const xhr = new XMLHttpRequest(); xhr.addEventListener('load', (e) => { const { responseURL, status, statusText } = e.target; // example.png 404 Not found }) xhr.open('GET', 'example.png'); xhr.send(); 复制代码
一般通过 xhr 的实例方法改写来实现错误收集,网上的资料很多,大家可以自行搜索。
unhandledrejectionEvent
现在我们常用 Promise 来解决回调地狱的问题,如果 Promise 的 rejection 状态没被处理会怎么样呢?
Promise.reject(2) // Uncaught (in promise) 2 复制代码
对于未处理的 rejection 会控制台输出,我们可以使用 unhandledrejection
来捕获
window.addEventListener('unhandledrejection', (e) => { const { type, reason } = e; // unhandledrejection 2 e.preventDefault(); // 控制台不再输出reject }) 复制代码
错误收集方法小结
前面我们介绍了不同的错误收集方法
JS语法错误 | JS运行错误 | JS异步错误 | 静态资源加载 | 请求异常 | Promise未处理异常 | |
try-catch | x | √ | x | x | x | x |
onerror | √ | √ | √ | x | x | x |
errorEvent | x | √ | √ | √ | x | x |
unhandledrejection | x | x | x | x | x | √ |
请求事件监听/重写 | x | x | x | x | √ | x |
可以看出,onerror
的功能最强大,可以收集最多错误类型,而且错误内容比较齐全,包含错误位置及错误信息。所以我们可以使用 onerror
作为统一的错误收集方法,同时对 静态资源加载
请求异常
Promise未处理异常
单独进行处理收集。在复杂的业务流程中则使用 try-catch
来做收集,容错处理。
注意防止重复收集。
跨域脚本异常(Script error)
Script error
是浏览器在同源策略限制下产生的,浏览器处于对安全性上的考虑,当页面引用非同域名外部脚本文件时中抛出异常的话,此时本页面是没有权利知道这个报错信息(包括语法错误,运行错误,promise未处理异常)的,取而代之的是输出 Script error
这样的信息。
所以跨域JS脚本的错误详情无法在主页面脚本捕获,只能得到 Script error
,而未处理的promise异常也无法在 unhandledrejection
中捕获。
如何解决呢?在脚本标签中加上 crossorigin
属性
<script src="http://localhost:8081/test.js" crossorigin></script> 复制代码
同时后端服务器按照 CORS
配置可访问域名就行,具体配置参考 CORS(跨源资源共享)
上报
上报方式和正常的请求一样,可以使用
- img 上报
- ajax 上报
function report(errInfo) { new Image().src = 'http://your-api-website?data=' + errInfo; } 复制代码
注意:img 请求有长度限制,数据太大最好还是用 ajax.post。
如果遇到错误信息太多的话,可以做了随机过滤
function report(errInfo) { if (Math.random() > 0.3) { new Image().src = 'http://your-api-website?data=' + errInfo; } } 复制代码
待完善
由于自己对 sourceMap
的原理及使用方面的知识不足,所以没有研究如何通过 sourceMap
使压缩后的行列和源码位置相对应,后期有研究再补充上。
总结
本文主要是向大家介绍了不同的错误类型及其对应的错误收集方法,如果在业务中有错误监控平台需求的话,还需了解更多的相关知识和业务经验才行。