那就要说说我们的项目生产体系,在我们的开发阶段开发人员会产生一些隐式的代码问题如果这些问题没有在测试阶段被暴露那么这些问题就会被带入线上这部分问题就会被无限放大。。。为啥会这样?毕竟人是最不稳定因数,为了弥补这些我们在开发阶段进行代码格式化校验排除一部分显示错误,开发人员不断的跑流程测试可行性,合并阶段进行codereview让代码再次过审,测试阶段不断发现问题对产品不断优化改进。。。。尽管我们尽了一切努力依然没办法让我们的项目在线上完美运行,时不时需要来个hotfix。。。这并不是我们所希望的发生的。但是真正的痛点在于我们无法准确定位问题,无法准确复现,那么一个问题如果不知道原因那么终将无解。客户:balabala…,我们:一脸闷b打开一看啥事没有。。。。所以为了很好的解决这一块的问题我们需要对项目进行实时监控,准确复现案发现场。
1:try-catch 应该是项目中出现频率比较高的异常处理
let a = null
try {
console.log('ceshi', a.splice(','))
console.log('我永远并不会继续执行')
} catch (err) {
console.log('err', err)
}
console.log('虽然你报错了但是我还是继续执行')
从上面的代码中我们我们可以捕捉到try块中的js报错,但是对于try-catch之外的同步代码还是会继续执行,部分出错,但是并不影响后续代码运行,最终的反馈就是页面不会卡死
let a = null
try {
setTimeout(() => {
console.log('ceshi', a.splice(','))
});
console.log('我会继续执行')
} catch (err) {
console.log('err', err)
}
console.log('虽然你报错了但是我还是继续执行')
2:window.onerror 异常捕捉
/**
* @param {String} msg 错误信息
* @param {String} url 出错文件
* @param {Number} row 行号
* @param {Number} col 列号
* @param {Object} error 错误详细信息
*/
setTimeout(() => {
let a = null
console.log('ceshi', a.splice(','))
});
window.onerror = function (msg, url, row, col, error) {
console.log('我知道错误了', {
msg, url, row, col, error
})
return true;
};
运行上面的代码我们可以看出window.onerror
貌似可以捕捉到异步错误,而且报错信息很全
let a = null
window.onerror = function (msg, url, row, col, error) {
console.log('我知道同步错误了', {
msg, url, row, col, error
})
return true;
};
console.log('ceshi', a.splice(','))
console.log('我将不会运行')
此时我们看的window.onerror
其实还不错,同步异步都可以捕捉到
但是有个问题就是window.onerror
必须先加载不然是无法捕捉错误,但是对于引入的js我们就必须进行跨域处理,不然无法取到报错详情否则只会报出一个脚本错误无任何详情也就失去了意义,所以解决这个问题就需要在引入资源的时候加入一个crossorigin的属性,同时静态资源请求需要加多一个Access-Control-Allow-Origin头部还是比较麻烦的。。。
所以可以总结一下
try-catch有如下特点:
window.onerror有如下特点:
后面还有比较多的报错捕捉比如
img的错误捕捉可以借用img标签的onerror事件,img标签支持onerror 事件,在装载文档或图像的过程中如果发生了错误,就会触发onerror事件。可以使用一张提示错误的图片代替显示不了的图片。代码如下:
<img src="images/logo.png" onerror="javascript:this.src='images/logoError.png';">
或者:
<script type="text/javascript">
function imgerrorfun(){
var img=event.srcElement;
img.src="images/logoError.png";
img.onerror=null;
}
</script>
<img src="images/logo.png" onerror="imgerrorfun();" />
当然对于代码中出现比较多的promise来说以上的监控返回也是无能为力。。。最好的方式就是在写 Promise 实例的时候养成最后写上 catch 函数,但是大多数时候会被忽略。即使这样还是有方法来处理这些报错。
window.addEventListener("unhandledrejection", function(e){
e.preventDefault()
console.log('我知道 promise 的错误了');
console.log(e.reason);
return true;
});
Promise.reject('promise error');
new Promise((resolve, reject) => {
reject('promise error');
});
new Promise((resolve) => {
resolve();
}).then(() => {
throw 'promise error'
});
总的来说要做到无缝监控需要时间积累,并且需要前后端相互配合才行,理想和现实还是有距离的。。。
接下来所说上报方式:
目前有两种主流的形式
这一块简单说吧,就是再发错误的时候进行上报到指定的链接,发送指定的参数即可(说的比较简短实际上是一个复杂的过程)
说了这么多总归要进入主题:sentry
为啥用sentry?其实很简单,就是不要钱+可以自己部署服务
而且还提供了各种sdk算是很全了。
sentry,中文翻译是哨兵。它是一个错误监控和收集工具。用户在项目使用中,遇到报错,sentry会第一时间通知开发者,项目出现了什么错误,错误出现在哪儿,并且会帮我们记录错误。
关于如何使用sentry并且申请的可以进入他们的官网进行查看,总的流程不是很难,网上教程也比较多,这里就不做过多的赘述。我们还是直接看一下我们在项目中如何引入sentry,如何使用以及一些注意的点
官网那边申请好apikey与dsn后
// 安装官方提供的库
@sentry/browser:通过包含和配置Sentry,SDK将自动附加全局处理程序以捕获未捕获的异常和未处理的拒绝。
@sentry/integrations:将捕获引发错误的Vue活动组件的名称和props状态。
yarn add @sentry/browser
yarn add @sentry/integrations
在 main.js 文件里面引入
import * as Sentry from '@sentry/browser'
import * as Integrations from '@sentry/integrations'
Sentry.init({
dsn: 'https://[email protected]/xxxxx',
integrations: [new Integrations.Vue({Vue, attachProps: true})],
})
我们重新运行一下项目之后就可以开始监控啦
当前可能还不是很完全所以需要加入sourcemap定位代码错误的位置
yarn add -g @sentry/cli
在vue.config.js
const SentryCliPlugin = require('@sentry/webpack-plugin')
const config = require('./package.json')
const Version = config.version
configureWebpack: config => {
config.devtool = 'source-map'
config.plugins.push(
new SentryCliPlugin({
include: './dist', // 需要上传的文件
suppressErrors: true, // 上传失败时不阻碍webpack继续执行
release: Version, // 对应的版本号
configFile: 'sentry.properties', // 暂时写死
// ignore: ['node_modules'], // 好像没啥用
urlPrefix: 'http://localhost:5000/', // 前缀(很重要)
}),
)
},
在main.js加入release(版本号,因为可能你会上传多个版本号,需要用到版本号对应指定的代码才可以)
const config = require('../package.json')
const Version = config.version
Sentry.init({
dsn: 'https://[email protected]/xxxxx',
integrations: [new Integrations.Vue({Vue, attachProps: true})],
release: Version,
})
以上做好其实监控体系已经完成,后期需要与运维一起将sentry部署一个到自己的服务器,后期本人还会介绍如何使用docker进行部署服务