Sentry Source Maps #
什么是 Source Maps? #
Source Maps 是一种映射文件,将压缩/编译后的代码映射回原始源代码。
text
┌─────────────────────────────────────────────────────────────┐
│ Source Maps 的作用 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 没有 Source Maps: │
│ │
│ Error at main.min.js:1:2345 │
│ at a.min.js:2:1234 │
│ at b.min.js:3:5678 │
│ │
│ → 无法定位问题,代码不可读 │
│ │
│ 有 Source Maps: │
│ │
│ Error at src/components/Button.tsx:45:12 │
│ at handleClick (src/hooks/useClick.ts:23:8) │
│ at onClick (src/App.tsx:102:15) │
│ │
│ → 清晰看到源代码位置,快速定位问题 │
│ │
└─────────────────────────────────────────────────────────────┘
生成 Source Maps #
Webpack 配置 #
javascript
// webpack.config.js
const path = require("path");
module.exports = {
mode: "production",
entry: "./src/index.js",
output: {
path: path.resolve(__dirname, "dist"),
filename: "[name].[contenthash].js",
sourceMapFilename: "[name].[contenthash].js.map",
// Source Map 路径
devtoolModuleFilenameTemplate: (info) => {
return `webpack:///${info.resourcePath}`;
},
},
devtool: "hidden-source-map", // 推荐:生成但不暴露给用户
// 或者使用 source-map
// devtool: "source-map",
};
Vite 配置 #
javascript
// vite.config.js
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
export default defineConfig({
plugins: [react()],
build: {
sourcemap: "hidden", // 或 true
rollupOptions: {
output: {
sourcemap: true,
sourcemapBaseUrl: "https://example.com/sourcemaps/",
},
},
},
});
Next.js 配置 #
javascript
// next.config.js
const { withSentryConfig } = require("@sentry/nextjs");
/** @type {import('next').NextConfig} */
const nextConfig = {
productionBrowserSourceMaps: true, // 启用 Source Maps
};
const sentryWebpackPluginOptions = {
silent: true,
authToken: process.env.SENTRY_AUTH_TOKEN,
org: "my-org",
project: "my-project",
};
module.exports = withSentryConfig(nextConfig, sentryWebpackPluginOptions);
Rollup 配置 #
javascript
// rollup.config.js
import resolve from "@rollup/plugin-node-resolve";
import commonjs from "@rollup/plugin-commonjs";
import terser from "@rollup/plugin-terser";
export default {
input: "src/index.js",
output: {
file: "dist/bundle.js",
format: "iife",
sourcemap: "hidden", // 或 true
},
plugins: [
resolve(),
commonjs(),
terser({
sourcemap: true,
}),
],
};
TypeScript 配置 #
json
// tsconfig.json
{
"compilerOptions": {
"sourceMap": true,
"inlineSources": true,
"sourceRoot": "/"
}
}
上传 Source Maps #
使用 Sentry CLI #
bash
# 安装 Sentry CLI
npm install -g @sentry/cli
# 上传 Source Maps
sentry-cli sourcemaps upload ./dist \
--release my-app@1.0.0 \
--dist ./dist \
--url-prefix "~/static/js/"
# 或使用配置文件
sentry-cli sourcemaps upload --config .sentryclirc ./dist
使用 Webpack 插件 #
javascript
// webpack.config.js
const { sentryWebpackPlugin } = require("@sentry/webpack-plugin");
module.exports = {
// ...其他配置
plugins: [
sentryWebpackPlugin({
authToken: process.env.SENTRY_AUTH_TOKEN,
org: "my-org",
project: "my-project",
release: {
name: "my-app@1.0.0",
create: true,
finalize: true,
},
sourcemaps: {
assets: "./dist/**/*",
ignore: "./node_modules/**/*",
urlPrefix: "~/static/js/",
},
}),
],
};
使用 Vite 插件 #
javascript
// vite.config.js
import { defineConfig } from "vite";
import { sentryVitePlugin } from "@sentry/vite-plugin";
export default defineConfig({
build: {
sourcemap: true,
},
plugins: [
sentryVitePlugin({
authToken: process.env.SENTRY_AUTH_TOKEN,
org: "my-org",
project: "my-project",
release: {
name: "my-app@1.0.0",
},
sourcemaps: {
assets: "./dist/**/*",
ignore: ["./node_modules/**/*"],
urlPrefix: "~/static/js/",
},
}),
],
});
使用 esbuild 插件 #
javascript
// build.js
const esbuild = require("esbuild");
const { sentryEsbuildPlugin } = require("@sentry/esbuild-plugin");
esbuild.build({
entryPoints: ["src/index.js"],
bundle: true,
outdir: "dist",
sourcemap: "hidden",
plugins: [
sentryEsbuildPlugin({
authToken: process.env.SENTRY_AUTH_TOKEN,
org: "my-org",
project: "my-project",
release: {
name: "my-app@1.0.0",
},
}),
],
});
CI/CD 集成 #
yaml
# GitHub Actions
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Upload Source Maps
uses: getsentry/action-release@v1
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_ORG: my-org
SENTRY_PROJECT: my-project
with:
release: my-app@${{ github.sha }}
sourcemaps: ./dist
url_prefix: "~/static/js/"
URL 前缀配置 #
理解 URL 前缀 #
text
┌─────────────────────────────────────────────────────────────┐
│ URL 前缀匹配 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 错误堆栈中的文件路径: │
│ https://example.com/static/js/main.abc123.js │
│ │
│ Source Map 文件中的 sources: │
│ ["webpack:///./src/index.js", "webpack:///./src/App.js"] │
│ │
│ 需要配置 URL 前缀使它们匹配: │
│ │
│ urlPrefix: "~/static/js/" │
│ │
│ 匹配规则: │
│ ~/static/js/main.abc123.js │
│ └── 对应上传的 Source Map 文件 │
│ │
└─────────────────────────────────────────────────────────────┘
常见 URL 前缀配置 #
javascript
// 静态资源在根目录
urlPrefix: "~/"
// 静态资源在 /static/js/ 目录
urlPrefix: "~/static/js/"
// 使用 CDN
urlPrefix: "https://cdn.example.com/static/js/"
// Webpack 默认
urlPrefix: "~/"
验证 URL 前缀 #
bash
# 使用 Sentry CLI 验证
sentry-cli sourcemaps explain \
--org my-org \
--project my-project \
"https://example.com/static/js/main.abc123.js"
Source Map 类型 #
不同类型的 Source Map #
| 类型 | 描述 | 推荐场景 |
|---|---|---|
source-map |
生成独立的 .map 文件 | 生产环境 |
hidden-source-map |
生成但不引用 | 生产环境(推荐) |
inline-source-map |
内联到文件中 | 开发环境 |
eval-source-map |
使用 eval | 开发环境 |
nosources-source-map |
不包含源代码 | 保护源码 |
推荐配置 #
javascript
// 生产环境
devtool: "hidden-source-map"
// 开发环境
devtool: "eval-source-map"
// 需要保护源码
devtool: "nosources-source-map"
验证 Source Maps #
检查上传状态 #
bash
# 列出 Release 的 Source Maps
sentry-cli releases files my-app@1.0.0 list
# 检查特定文件
sentry-cli releases files my-app@1.0.0 get "~/static/js/main.abc123.js.map"
在 Sentry 控制台验证 #
- 进入 Settings → Projects → 选择项目 → Source Maps
- 查看 Source Map 状态
- 检查是否有错误或警告
手动验证 #
bash
# 下载 Source Map 文件
curl -O https://example.com/static/js/main.abc123.js.map
# 检查 Source Map 内容
cat main.abc123.js.map | jq '.sources'
# 验证 Source Map 有效性
npx source-map-visualization main.abc123.js.map
故障排除 #
常见问题 #
text
┌─────────────────────────────────────────────────────────────┐
│ 常见 Source Map 问题 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. Source Map 未找到 │
│ 原因:URL 前缀配置错误 │
│ 解决:检查 urlPrefix 配置 │
│ │
│ 2. Release 不匹配 │
│ 原因:上传和运行时的 Release 不一致 │
│ 解决:确保 Release 名称一致 │
│ │
│ 3. 文件路径不匹配 │
│ 原因:构建配置问题 │
│ 解决:检查 devtoolModuleFilenameTemplate │
│ │
│ 4. Source Map 格式错误 │
│ 原因:构建工具配置问题 │
│ 解决:检查 sourcemap 配置 │
│ │
└─────────────────────────────────────────────────────────────┘
调试技巧 #
javascript
// 在 Sentry 初始化时启用调试
Sentry.init({
dsn: "https://xxxxxxxx@o123456.ingest.sentry.io/1234567",
release: "my-app@1.0.0",
// 启用调试
debug: true,
// 记录 Source Map 解析
_experiments: {
debugSymbols: true,
},
});
检查清单 #
markdown
## Source Map 检查清单
- [ ] 构建时生成了 Source Map 文件
- [ ] Source Map 文件已上传到 Sentry
- [ ] Release 名称一致
- [ ] URL 前缀配置正确
- [ ] 文件路径匹配
- [ ] Source Map 格式有效
- [ ] 没有缓存问题
多环境配置 #
不同环境的 Source Maps #
javascript
// webpack.config.js
const isProduction = process.env.NODE_ENV === "production";
module.exports = {
mode: isProduction ? "production" : "development",
devtool: isProduction ? "hidden-source-map" : "eval-source-map",
plugins: [
isProduction && sentryWebpackPlugin({
authToken: process.env.SENTRY_AUTH_TOKEN,
org: "my-org",
project: "my-project",
release: {
name: `my-app@${process.env.APP_VERSION}`,
},
}),
].filter(Boolean),
};
多 CDN 配置 #
javascript
// 不同 CDN 使用不同 URL 前缀
const cdnUrl = process.env.CDN_URL || "https://cdn.example.com";
sentry-cli sourcemaps upload ./dist \
--release my-app@1.0.0 \
--url-prefix "${cdnUrl}/static/js/"
Source Maps 最佳实践 #
1. 使用 hidden-source-map #
javascript
// ✅ 推荐
devtool: "hidden-source-map"
// ❌ 不推荐(会暴露给用户)
devtool: "source-map"
2. 自动化上传 #
yaml
# 在 CI/CD 中自动上传
- name: Upload Source Maps
run: |
npm run build
sentry-cli sourcemaps upload ./dist \
--release my-app@${{ github.sha }} \
--url-prefix "~/static/js/"
3. 版本关联 #
javascript
// 确保 Release 名称一致
const release = `my-app@${process.env.APP_VERSION}`;
// 构建配置
Sentry.init({ release });
// 上传配置
sentry-cli releases files ${release} upload-sourcemaps ./dist
4. 保护源码 #
javascript
// 使用 nosources-source-map 保护源码
devtool: "nosources-source-map"
// 这样只会显示文件名和行号,不会显示源代码
5. 清理旧文件 #
bash
# 删除旧版本的 Source Maps
sentry-cli releases files my-app@0.9.0 delete --all
下一步 #
现在你已经掌握了 Source Maps 的知识,接下来学习 隐私与安全 了解如何保护用户隐私!
最后更新:2026-03-29