最近要做一个前端异常日志收集的功能,仔细想想,如果只是做一个日志上报的功能,貌似没多大意义。本着服务大众的想法,结合之前图片重试功能的开发,何不开发一个webpack插件来完成收集日志的功能呢。最终目标就是只需要简单配置一些配置项,就可以自动生成一份异常监控的代码,insert到入口文件。
先看看使用效果:
配置以下打包脚本:
plugins: [ new HtmlWebpackPlugin({ template: './src/index.html', filename: './index.html', minify: true, inject: false, showErrors: true, chunks: ['vendors', 'index'] }), new ExceptionReportPlugin({ srcPath: '../demo/src/index.html', // 必须, 入口文件 ratio: 'commonConfig.ratio', // 上报比例 必填 默认 0.1 loggerSwitcher: 'commonConfig.logger', // 监控开关 必填 jsSwitcher: 'commonConfig.jsswitcher', // jserro开关 必填 exceptionWhiteList: 'commonConfig.exception_whitelist', business: 'yourdomain Or keyword', //必传,业务标记 headerScript: 'exceptionReport', // 头部的script标记 不可修改 footerScript: 'bottomScript' // 底部的script标记 不可修改 }) ],
打包后的效果
在此之前,我发现目前错误监控的方式比较粗暴,在入口文件的底部有一段用于捕获异常的脚本:
window.onerror = function(message, source, lineno, colno, error){ doReport(message, source, lineno, colno,error) // doReport 为上报异常的方法 }
后来发现,这样捕获到的异常非常少的,查阅了一些资料,发现代码还是存在以下问题。
首先,使用window.onerror捕获异常存在一些局限性。
了解下window.onerror:当JavaScript运行时错误(包括语法错误)发生时,window会触发一个ErrorEvent接口的事件,并执行window.onerror();但语法错误会导致出现语法错误的那个脚本块执行失败,所以语法错误会导致当前代码块运行终止,从而导致整个程序运行中断,如果语法错误发生在我们的错误监控语句块中,那么我们就什么也监控不到了。
window.onerror 捕获不到资源加载异常的情况,此时考虑使用事件监听window.addEventListerner(‘error’)来捕获异常。也就是说window.onerror 与 window.addEventListener(‘error’)相比较,后者能捕获的异常更加全面一些。
另外window.onerror,无法捕获Promise异常。最新的规范中定义了 unhandledrejection事件用于全局捕获promise对象没有rejection处理器时异常情况。
前端框架是怎么捕获错误?
有同事发现在vue框架中某些错误并没有被捕获,后来发现VUE提供了专门的方法Vue.config.errorHandler来处理捕获到的错误。
// err: 捕获到的错误对象。 // vm: 出错的VueComponent. // info: Vue 特定的错误信息,比如错误所在的生命周期钩子 Vue.config.errorHandler = function (err, vm, info) {}
此外还存在一个问题,如果入口文件head部分有js脚本,底部的onerror 可能捕获不到异常。
基于上面的分析,我们需要重新编写代码来捕获异常。
我个人的思路是:
- 在页面head部分插入内联的js脚本,用于收集异常,异常收集要涵盖上述几个方面;
- 在页面底部加入内联的js脚本,对收集到的异常集中上报处理。
- 通过打包工具,将脚本植入到指定的文件相应的位置中。
所以,工作包含两个方面:
- 实现异常捕获和异常日志上报脚本
- 实现打包插入脚本的插件
先说webpack插件开发:
因为项目中用到了html-webpack-plugin插件,作用就是生成html入口文件,具体如何使用这里就不展开。
html-webpack-plugin 插件作者提供了插件事件,允许其他插件来改变html文件内容。提供事件如下:
Async(异步事件):
* html-webpack-plugin-before-html-generation // 生产html之前
* html-webpack-plugin-before-html-processing // 处理html文件之前
* html-webpack-plugin-alter-asset-tags // 处理tag 比如link srcipt 之后
* html-webpack-plugin-after-html-processing // 处理html文件之后
* html-webpack-plugin-after-emit
Sync(同步事件):
* html-webpack-plugin-alter-chunks
我们可以使用这些事件来开发自己的插件。
function newPlugin(options) { // 配置项处理 } newPlugin.prototype.apply = function(compiler) { compiler.plugin('compilation', function(compilation) { compilation.plugin('html-webpack-plugin-before-html-processing', function(htmlPluginData, callback) { // todo 对htmlPluginData.html进行处理,然后返回 // to do 骚操作,将打包好的脚本 insert到入口文件中 callback(null, htmlPluginData); }); }); }; module.exports = MyPlugin;
接下来就是如何组织异常捕获和异常上报的通用代码了:
参考:
https://segmentfault.com/a/1190000016959011
https://www.cnblogs.com/datiangou/p/10224846.html