线上问题监控 Sentry 接入全过程
线上问题 异常回放,日志监控平台
背景:
线上偶发问题出现后 ,测试人员仅通过接口信息无法复现错误场景;并且线上环境的监控,对于提高系统的稳定性 (降低脱发率) 至关重要;现在线上监控工具这个多,为什么选择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 = TEST
VUE_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` 方法用于将插件安装到 Vue
install (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;
},
// 设置分布式追踪的目标 URL
tracePropagationTargets: [],
// 性能监控配置
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 tokens
org: 'sentry', // 上文中的组织名称
project: 'environment-assistant', // 上文中的项目名称
urlPrefix: "./", // 上传资源的路径前缀,路径通常是 /static/js, 如有变化自行更改
//如在子域名(如https://www.baidu.com/house-inspection/#/overall),urlPrefix改成"~/house-inspection/"
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
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)