背景:
线上偶发问题出现后 ,测试人员仅通过接口信息无法复现错误场景;并且线上环境的监控,对于提高系统的稳定性 (降低脱发率) 至关重要;现在线上监控工具这个多,为什么选择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

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