Skip to content

错误追踪与 Source Map:去重、还原与 Release

基于 Sentry 8.x · 核于 2026-08

速查

  • 错误采集流程:应用抛异常 → SDK 捕获(全局未处理异常 hook)→ 收集 stack trace + 环境 + 用户 + 请求上下文 → 上报 Sentry → fingerprint 去重归并 issue。
  • Event vs IssueEvent 是单次错误(每次抛出一条);Issue 是同源错误的归并组(按 fingerprint)。一个 issue 下挂多个 event,issue 页显示聚合统计(次数/用户数/趋势)。
  • fingerprint 指纹:默认基于 stack trace 关键帧(错误类型 + 出错文件 + 函数 + 行号)算指纹,栈相同的归为同一 issue——避免同源错误刷屏。
  • 自定义 fingerprintSentry.withScope(scope => scope.setFingerprint([...])),用于动态参数导致默认分组不准的场景(如「订单 ID 不同但本质同源」按错误类型分组)。
  • Source Map 上传:CI/CD 构建后用 sentry-cli sourcemaps upload --release v1.2.0 dist/ 上传,Sentry 据此还原压缩栈到源码行。
  • Source Map 安全:Source Map 不要部署到生产 CDN(暴露源码),只上传 Sentry 内部。
  • Release tracking:SDK 初始化传 release: 'v1.2.0',上传 Source Map 带 release → Sentry 标记错误首次出现的版本 + 关联 commit + 检测回归。
  • 回归检测(regression):某错误在 release A 标记 resolved,在 release B 又出现 → 自动标记 regression,提醒修复失效。
  • issue 状态机:unresolved(待修)→ assigned(已分配)→ resolved(已修)/ ignored(忽略)。resolved 后又出现触发 regression。

一、错误采集:从异常到 Event

Sentry SDK 通过 hook 全局未处理异常采集错误:

前端(浏览器)

js
// React 应用(@sentry/react)
import * as Sentry from "@sentry/react";

Sentry.init({
  dsn: "https://xxx@sentry.io/123",      // 项目唯一标识
  release: "myapp@1.2.0",                 // 版本(Release tracking)
  environment: "production",
  tracesSampleRate: 0.2,                  // 性能采样率(20%)
  replaysSessionSampleRate: 0.1,          // Session Replay 采样
});

// SDK 自动 hook:
//   - window.onerror(同步错误)
//   - window.onunhandledrejection(Promise 未捕获 reject)
//   - fetch/XHR(请求错误)
//   - console.error(可选)

后端(Python/Node)

python
# Python(sentry-sdk)
import sentry_sdk
sentry_sdk.init(
    dsn="https://xxx@sentry.io/123",
    release="myapi@1.2.0",
    traces_sample_rate=0.2,
)
# SDK 自动 hook:
#   - sys.excepthook(未捕获异常)
#   - logging(ERROR 级日志)
#   - Django/Flask/FastAPI 框架中间件
#   - DB 查询(SQLAlchemy/Django ORM)
  • 采集的上下文:stack trace(核心)+ environment(生产/测试)+ release + 用户信息(user_id/email/IP)+ 请求信息(URL/method/headers)+ 自定义 tags/extra。
  • 采样:高流量场景 SDK 配置采样率(如 tracesSampleRate: 0.2 只上报 20% 性能 trace),错误事件默认全采(不采样,因为错误稀有且重要)。

二、fingerprint 去重:同源错误归一

Sentry 的核心智能是把同源错误归为一组(issue),靠 fingerprint:

默认指纹算法

错误事件到达 → Sentry 取 stack trace
→ 提取关键帧(错误类型 + 出错文件 + 函数名 + 行号)
→ 算 fingerprint(哈希)
→ 指纹相同的归为同一 issue
→ 新指纹创建新 issue(标「新错误」)
  • 默认分组合理:同一处代码抛同一类型异常,栈相同 → 同一 issue。不同代码路径抛同类异常 → 不同 issue(即使错误类型相同)。
  • issue 聚合:issue 页显示首次/末次出现时间、总 event 数、影响用户数、stack trace、趋势图——一眼看错误严重度与趋势。

自定义 fingerprint

某些场景默认分组不准:

