背景:
线上偶发问题出现后 ,测试人员仅通过接口信息无法复现错误场景;并且线上环境的监控,对于提高系统的稳定性 (降低脱发率) 至关重要;现在线上监控工具这个多,为什么选择Sentry? 因为它能够回放(录屏) ,自动收集报错,用户多,免费且改造成本低等各种优点。为什么写这篇文章? 因为其他文章都没啥参考性,要么过时了,要么太短了。下面这些内容都已经正式上线了,刚写完,新鲜的!
环境参数:
node version is 14.16.1
“vue”: “^2.5.2”,
“@sentry/vue”: “^7.98.0”,
@sentry/webpack-plugin": "^2.10.3
一、接入步骤
1、修改配置文件
配置DSN、AUTH_TOKEN、RELEASE、CURRENTENV
DSN 是服务端与客户端通信的密钥,发送事务的目的地;AUTH_TOKEN 是用户token,用于权限控制;
RELEASE 是版本号,用于判断修改后的效果;CURRENTENV当前的环境,非必须。
示例(在 .env.test 环境文件中写入):
VUE_APP_SENTRY_AUTH_TOKEN = 25a7bcf607c19c72af0f30fae9...VUE_APP_SENTRY_DSN = DSN...VUE_APP_CURRENTENV = TESTVUE_APP_RELEASE=staging@1.0.1
2、下载依赖@sentry/vue 和 @sentry/webpack-plugin
npm install --save @sentry/vue @sentry/webpack-plugin
3、初始化 Sentry,并配置相关选项,在main.js同级新增 sentry.js 文件。
import * as Sentry from "@sentry/vue";// 导出一个对象,其中包含 `install` 和其他方法
export default {// `install` 方法用于将插件安装到 Vueinstall (Vue, options) {this.init(Vue, options);// 在 Vue 实例上挂载 Sentry 对象Vue.prototype.$sentry = Sentry;// 在 Vue 实例上挂载自定义的错误捕获方法Vue.prototype.$httpSentryCaptureMessage = this.httpSentryCaptureMessage;Vue.prototype.$captureMessage = this.captureMessage;},// 初始化 Sentry,并配置相关选项init (Vue, {router}) {// 初始化 Sentry,配置 DSN、环境、调试模式等Sentry.init({Vue,dsn: process.env.VUE_APP_SENTRY_DSN,release: process.env.VUE_APP_RELEASE, // 与 vue.config 中的保持一致environment: process.env.VUE_APP_CURRENTENV,debug: true,// http:{"verify_ssl": false},integrations: [// 启用浏览器性能追踪Sentry.browserTracingIntegration({ router }),// 设置会话回放的配置Sentry.replayIntegration({maskAllText: true, // 是否屏蔽所有文本内容blockAllMedia: true, // 是否阻止所有媒体内容的回放networkDetailAllowUrls: [window.location.origin], // 请求和响应标头或正文的允许 URL 列表}),],initialScope: {// 可以在这里设置初始的 Scope 信息// level: "info",},// 在每次发送 event 前触发的钩子函数beforeSend(event) {// event.level = "warning"event.tags.location = window.location.href;return event;},// 设置分布式追踪的目标 URLtracePropagationTargets: [],// 性能监控配置tracesSampleRate: 1.0, // 采集 100% 的事务// 会话回放配置replaysSessionSampleRate: 0.1, // 设置采样率为 10%。在开发时可能希望设置为 100%,然后在生产中采样率更低。replaysOnErrorSampleRate: 1.0, // 如果你不是已经采样了整个会话,在错误发生时改变采样率到 100%。});},/*** 主动上报捕获的异常。* @param { string } title 异常标识* @param { object } params 额外参数* @param { object } stack 错误对象或微信返回对象*/async captureMessage(title, params, stack = {}) {try {// 判断错误类型并上报const isError = typeof stack === 'object' && !!stack.stack;const errorName = (isError ? stack.message : stack.errMsg) || 'unknown';const extra = {params,errMsg: isError ? stack : stack.errMsg || '',href: location.href,};// 使用 Sentry 上报错误Sentry.withScope((scope) => {scope.setFingerprint([title, errorName]);const errMessage = new Error(errorName);errMessage.name = `前端异常上报:${title}`;console.log('前端异常:', title);Sentry.captureException(errMessage, {extra,level: 'error',});});} catch (error) {console.log('sentry:', error);}},/*** 上报服务请求异常。* @param {*} stack 错误信息对象*/async httpSentryCaptureMessage(stack) {try {// 处理服务请求异常,决定是否上报const errorMsg = stack.message;const errorCode = (stack.response && stack.response.status) || 0;if ([401, 403, 40301].includes(errorCode)) {// 过滤特定的凭证错误return;}// 根据错误信息和状态码设置错误名称let errorName;if (/timeout/i.test(errorMsg) || errorCode===504) {errorName = '接口超时';} else if (/^4\d{2}$/.test(errorCode) || /^5\d{2}$/.test(errorCode)) {errorName = `服务端${errorCode}错误`;} else {errorName = '调用异常';}const extra = {...(stack.config || {}),errMsg: stack,href: location.href,};// 如果是接口超时,上报接口的参数errorName === '接口超时' && (extra.networkInfo = await this.getResourceLoad('xmlhttprequest', extra.url));// 使用 Sentry 上报异常Sentry.withScope((scope) => {scope.setFingerprint([extra.method, extra.url, errorName]);const errMessage = new Error(`异常接口地址: ${extra.url}`);errMessage.name = errorName;Sentry.captureException(errMessage, {extra,level: 'error',});});} catch (error) {console.log('sentry:', error);}},/*** 获取资源加载信息。* @param { string } type 资源类型* @param { string } name 资源名称匹配* @returns { Promise<Object> } 返回资源信息的 Promise 对象*/getResourceLoad(type = 'xmlhttprequest', name = '') {return new Promise((resolve) => {if (!window.performance) {resolve({});return;}setTimeout(() => {// 获取并过滤性能监测中的资源信息const list = window.performance.getEntries().filter((item) => item.initiatorType === type) || [];if (!name) {resolve(list);return;}// 查找匹配资源信息let result = {};for (let i = list.length - 1; i >= 0; i -= 1) {if (list[i].name && list[i].name.indexOf(name) >= 0) {result = list[i];break;}}resolve(result);}, 50);});}}
在这里插入代码片
4、作为插件挂载到Vue上,在main.js中引入 ;
import SentryReport from './sentry.js';if(process.env.VUE_APP_CURRENTENV!='DEV'){// 开发时引入,会导致控制台输出内容不能指定到所在行,所以打包时才引入Vue.use(SentryReport, {router})}
5、通过webpack打包时上传sourcemap,用于映射线上错误代码的具体位置。
在vue.config.js中加入configureWebpack的配置;
configureWebpack: (config) => {//产生map文件config.devtool='source-map';if(process.env.VUE_APP_CURRENTENV !='DEV'){Object.assign(config, {plugins: [...config.plugins,sentryWebpackPlugin({url: "https://test-sentry.scxljs.cn/", // Sentry 后台地址release: process.env.VUE_APP_RELEASE, // 和 Sentry.init 中的保持一致include: path.join(process.cwd(), "/dist"), // 需要上传到 sentry 服务器的资源目录ignore: ["node_modules", "vue.config.js"], // 忽略文件目录,如果在 inlcude 中已经定义了具体路径,这个参数可以不加authToken: process.env.VUE_APP_SENTRY_AUTH_TOKEN, // 上文中的 User auth tokensorg: 'sentry', // 上文中的组织名称project: 'environment-assistant', // 上文中的项目名称urlPrefix: "./", // 上传资源的路径前缀,路径通常是 /static/js, 如有变化自行更改cleanArtifacts: true, // 先清理再上传debug: true,sourcemaps: {// assets: ['./dist'],//上传后删除映射文件filesToDeleteAfterUpload: ['./dist/js/**/*.map', ]},errorHandler: (error) => {console.log('upload SourceMap error', error)},}),],});}},
6、在首次拿到用户信息的地方设置用户信息
let userInfo = {username: res.data.userName || '-1',id: res.data.userId || '-1',email: res.data.userName, //为方便辨识用户,将email设置为userName
}
// 给issue传递用户信息
this.$sentry.setUser(userInfo);
7、主动上报异常
axios的请求响应拦截中:
Vue.prototype.$httpSentryCaptureMessage(err)
业务场景中:
this.$sentry.httpSentryCaptureMessage(err);
二、异常自动推送
1、在Sentry服务端,配置Alert规则,即可通知相关人员;
2、还可通过webhook借助飞书捷径+飞书机器人 通知到相应的责任人(见参考资料),配置alert时,一定要检查发送途径是否选了webhook。
三、私有化部署
私有化部署部署前,先看好硬件配置要求(https://develop.sentry.dev/self-hosted/),下包前,先把梯子搭好,下外面的官方版本,不然会爆发很多奇怪的问题。
四、遇到的问题
1、在技术预研时,没有私有化部署,直接用了官方的平台,但因为国内墙的原因(翻墙也不行),上传sourcemap时,控制台会报错 API request failed
,研究了一段,进行了私有化部署后就能解决;
2、慎用 Sentry.close ,会出现sentry中issue与replay不对应的情况;
3、sentry-cli对node版本有要求,14.16及以下无法使用。
参考资料及其他:
使用 Sentry 做异常监控 - 借助飞书捷径
不明白的欢迎交流!
感谢美帝~
提了个issue,以为会石沉大海,没想到一直都有回应,ღ( ´・ᴗ・` )比心!
记录下这个关闭的issue
https://github.com/getsentry/sentry-javascript-bundler-plugins/issues/470