线上报错是家常便饭,但如果看到 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 配置实例:
// 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 完全匹配。
// src/main.tsx
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 上传,你们是怎么做版本隔离的?