前端知库

错误监控平台怎样接入?以 Sentry 为例讲 SDK 初始化、Source Map 上传、release 关联和采样

接入 Sentry 分四步:在应用最早执行的地方调用 Sentry.init,配置 dsn、release、environment;构建时用 Sentry 的打包插件生成并上传 Source Map,上传后删掉 map 文件;让 SDK 上报的 release 和上传 Source Map 时的 release 一致,Sentry 才能按版本统计错误、定位引入问题的提交;错误用 sampleRate、性能追踪用 tracesSampleRate 或 tracesSampler 采样,另外用 beforeSend 去掉敏感信息。

参考答案

简答

前端接一个错误监控平台,以 Sentry 为例,要做四件事:初始化 SDK、上传 Source Map、关联版本、控制采样。

初始化就是在应用最早执行的地方调用 Sentry.init,至少配 dsn,它告诉 SDK 把事件发到哪个项目。再配上 release 和 environment,标明是哪个版本、哪个环境报的错。SDK 默认启用的 globalHandlersIntegration 会捕获未处理的错误和 Promise 拒绝,不用自己写 window.onerror。

线上代码是压缩过的,报错堆栈看不懂,所以构建时要用 Sentry 的打包插件把 Source Map 上传上去,上传完把 .map 文件删掉,不对外公开。插件默认会往产物里注入 Debug ID,用来把压缩文件和对应的 map 对上;同时会创建 release,并把版本号注入到产物里。有了 release,Sentry 才能告诉你某个错误是哪个版本开始出现的、可能是哪次提交引入的。

采样分两类。错误用 sampleRate,默认是 1,也就是全部上报;性能追踪用 tracesSampleRate,或者用 tracesSampler 按页面、接口设置不同比例。官方提醒,降低错误采样率会让你看不到部分错误,量太大时可以优先考虑项目的限流。另外要用 beforeSend 去掉用户隐私,dsn 本身可以公开,它只能用来提交事件,不能读数据。

初始化 SDK

浏览器项目安装 @sentry/browser,React、Vue 等框架有各自的包,初始化方式相同。下面的配置参考 Sentry 文档的快速开始:

JavaScript
import * as Sentry from '@sentry/browser';

Sentry.init({
  dsn: 'https://<public-key>@<host>/<project-id>',
  release: 'my-project-name@2.3.12',
  environment: 'production',
  integrations: [Sentry.browserTracingIntegration(), Sentry.replayIntegration()],
  sampleRate: 1.0,
  tracesSampleRate: 0.1,
  replaysSessionSampleRate: 0.1,
  replaysOnErrorSampleRate: 1.0,
  beforeSend(event) {
    if (event.request?.cookies) delete event.request.cookies;
    return event;
  },
});

dsn 写在前端代码里,会不会被别人看到、拿去乱用?Sentry 的文档说明 DSN 可以公开,它只允许提交新事件,没有读取任何数据的权限。被人滥发事件的风险确实存在,文档说 Sentry 提供了屏蔽 IP 之类的控制,也可以在项目的 Client Keys 设置里轮换或吊销 DSN。

release 一般不手写,下一节的打包插件会自动注入。文档还说明,浏览器里的 SDK 会尝试从 window.SENTRY_RELEASE.id 读取版本号。environment 区分生产、预发和开发环境,在 Sentry 里可以按环境筛选错误。

integrations 里的 browserTracingIntegration 开启性能追踪,replayIntegration 开启会话回放,不需要可以不加。beforeSend 在每个错误事件发送前调用,可以修改事件,返回 null 就丢弃这个事件,文档举的用途就是发送前手动去掉个人信息。

SDK 默认启用 globalHandlersIntegration 捕获全局的未处理错误和 Promise 拒绝,原理和自己写全局监听一样,见《前端怎样全局捕获 JS 错误、Promise 拒绝和资源加载失败?》。

上传 Source Map

以 Vite 为例,Sentry 提供 sentryVitePlugin。下面的配置来自 Sentry 的 Vite 文档:

JavaScript
import { defineConfig } from 'vite';
import { sentryVitePlugin } from '@sentry/bundler-plugins/vite';

export default defineConfig({
  build: {
    sourcemap: 'hidden', // 必须开启 Source Map 生成
  },
  plugins: [
    // Sentry 插件放在所有插件之后
    sentryVitePlugin({
      org: '<org-slug>',
      project: '<project-slug>',
      authToken: process.env.SENTRY_AUTH_TOKEN,
      sourcemaps: {
        filesToDeleteAfterUpload: ['./**/*.map'],
      },
    }),
  ],
});

