Sentry最佳实践

902 阅读3分钟

项目技术栈:next@13.2.4、@sentry/nextjs@^7.65.0、@reduxjs/toolkit@^1.9.3

本文并不搬运官方文档中已有的内容,只是对实践中觉得有用的部分加以说明总结

基础配置

官方文档:docs.sentry.io/platforms/j…

  • setUser:上报用户信息。可以设置用户登录后的ID或邮箱,这样当某个用户反馈问题的时候可以按用户筛选

  • Source map 对于定位异常所在的源码位置很有用。实践中很多异常是无法定位到具体位置的,并非是source map不生效,而是异常的位置出现在项目依赖包中(更准确的表达是,不是当前nextjs项目负责编译并打包的那部分代码),这一类很可能是噪音,这个后面会讨论

    • 如果需要手动配置要上传source map的文件,可以通过以下方式配置:

          include: [
            {
              paths: ['./.next/static/chunks'],
              urlPrefix: '~/sign/_next/static/chunks',
            },
            // ...
          ],
          ```
      
      
    • 但是@sentry/nextjs可能会默认上传一些所需文件的sourcemap,所以可能是无需额外配置的

    • 其中urlPrefix是指上传到sentry服务的source map文件前缀,在项目设置中可以看到:

      image.png

      Sentry根据文件路径匹配source map文件,例如当浏览器中出现异常的文件为<your_website_host>/sign/_next/static/chunks/681-c6678eff4302fed3.js,那么Sentry会去查找source map文件<sentry_host>/sign/_next/static/chunks/681-c6678eff4302fed3.js.map

  • 排除噪音

    • ignoreErrors
    • denyUrls
    [
      // Chrome extensions
      /extensions//i,
      /extension://i,
      /^chrome:///i,
      // 依赖的 sdk 
      /posthog\//
      // 其它 ...
    ]
    

主动上报的场景

如果觉得sentry的噪音很多,可以先在核心功能的代码增加手动上报,优先解决手动上报的异常

外部模块异常处理(npm包、浏览器接口等)

外部模块抛出异常不会有source map,通常也难以定位异常是否对用户带来影响。上报方式有

  • 提前检查传入传出参数是否异常
  • 在调用处加try catch
  • 对于React组件则可以在外部加一层Error Boundaries。通过全局Error Boundaries兜底

Redux方法异常

thunk方法的异常容易被吞,如:

extraReducers: builder => {
  builder
    .addCase(fetchData.pending, state => {
      state.status = 'loading';
    })
    .addCase(fetchData.fulfilled, state => {
      state.status = 'succeeded';
    })
    .addCase(fetchData.rejected, (state, action) => {
      // 如果没加这个case, 那么fetchData抛出异常不会被调用者捕获,只是函数返回值为undefined
      // 可在此处上报异常
      console.log('Error:', action.error.message); 
      state.status = 'failed';
    });
},

主动上报的方法

自定义事务

例子:

const transaction = startTransaction('上传文件');
// 业务代码
// 这里的错误都会归为事务“上传文件”
// ...
transaction?.finish();

Sentry默认会以页面为维度作为一个事务.

主动上报异常

用captureException方法

自动上报的level默认为error,主动上报的level可以设为fatal,方便筛选

captureMessage方法没有调用栈和source map信息,不要用。

自动上报的规则

接入sentry后所有未被捕获的异常都会自动上报。

这样会自动上报

function errorFunction1() {
  throw new Error('custom error');
}
function errorFunction2() {
  Promise.reject('custom error');
}

errorFunction1(); 
errorFunction2();

这样不会自动上报

function errorFunction1() {
  try {
    throw new Error('custom error');
  } catch (error) {
    console.error(error);
  }
}
function errorFunction2() {
  Promise.reject('custom error').catch((error) => {
    console.error(error);
  });
}

errorFunction1();
errorFunction2();

结论:理想情况下不应该出现未被捕获的异常,所有异常都应该在代码中捕获处理并手动上报。但现实是不太可能的,很多历史代码或者依赖的库会有一些异常噪音

离线版处理方案

Sentry提供断网的方案,即断网时会将异常对象序列化后存本地或indexDB,联网则自动上报并删除本地异常信息。

对于无法连外网的设备,可以通过钩子获取异常,手动存储在本地日志或indexDB中:

beforeSend: (event) => {
  if(isSelfHost()) {
    saveLogs(JSON.stringify(event))
  }
},

还有一些有意思的功能

问卷 docs.sentry.io/platforms/j…

截屏 docs.sentry.io/platforms/j…

上传异常附件(Attachments),对客户端软件可能有用

Code owner 自动分配issue docs.sentry.io/product/iss…

看看Distributed Tracing能否用在后端接口异常监控: sentry.io/features/di…