线上报错是家常便饭,但如果看到 Sentry 里的错误栈全是类似 at a (main.min.js:1:3482) 这种被混淆压缩过的代码,除了抓狂你什么也做不了。没有 Source Map,排查线上 Bug 就跟开盲盒一样痛苦。但是,如果把 Source Map 直接发到线上,又无异于把源码公开给所有人,存在严重的安全风险。最完美的方案是在打包编译时把 Source Map 生成出来,在 CI 流水线里悄悄上传到 Sentry 服务器,然后再把本地生成的 .map 文件删掉。这样既保证了线上代码的安全,又能让 Sentry 在后台自动解析出清晰的源码堆栈。
痛点:为什么你的 Sentry 匹配不到 Source Map?
很多团队在配 Sentry 的时候经常踩这个坑:明配置了上传,但报错页面上依然是混淆代码。这通常是因为以下三个条件没有同时满足:
- Release 版本号必须完全一致:前端 SDK 初始化时声明的
release字段值,必须跟打包上传 Source Map 时指定的 release 版本号一模一样(比如用同一个 Git Commit Hash)。 - URL 路径必须匹配:Sentry 还原堆栈时,会根据报错的 JS 文件 URL(如
https://example.com/assets/index.js)去匹配它数据库里的 Source Map。如果路径前缀不对,就找不到对应的 map 文件。 - JS 资源包含 SourceMappingURL:混淆后的 JS 文件末尾必须有一行类似
//# sourceMappingURL=index.js.map的注释,Sentry 才能知道要去关联哪一个 map。
实战:在 Vite 中通过 @sentry/vite-plugin 自动上传
现在我们的前端项目基本都迁移到 Vite 了。Sentry 官方提供了非常好用的 Vite 插件,能在构建打包时一键搞定生成、上传和自动清除。
下面是我们的 vite.config.ts 配置实例:
import { defineConfig } from 'vite';import react from '@vitejs/plugin-react';import { sentryVitePlugin } from '@sentry/vite-plugin';
// 注入 Git Release 版本号,确保打包和运行时版本号一致const RELEASE_VERSION = process.env.SENTRY_RELEASE || `my-app@${process.env.GIT_SHA || 'dev'}`;
export default defineConfig({ plugins: [ react(), sentryVitePlugin({ org: 'company-tech', project: 'frontend-portal', // Sentry CLI 认证 Token,建议从环境变量注入而非写死 authToken: process.env.SENTRY_AUTH_TOKEN, // 告诉插件打包结束后把本地的 .map 文件删掉,防止泄漏 sourcemaps: { assets: './dist/**', filesToDeleteAfterUpload: ['./dist/**/*.map'], }, // 关联的 Release 版本号 release: { name: RELEASE_VERSION, // 是否将当前的 commits 信息也关联到该 release 下 setCommits: { auto: true, } }, }), ], build: { // 必须开启 sourcemap 生成,否则插件无源可用 sourcemap: true, },});运行时 SDK 的同步配置
在前端入口文件(如 main.tsx)里初始化 Sentry 时,一定要确保传入的 release 和上面 Vite 插件里配置的 RELEASE_VERSION 完全匹配。
import React from 'react';import ReactDOM from 'react-dom/client';import * as Sentry from '@sentry/react';import App from './App';
// 在打包时由 Vite 插件 DefinePlugin 注入,或者直接读取 window 配置const SENTRY_RELEASE = import.meta.env.VITE_SENTRY_RELEASE || 'my-app@dev';
Sentry.init({ dsn: 'https://your-dsn-key@o123456.ingest.sentry.io/4507000', integrations: [ Sentry.browserTracingIntegration(), Sentry.replayIntegration(), ], tracesSampleRate: 0.1, replaysSessionSampleRate: 0.1, replaysOnErrorSampleRate: 1.0, release: SENTRY_RELEASE, environment: import.meta.env.MODE, // production / staging});
ReactDOM.createRoot(document.getElementById('root')!).render( <React.StrictMode> <App /> </React.StrictMode>);在 CI/CD 中注入环境变量
最后,在你的 GitHub Actions 或者 Jenkins 编译流水线中,只需要在执行 npm run build 前注入 SENTRY_AUTH_TOKEN 和当前构建的 GIT_SHA:
# 示例构建脚本export GIT_SHA=$(git rev-parse --short HEAD)export SENTRY_RELEASE="my-app@${GIT_SHA}"export VITE_SENTRY_RELEASE="${SENTRY_RELEASE}"export SENTRY_AUTH_TOKEN="sntryu_..." # 从 CI 密钥管理中注入
npm run build这样,一旦代码打包完毕,Vite 插件就会自动把生成的 .map 文件上传至 Sentry,接着把 dist 目录下所有的 .map 文件彻底删除。线上运行的 JS 虽有 sourceMappingURL 注释,但访问该 URL 会返回 404,完全不用担心源码泄露。
大家在配置 Sentry 自动上传 Source Map 时有没有踩过版本不匹配的坑?另外,对于多 SPA 微前端架构下的 Source Map 上传,你们是怎么做版本隔离的?