sourcemap: 'hidden' 会生成 map 文件,但不在 JS 末尾写 sourceMappingURL 注释,浏览器不会去请求它。filesToDeleteAfterUpload 在上传后删掉 map 文件,所以部署出去的目录里没有它们。文档明确提醒,生成的 Source Map 如果被公开,源码可能泄漏,要么让服务器拒绝访问 .js.map,要么用这个选项删掉。

authToken 有项目的写权限,属于敏感信息,文档建议放在 CI 的环境变量里,或者放在 .env.sentry-build-plugin 文件里并加进 .gitignore。插件在开发模式和 watch 模式下不会上传,所以要用生产构建来验证配置。

上传以后,Sentry 怎么知道哪个 map 对应哪个 JS 文件?文档说默认做法是往构建产物里注入 Debug ID,压缩文件和 map 文件带同一个 ID,就能对上,不依赖文件名和路径。为什么 map 不能公开、Source Map 是怎样把位置还原回来的,见《线上报错怎样用 Source Map 还原,又不把 map 文件公开?》。

release 怎样关联

release 是代码的一个版本。打包插件的 release 选项默认都是开启的:release.create 在构建时在 Sentry 上创建 release,release.inject 把版本信息注入产物,让 SDK 上报时带上;release.name 不填时,插件会从 CI 环境里自动识别,识别不到就用 git 当前提交的 SHA。release.setCommits 默认 { auto: true },把 release 和对应的提交关联起来。

关联上以后能做什么?Sentry 的 release 文档列了这几件事:看出新版本引入了哪些错误和回归;推测是哪次提交导致的问题、谁可能负责;在提交信息里写上问题编号,发版后自动把问题标为已解决。还有 release 健康度:SDK 默认会上报会话,Sentry 据此算出无崩溃会话的比例,看新版本是不是变差了。

关键是一致:SDK 上报事件时带的 release,和上传 Source Map、创建 release 时用的名字要相同。所以最省事的做法是让插件统一生成和注入,不要在 Sentry.init 里另写一个值。

采样怎样设置

错误事件用 sampleRate,范围 0 到 1,默认 1.0,表示全部上报;设成 0.1 就随机只发 10%。文档同时提醒,改错误采样率要重新部署,而且会让你看不到一部分错误的来源,错误量大时,在项目上设置限流可能更合适,因为限流只在量高时丢事件。

性能追踪用 tracesSampleRate,表示每条 trace 被发送的概率;要么配它,要么配 tracesSampler,两个都不配就不开启追踪。tracesSampler 是一个函数,可以按名称返回不同的比例,两个都配了,以它为准:

JavaScript
Sentry.init({
  tracesSampler: ({ name, inheritOrSampleWith }) => {
    if (name.includes('healthcheck')) return 0; // 健康检查不采样
    if (name.includes('auth')) return 1; // 登录相关全采
    return inheritOrSampleWith(0.5); // 其他沿用上游的采样决定,没有时按 0.5
  },
});

inheritOrSampleWith 的意思是:如果这条 trace 是从上游服务延续过来的,就沿用上游的采样决定,否则按给定的比例采样。这样前后端同一条链路要么都采、要么都不采,不会只采到一半。

会话回放有两个单独的采样率:replaysSessionSampleRate 是从会话开始就录制的比例,replaysOnErrorSampleRate 是发生错误时录制的比例。快速开始的示例里前者是 0.1,后者是 1.0。

采样之外,监控数据怎样去重、聚合和告警,见《前端监控数据怎样做采样、去重、聚合和告警?》。

常见追问

怎样过滤掉没用的错误,比如浏览器插件报的错?

用 ignoreErrors 配错误信息的字符串或正则,匹配到的不上报;用 allowUrls 只上报来自自己域名脚本的错误。更细的判断写在 beforeSend 里,返回 null 就丢弃。

Source Map 上传了,堆栈还是没还原,怎么排查?

先确认部署出去的代码里有 Debug ID,Sentry 文档的排查建议是重新运行 npx @sentry/wizard@latest -i sourcemaps 并确认用的是生产构建。再确认上传发生在错误出现之前,也就是先上传再部署。

dsn 可以公开,authToken 呢?

不能公开。authToken 有上传 Source Map、创建 release 的权限,只能放在 CI 的密钥或本地不提交的环境文件里,而且只传给需要它的构建步骤。