js
// 例:订单查询失败,错误信息含订单 ID,默认按消息分组会让每个订单一个 issue
try {
  await getOrder(orderId);
} catch (e) {
  Sentry.withScope(scope => {
    // 强制按错误类型分组,忽略消息中的动态订单 ID
    scope.setFingerprint(["{{ default }}", "order-fetch-error"]);
    Sentry.captureException(e);
  });
}
  • 何时自定义:动态参数(ID/时间戳)导致同源错误被拆成多 issue;或反过来想把不同栈的错误合并。
  • {{ default }}:占位符,保留默认指纹 + 追加自定义维度。

三、Source Map:还原压缩栈到源码

生产前端代码压缩后,报错栈无法定位:

压缩栈(用户报错):
  at handleLogin (https://cdn.com/app.min.js:1:23456)
  at HTMLButtonElement.onclick (https://cdn.com/app.min.js:1:45678)

问题:app.min.js 是几万行压缩成一行的,1:23456 无法定位源码

Source Map 解决

构建产物:
  dist/assets/app-[hash].js         (压缩混淆的生产代码)
  dist/assets/app-[hash].js.map     (Source Map,记录映射)

流程:
  1. CI 构建后上传 Source Map 到 Sentry
     sentry-cli sourcemaps upload --release myapp@1.2.0 dist/assets/
  2. 用户报错栈:app-[hash].js:1:23456
  3. Sentry 查该 release 的 Source Map:1:23456 → src/components/Login.tsx:42
  4. 展示还原栈:
     at handleLogin (src/components/Login.tsx:42)
     at onSubmit (src/components/Login.tsx:28)

上传命令(sentry-cli)

bash
# 安装
npm i -g @sentry/cli

# 配置(CI 环境变量)
export SENTRY_AUTH_TOKEN=xxx
export SENTRY_ORG=my-org
export SENTRY_PROJECT=my-frontend

# 上传(构建后)
sentry-cli sourcemaps upload \
  --release myapp@1.2.0 \           # 与 SDK init 的 release 一致
  --dist 1 \                         # 可选,区分部署目标
  dist/assets/

# 或用 sentry-webpack-plugin / @sentry/vite-plugin 自动化

安全注意事项

  • 不要部署 Source Map 到生产 CDN:Source Map 含完整源码映射,公开访问等于泄露源码。只上传到 Sentry,构建产物(dist)只发压缩 JS,不发 .map。
  • Webpack hidden-source-map:生成 Source Map 但不在 JS 末尾引用(浏览器不加载,仅 Sentry 用)。
  • 后端 Debug Symbol:Python/Java/Go 的二进制栈要靠符号表(symbol)还原,类似 Source Map。

四、Release tracking:版本关联

Sentry 把 release 作为一等概念,关联错误与版本:

js
Sentry.init({
  release: "myapp@" + process.env.npm_package_version,  // v1.2.0
});
  • 标记首次出现:每个错误标「首次出现在 release v1.2.0」,结合该 release 的 commit 范围,知道是哪个 commit 引入的。
  • 关联 commit:用 sentry-cli releases set-commits 把 release 与 git commit 范围关联,issue 页直接显示「可能引入此错误的 commit」。
  • 部署追踪sentry-cli releases deploys new 记录部署(环境/时间),看版本在哪些环境部署。
  • 采用率(adoption):SDK 上报时带 release,Sentry 统计每个 release 覆盖的用户比例(版本发布后扩散速度)。

五、回归检测(regression)

Sentry 自动检测「修复后又重现」的错误:

release v1.0.0:错误 A 出现 → issue 创建
release v1.1.0:开发者修复错误 A → issue 标 resolved
release v1.2.0:错误 A 又出现 → Sentry 自动标 regression(回归)
  → 告警「修复失效」,提醒重新审视
  • 价值:避免「修了又坏」的隐性回归被忽视。传统日志系统做不到(没有 issue 状态)。
  • 依赖 Release tracking:必须有准确的 release 标记,Sentry 才能算「在哪个版本又出现」。

六、issue 状态机

每个 issue 有状态,像工单系统:

状态含义
unresolved待处理(默认)
assigned已分配给某人
resolved已修复(手动或 merge PR 自动)
ignored忽略(已知问题不修,如第三方 bug)
regressionresolved 后又出现(自动标记)
  • 自动 resolved:上传新 release 时,可配置「标记该 release 之前的所有 issue 为 resolved」(假设新版本修复了)。
  • Suspect Commits:issue 页显示「可能引入此错误的 commit」(基于 Release 关联),加速定位。

下一步

掌握了错误追踪与 Source Map 后,下一步看性能监控与 Session Replay——Performance trace 慢请求、Replay 录制回放、前后端全栈集成与 APM 边界。