Sentry 监控接入
前言
用户反馈页面报错时,往往只记得“刚才点了一下就坏了”。本机复现不出来,服务器日志又没有前端现场,就很难知道问题发生在哪一版、哪个页面、哪一步操作。
Sentry 可以把异常、版本、调用栈和操作线索放到一起。接入时除了让错误进后台,还要确认源码能还原、用户不会串号、敏感数据没有跟着上传,以及独立页面没有遗漏。
监控范围
浏览器页面 → 浏览器 SDK → 前端项目
服务端请求 → 服务端 SDK → 后端项目
构建流程 → source map 上传 → 还原对应产物的源码栈前后端可以分成两个 Sentry 项目,便于分别管理错误、告警和额度;并不是技术上必须分开。DSN 指向哪个项目,事件就进入哪个项目,不要仅凭名字相似判断配置。
按实际框架选择 SDK,例如 React、Vue 或对应的 Node 服务端集成。初始化位置、错误边界和中间件顺序遵循该版本的集成方式,不把某个框架的文件原封不动复制过去。
先接异常监控,再按需要增加性能采样。Replay、日志和 Profiling 分别评估,不必一次全部打开。
配置与注入时机
| 配置项 | 来源与约束 |
|---|---|
| 浏览器和服务端 DSN | 从对应 Sentry 项目的 Client Keys 获取,明确事件归属 |
| 组织、项目标识 | 从项目设置确认,上传 source map 的目标与事件项目对应 |
| 上传 Token | 使用满足上传需求的最小权限凭据,仅注入构建环境,不进入前端变量或产物 |
| release、environment | 从发布流程生成与注入,能追溯实际构建,不写死成上一版 |
| SDK 与框架入口 | 查当前依赖版本、前端启动和服务端 instrumentation 入口 |
| trace 传播范围 | 按自有 API 的完整 origin 和路径配置,不匹配任意子域或第三方服务 |
| source map 目录 | 从实际构建输出确定上传和删除范围,不使用过宽的工作区 glob |
| 独立页面与 CSP | 盘点未加载主入口的页面,确认 SDK 脚本和事件上报地址允许访问 |
DSN 会出现在浏览器代码里,不等于上传 Token,也不授予读取事件的管理权限;但仍可能被滥用来写入垃圾事件,需要配合接收限制和额度管理。
Vite 客户端变量通常在构建时写入产物,后端环境变量则由运行进程读取。只修改服务器环境,不会自动改变已经构建的浏览器 DSN。
同批发布的前后端可以共用 release;独立发布时分别标记真实版本,并记录对应关系。source map 必须匹配实际运行的产物,不能靠相同字符串弥补构建不一致。
初始化与采样
浏览器 SDK 尽早接入启动流程,同时避免监控下载失败阻断应用。懒加载可以减轻首屏负担,但会留下初始化之前的监控窗口;“没有 DSN 就完全不打包 SDK”也需要检查构建产物,不能只凭一个判断语句保证。
服务端按框架要求在被监控模块加载前初始化。优先使用适配的官方集成;只有明确了解缺口时才手动补请求 span,不默认关闭全部自动 instrumentation。
以 tracesSampleRate: 0.05 为例,它控制性能 trace 采样,不是“只收 5% 的异常”。异常采样、过滤和额度分别检查。健康探测、静态资源等低价值流量可按路由规则过滤,路由名称用 /items/:id 这样的模板,避免高基数字段。
浏览器 trace 目标可以按下面的形状配置,域名和路径需要换成真实 API:
tracePropagationTargets: [
/^\/api(?:\/|\?|$)/,
/^https:\/\/api\.example\.com\/api(?:\/|\?|$)/,
]检查实际请求是否带上 sentry-trace、baggage,第三方请求是否没有携带。跨域时还需后端正确处理相关 CORS 头;不要为排障直接放宽到所有域名。选项语义见 Sentry SDK 配置。
用户关联与隐私清洗
如果业务确实需要按账号排障,使用登录系统已验证的稳定账号 ID;匿名事件保持匿名,不拿 IP、Cookie 或随机会话号冒充用户。
// 在登录状态确认后调用;user 来自应用已验证的账号信息
function syncMonitoringUser(user) {
Sentry.setUser(user ? { id: String(user.id) } : null)
}
// 登录、账号切换和会话刷新成功后同步
syncMonitoringUser(authenticatedUser)
// 退出登录或会话失效时清除
syncMonitoringUser(null)账号 ID 也属于用户相关数据,是否采集要符合产品隐私约定。昵称只有确实需要时才保留,不附带完整用户对象、邮箱、手机号或凭据。
服务端从可信会话取身份,在每个请求的隔离 scope 中设置;不要相信客户端传来的 X-User-Id,也不要把用户写入所有请求共享的全局状态。已有官方中间件提供隔离时直接使用;手动集成必须验证并发请求不会串号,参见 异步上下文隔离。
关闭默认 PII 采集不等于完成脱敏。旧配置常使用 sendDefaultPii: false;当前文档已提供 dataCollection 并标记前者弃用,升级时按安装版本逐项核对,不机械替换成默认更宽松的配置。
在事件、性能数据和面包屑出站前做白名单清洗:
- URL 去掉敏感查询参数,路径中的账号或业务标识也要处理。
- 请求头排除 Cookie、Authorization 等凭据,请求体默认不采集。
extra、context、日志消息和 breadcrumb 不直接塞原始响应对象。- 用户仅保留约定字段;设备信息按排障需要和隐私策略保留。
错误事件的 beforeSend 不覆盖全部数据类型。性能事件、面包屑、Replay 或独立日志产品,需要各自的采集和清洗配置。最后检查实际事件载荷,而不是只看配置文件。
调用栈还原源码
source map 将压缩后的栈映射回源文件。以 Vite 为例,把上传插件合并到现有构建配置,放在其他插件之后:
import { defineConfig } from 'vite'
import { sentryVitePlugin } from '@sentry/vite-plugin'
// 此例用于要求上传映射的构建;dist 为示例输出目录
for (const key of ['SENTRY_ORG', 'SENTRY_PROJECT', 'SENTRY_AUTH_TOKEN']) {
if (!process.env[key]) throw new Error(`缺少构建配置:${key}`)
}
export default defineConfig({
build: { sourcemap: 'hidden' },
plugins: [
// 原有插件……
sentryVitePlugin({
org: process.env.SENTRY_ORG,
project: process.env.SENTRY_PROJECT,
authToken: process.env.SENTRY_AUTH_TOKEN,
sourcemaps: {
filesToDeleteAfterUpload: ['./dist/**/*.map'],
},
}),
],
})合并时保留项目原有插件和构建选项。Token 由 CI 秘密存储或仓库外的受控文件注入,缺少上传凭据时按发布策略明确失败或关闭 map 生成,不留下未检查的公开映射文件。
hidden 只隐藏 JS 中的映射注释,不会阻止下载 .map。确认上传成功、映射与产物匹配,再检查最终公开目录没有不应发布的 map;上传后不要再次修改 bundle,破坏对应关系。官方示例见 Vite source map 上传。
验收用真实业务文件中的受控错误,查看原始文件名和行号。控制台临时抛错只能检查上报链路,不能证明业务代码的映射正确。对公开 map URL 应返回 404/403,而不是下载到内容;仅测试一个猜测文件名也不够,还要检查发布目录。
独立页面
分享页、打印页、回调页、iframe 和独立 Worker 可能根本没有加载主应用的 SDK。逐个确认它们的执行入口,而不是认为主页面装了监控就全部覆盖。
独立浏览器页面可以使用单独的监控入口,并通过 surface 标签区分。是否共用前端项目按归属决定;Worker 则使用其环境支持的方案,不能直接照搬依赖 DOM 的浏览器初始化。
构建时确保入口产物不会被后续清理覆盖。配置如果由服务端注入 HTML,只包含允许公开的 DSN、版本等字段,使用正确转义,绝不注入上传 Token 或登录凭据。
严格 CSP 下只放行实际需要的脚本和 ingest 地址,不直接增加宽泛的 https:。监控失败不应让业务白屏,但也不能静默到永远没人发现;保留加载失败记录或外部检查。
用少量线索补齐现场
面包屑回答“出错前做了什么”,不需要每个动作单独开一个 issue:
Sentry.addBreadcrumb({
category: 'ui',
message: 'save.started',
data: { screen: 'editor', source: 'button' },
})记录动作名、阶段和结果,不上传原始搜索词、正文或完整 URL。已经被框架处理掉的异常,需要在合适的错误边界主动上报;自动捕获和手动 capture 要防止重复。
已有客户端日志回传也可以保留,但分清职责:普通过程日志用于诊断,值得处理的异常才进入错误追踪。服务端转发到 Sentry 的应只是带诊断数据的失败记录;“页面已加载”“播放器已挂载”这类过程日志原样转发,会变成事件数持续增长却没有任何错误信息的 issue,淹没真正的问题。为同一故障生成关联 ID,浏览器和服务端按约定去重;“已调用 capture”不代表远端已成功收到。
公开日志入口限制频率、载荷和字段,不接受任意对象直接转发到 Sentry。浏览器事件也不要简单改由服务端上传,再误以为 Node 上下文就是出错设备的现场。
没抛异常的问题
播放进度倒退、任务停滞或 Worker 内部状态异常,不一定产生可捕获异常。可以补充:
| 手段 | 用法 |
|---|---|
| 有界状态缓冲 | 保留最近一小段关键状态,异常时一起上报,不长期收集全部数据 |
| 状态哨兵 | 检测违反业务预期的变化,排除主动操作后再报告 |
| 受控探测 | 仅对允许的只读目标检查状态和耗时,设置超时、次数与冷却时间 |
fetch(..., { mode: 'no-cors' }) 的 opaque 响应不能证明服务返回成功或内容正确,不要拿 resolve 当健康检查通过。
Replay 是否值得启用,要看问题类型、环境支持、隐私和成本。它能补充部分交互现场,但不能代替业务状态采集,也不能用“非 DOM 问题一律无用”概括所有能力。
接入验收
| 检查项 | 预期结果 |
|---|---|
| 浏览器与服务端错误 | 各进入正确项目,版本和环境准确,避免重复事件 |
| source map | 受控业务错误还原到对应源码位置,公开产物不泄露映射文件 |
| 用户生命周期 | 登录后 ID 正确,退出后不残留,切换账号和并发请求不串号 |
| 匿名事件 | 没有被人为补成真实账号,历史匿名事件不假定会自动补身份 |
| 数据清洗 | 查询参数、凭据、请求体和自定义字段符合白名单 |
| trace | 自有 API 按预期关联,不向第三方扩散追踪头 |
| 独立入口 | 每种页面或执行环境都单独触发并检查,不只测 SPA |
| 监控失效 | SDK 被阻止或 DSN 缺失时,业务仍可运行,有其他发现问题的途径 |
先在测试环境做受控错误,移除测试入口后再发布。事件是否出现、栈是否正确、敏感字段是否缺席都要实际查看,不能把 SDK 初始化成功当作验收完成。
浏览器入口根本没执行、上报被拦截或网络完全断开时,Sentry 也可能没有记录。保留服务器日志、外部拨测和告警,才能知道“没有事件”究竟是没有错误,还是监控没工作。

暂无评论