Sentry 错误追踪、Release 与 source map 工具手册
一条错误已经上报,为什么仍然无法修复
发布后的支付页开始偶发白屏。浏览器 Network 面板里,Sentry 的 envelope 请求返回了成功状态;Sentry 也出现了一个新 Issue,但堆栈只有 assets/index-Cc8f3.js:1:18492。后端同一时刻产生了 InventoryUnavailableError,却被拆成几十个 Issue。值班同学不知道错误属于哪个版本,也无法判断它是否只发生在灰度环境。
这不是一个“SDK 是否安装成功”的问题,而是五段链路同时失去约束:
SDK 生成 Event,但 Event 的 release、environment、trace 上下文不稳定。服务端接收 Event,却按错误堆栈和 grouping signature 得到了意外的 Issue。前端构建产物带着压缩堆栈,匹配它的 source map 没有在错误发生前上传。
Release 没有关联 commit 和 deploy,Issue 即使可读,也无法指向最可疑的变更。Replay、Profile 和 Trace 开启后迅速消耗配额,团队只好临时关掉采集,又失去了事故证据。
Sentry 的核心对象并不复杂。一次实际发生的错误是 Event;服务端用堆栈、异常类型、消息和指纹等信息计算 grouping signature,把相似 Event 汇成 Issue。Release 表示一份可追溯的软件版本,Deploy 表示这份 Release 进入某个 Environment 的动作。Trace、Profile 和 Replay 是与错误事件关联的其他证据,不是 Error Event 的别名。
看到 envelope 请求成功,只能说明客户端完成了一次发送。事件仍可能被 SDK 采样、入站过滤、组织配额、速率限制或服务端处理失败挡住。真正的最小成功标准是:拿到 Event ID,在目标项目中找到该 Event,确认它进入预期 Issue,并看到正确的 release、environment 和可读堆栈。
先选择谁来承担接收与存储
第一次接入,优先用 Sentry SaaS 建测试组织和测试项目。它省掉 Kafka、ClickHouse、PostgreSQL、Redis、Snuba、Symbolicator、Relay、Web 和 Worker 等组件的升级与容量责任,适合希望先验证错误治理流程的团队。需要数据驻留、隔离网络或自主管理底层存储时,再评估 self-hosted;“不付订阅费”并不等于总成本更低。
SaaS 接入只需要在组织中创建项目,选择运行平台,然后从项目设置取得 DSN。浏览器应用会把 DSN 放进公开 bundle,它的用途是把遥测数据投递到指定项目。DSN 不是允许读取 Issue、删除数据、上传 source map 或管理成员的 API auth token。即使如此,也应限制允许来源、监控滥用,并在泄露或项目迁移时轮换 client key。
self-hosted 应从官方 self-hosted 发布版启动,不应直接跟随仓库的 nightly 分支。官方当前入口使用 Linux、Docker、Docker Compose 和安装脚本:
VERSION=$(curl -Ls -o /dev/null -w '%{url_effective}' \
https://github.com/getsentry/self-hosted/releases/latest)
VERSION=${VERSION##*/}
git clone https://github.com/getsentry/self-hosted.git
cd self-hosted
git checkout "$VERSION"
./install.sh
docker compose up --wait
curl -fsS http://127.0.0.1:9000/_health/安装脚本会询问是否发送 self-hosted 诊断信息;自动化安装应显式决定,而不是接受无人知晓的默认行为。官方给出的最低资源起点是 4 核 CPU、16 GB RAM 加 16 GB swap、20 GB 可用磁盘,并建议 32 GB RAM;这些数字只能证明安装有机会启动,不能代替容量压测。单机 Compose 内的 Kafka 等服务仍是单节点,磁盘 I/O、iowait、保留期和队列积压才是它能否长期运行的判断依据。
反向代理接入后,要让 system.url-prefix 与外部 HTTPS 地址一致,并用 /_health/ 做负载均衡健康检查。URL 或协议不一致常表现为登录跳转、CSRF、集成回调失败,而不是“SDK 无法上报”。
停止测试栈时先保留调查所需数据,再执行:
docker compose down
# 确认不再需要所有 self-hosted 数据后,才删除卷。
docker compose down --volumes--volumes 会删除本地持久数据,不是普通的“重启”。共享环境禁止把它写进无确认的清理脚本。
用一个后端事件建立第一条证据链
下面的 Node.js 实验只需要一个测试项目 DSN、Node.js 和 npm。先把 DSN 写入当前终端的环境变量,不写进源码、命令历史截图或仓库:
mkdir sentry-node-lab
cd sentry-node-lab
npm init -y
npm install @sentry/node创建 verify.mjs:
import * as Sentry from "@sentry/node";
const required = ["SENTRY_DSN", "APP_RELEASE", "APP_ENV"];
for (const name of required) {
if (!process.env[name]) throw new Error(`missing ${name}`);
}
Sentry.init({
dsn: process.env.SENTRY_DSN,
release: process.env.APP_RELEASE,
environment: process.env.APP_ENV,
sampleRate: 1.0,
sendDefaultPii: false,
debug: true,
});
function reserveInventory() {
const error = new Error("inventory reservation failed");
error.name = "InventoryUnavailableError";
throw error;
}
let eventId;
try {
reserveInventory();
} catch (error) {
Sentry.setTag("test_case", "backend-event-v1");
Sentry.setContext("order", { channel: "verification", itemCount: 1 });
eventId = Sentry.captureException(error);
}
const flushed = await Sentry.flush(5000);
console.log({ eventId, flushed });为测试运行指定稳定值:
export SENTRY_DSN='<project-dsn>'
export APP_RELEASE='inventory-api@1.4.0+abc1234'
export APP_ENV='staging'
node verify.mjsPowerShell 可用 $env:SENTRY_DSN='<project-dsn>' 这种写法设置同名变量。程序应打印 32 位 Event ID 和 flushed: true。flush 只表示 SDK 传输队列在超时前完成,不保证服务端最终入库,所以随后仍要在 Sentry 按 Event ID 搜索并核对:
项目是测试项目,environment 为 staging;release 为 inventory-api@1.4.0+abc1234;异常类型为 InventoryUnavailableError;
栈顶指向 reserveInventory;test_case 可以搜索,order context 只用于单个事件调查。
Tag 与 Context 的区别会影响后续成本。Tag 会进入索引,可搜索和聚合;Context 提供结构化细节,通常不用于搜索。把订单号、请求 ID、用户 ID 全部做成 tag 会制造高基数索引;把需要检索的低基数维度全塞进 context,又会让值班人员查不到事件。
让 grouping 失败一次
创建 grouping.mjs,用同一测试项目发送两组事件。第一组使用稳定指纹,两个订单应进入同一 Issue;第二组错误地把订单号放进指纹,应产生两个 Issue:
import * as Sentry from "@sentry/node";
if (!process.env.SENTRY_DSN) throw new Error("missing SENTRY_DSN");
Sentry.init({
dsn: process.env.SENTRY_DSN,
environment: "staging",
release: "inventory-api@1.4.0+abc1234",
sampleRate: 1,
sendDefaultPii: false,
});
function send(orderId, fingerprint, testCase) {
return Sentry.withScope((scope) => {
scope.setTag("test_case", testCase);
scope.setTag("order_kind", "synthetic");
scope.setContext("verification", { orderId });
scope.setFingerprint(fingerprint);
const error = new Error("inventory reservation failed");
error.name = "InventoryUnavailableError";
return Sentry.captureException(error);
});
}
const eventIds = [
send("SYN-100", ["inventory-reservation"], "stable-fingerprint"),
send("SYN-200", ["inventory-reservation"], "stable-fingerprint"),
send("SYN-300", ["inventory-reservation", "SYN-300"], "dynamic-fingerprint"),
send("SYN-400", ["inventory-reservation", "SYN-400"], "dynamic-fingerprint"),
];
const flushed = await Sentry.flush(5000);
console.log({ eventIds, flushed });node grouping.mjs按打印出的四个 Event ID 逐个打开事件。stable-fingerprint 的两条 Event 应归入一个 Issue,dynamic-fingerprint 应归入两个 Issue;若数量不同,先确认测试项目没有服务端 fingerprint rule 覆盖 SDK 决策。这个反例说明把订单号、URL、用户输入或随机 ID 放进 fingerprint,会让 Issue 数量随业务请求增长,告警、指派和趋势全部失真。
需要保留默认分组输入并附加稳定故障语义时,可以使用 ["{{ default }}", "inventory-reservation"]。若一个通用异常类型承载多种可独立修复的故障,也只能按受控错误码拆分。Sentry 的 Issue Details 会显示 grouping 信息;修改 SDK fingerprint 或服务端 fingerprint rules 前,应在测试项目记录修改前后的 Event ID、Issue ID 和数量。
错误分组配置变更通常只影响后续事件。不要把 Merge 当成修复 grouping 的永久方法:Merge 改变现有 Issue 关系,下一次出现的事件仍会按当时的 grouping 配置计算。
Release 把错误接回代码和部署
release 不能用 latest、prod、Pod 名或会重复的流水线编号。组织内同名 release 会被视为同一软件版本,单仓库可采用 inventory-api@1.4.0+abc1234,多服务仓库可采用 <service>@<version>+<short-sha>。SDK、CI、source map 上传和 deploy 记录必须使用同一个字节串,大小写和前缀也不能漂移。
CI 中安装 sentry-cli 后,可把 Git 变更和部署动作接到 release:
export SENTRY_ORG='your-org'
export SENTRY_PROJECT='inventory-api'
export SENTRY_RELEASE='inventory-api@1.4.0+abc1234'
export SENTRY_AUTH_TOKEN='<ci-auth-token>'
sentry-cli releases new "$SENTRY_RELEASE"
sentry-cli releases set-commits "$SENTRY_RELEASE" --auto
sentry-cli releases finalize "$SENTRY_RELEASE"
sentry-cli releases deploys "$SENTRY_RELEASE" new -e staging这段命令需要面向 CI 的 token。优先使用组织或内部集成身份授予 org:ci,并满足 sentry-cli 读取组织信息所需的 org:read;不要复用个人管理员 token。set-commits --auto 依赖仓库集成、可用 Git 历史和可推导的上一版本。浅克隆常让自动推导失败;此时应补足 fetch depth,或显式提供上一提交与当前提交,不能把“没有 suspect commit”误判成 Sentry 的 grouping 故障。Release API 允许关联 commit,Deploy API 把 release 与 environment 联系起来;这两条关系共同支撑 first seen、last seen、regression 和 suspect commit 判断。
应用回滚到旧构建时,SDK 的 release 必须随构建回到旧值,同时记录一次旧 release 的新 deploy。不要新造 rollback-1 版本去代表旧二进制,否则 source map、commit 和错误趋势都会指向不存在的制品。回滚后用测试事件确认旧 release 的堆栈可读,再观察新 release 的事件速率是否停止增长。
Environment 只保留稳定枚举,例如 production、staging、development。把 pr-184、Pod UID 或分支名写入 environment 会留下大量历史值;Sentry 提供的是隐藏 environment,而不是把它当作临时标签随意创建和删除。临时环境身份放 tag,且要限制基数和生命周期。
让前端 source map 先失败,再沿证据修好
source map 故障必须用生产构建复现。开发服务器的模块地址、watch mode 和未压缩代码不能证明线上反混淆链有效。下面以 Vite 浏览器项目为例:
node --version
cd ..
npm create vite@9.1.1 sentry-web-lab -- --template vanilla
cd sentry-web-lab
npm install
npm install @sentry/browser
npm install --save-dev @sentry/clicreate-vite@9.1.1 要求 Node.js 20.19+ 或 22.12+。版本不满足时先切换团队锁定的 Node.js LTS,不要用忽略 engine 的方式继续生成。脚手架版本必须进入任务记录,生成后的 package-lock.json 必须保留,避免下一次实验悄悄换成另一套构建链。
在入口最早位置初始化 SDK,并放一个确定会抛错的按钮:
import * as Sentry from "@sentry/browser";
Sentry.init({
dsn: import.meta.env.VITE_SENTRY_DSN,
release: import.meta.env.VITE_SENTRY_RELEASE,
environment: import.meta.env.VITE_APP_ENV,
sendDefaultPii: false,
tracesSampleRate: 0,
});
document.querySelector("#app").innerHTML = `
<button id="verify-sentry">Trigger source map error</button>
`;
document.querySelector("#verify-sentry").addEventListener("click", () => {
const inventory = undefined;
inventory.reserve();
});让 Vite 生成 hidden source map。hidden 模式保留 .map 文件供 CI 上传,但不会在公开 bundle 尾部写 source map URL:
import { defineConfig } from "vite";
export default defineConfig({
build: {
sourcemap: "hidden",
},
});第一次故意不上传 source map。构建并用静态服务器启动同一份 dist:
export VITE_SENTRY_DSN='<browser-project-dsn>'
export VITE_SENTRY_RELEASE='storefront@2.8.0+def5678'
export VITE_APP_ENV='staging'
npm run build
npx sentry-cli sourcemaps inject dist
npx serve dist保持静态服务器运行,在另一个终端访问页面并点击按钮。预期 Event 的堆栈仍指向压缩 bundle;记录 Event ID。此时不要重新构建,因为重新构建可能生成不同制品和 Debug ID。
现在给 CI 终端注入 auth token,上传刚才那一份 dist,然后重新触发一次错误:
export SENTRY_AUTH_TOKEN='<ci-auth-token>'
export SENTRY_ORG='your-org'
export SENTRY_PROJECT='storefront'
npx sentry-cli sourcemaps upload \
--org "$SENTRY_ORG" \
--project "$SENTRY_PROJECT" \
--validate \
dist第二个新 Event 应显示原始文件、函数、行和列;第一个旧 Event 通常仍保持原来的压缩堆栈,因为事后上传 artifact 不会自动重新处理已接收事件。这个差异证明三个事实:部署的是注入 Debug ID 后的 bundle,上传的是与它完全相同的 map,新事件携带的 debug_meta 能与服务端 artifact 匹配。官方的JavaScript source map 故障排查还建议用 Event ID 执行:
npx sentry-cli sourcemaps explain <event-id>排查时按证据顺序推进:
浏览器实际加载的 bundle 是否就是 CI 上传对应的产物,CDN 是否仍在返回旧缓存。bundle 是否存在 Debug ID 注入片段,Event JSON 是否存在 debug_meta。raw_stacktrace 的 abs_path 是否能对应 debug image 的 code_file。
Source Maps 设置中是否能看到对应 artifact,上传发生时间是否早于新 Event。map 是否为 UTF-8 文本、包含原始 sources 和有效映射,压缩工作是否发生在上传之后。
如果 explain 报找不到 artifact,反复上传“最新构建”通常会让问题更乱。先保全 Event ID、线上 bundle 哈希、CI artifact 哈希和 Debug ID,再判断是上传失败、部署错包还是 CDN 缓存。上传成功后,可以按安全策略删除公开部署目录中的 .map;Sentry 已保存的 artifact 不依赖 Web 服务器继续公开 map。构建平台也可使用官方 Vite/webpack/esbuild 插件自动注入并上传,但仍要保留同样的验证事件,把上传失败直接判为发布失败。
Trace、Profile 和 Replay 要共享故障上下文
Error Event 告诉你哪里抛错,Trace 说明请求经过哪些服务和 span,Profile 说明采样时间内 CPU 在哪里消耗,Replay 还原浏览器交互与网络上下文。四类数据只有共享 release、environment、trace ID 和用户会话语义,才构成一条调查链。
采样配置彼此独立,不能因为错误事件要尽量保留,就把所有性能信号都设为 100%。浏览器 SDK 同时启用错误、Trace 和 Replay 时,典型结构如下:
import * as Sentry from "@sentry/browser";
Sentry.init({
dsn: import.meta.env.VITE_SENTRY_DSN,
environment: import.meta.env.VITE_APP_ENV,
release: import.meta.env.VITE_SENTRY_RELEASE,
integrations: [Sentry.replayIntegration()],
sampleRate: 1.0,
tracesSampler: ({ name, inheritOrSampleWith }) => {
if (name.includes("/health")) return 0;
if (name.includes("/checkout")) return 0.25;
return inheritOrSampleWith(0.05);
},
replaysSessionSampleRate: 0.01,
replaysOnErrorSampleRate: 1.0,
});sampleRate 控制错误事件;tracesSampler 或 tracesSampleRate 控制 transaction/trace 的入口决策;Replay 又有普通会话与错误会话两条策略。Profile 还需要目标平台对应的 profiling integration;采用 trace 绑定型 profilesSampleRate 的 SDK,实际 profile 比例是 trace 采样与 profile 采样两级决策的乘积。浏览器连续 Profile 与服务端 transaction Profile 的生命周期并不相同,升级 SDK 时要以该平台的 profiling 页面和迁移说明为准。不要把示例比例直接搬到生产,它们必须由流量、配额预算、故障价值和客户端开销反推。
分布式系统还要传播 sentry-trace 与 baggage。若下游服务忽略父级采样决定,各服务独立抽签,就会出现只有前端、没有后端,或只有中间一段的残缺 trace。跨域浏览器请求还要检查 tracePropagationTargets 和 CORS 是否允许相关 header。
Replay 会记录 DOM、交互、控制台和请求上下文,Profile 会增加客户端或服务端开销。上线时先在测试环境检查遮罩结果和包体/CPU 变化,再按低比例灰度。事故期间临时提高采样,要同时设置结束时间、配额上限和恢复动作。
配额耗尽时,先查数据在哪里被丢掉
SaaS 的产品数据配额、SDK 采样和 Web API rate limit 是三件事。API rate limit 约束脚本调用管理 API 的频率,可从响应 header 观察;产品配额约束 Error、Transaction、Replay、Profile 等数据量;SDK 采样发生在发送前。把 API 的 429 与事件配额混为一谈,会把排障方向带到错误的 token 或网络上。
一次突发循环可能每秒产生数千个相同错误。它们即使归到一个 Issue,仍然是大量 Event,并继续消耗接收、处理、索引和保留资源。客户端 beforeSend、ignoreErrors 或采样可以减量,服务端入站过滤和配额可以保护平台,但每个丢弃点都可能吞掉关键证据。
容量预算至少按信号分别计算:
每日接收量 = 请求或会话量 × 产生率 × 采样率
每日存储量 = 每日接收量 × 平均事件大小 × 保留天数
峰值接收率 = 峰值请求率 × 峰值产生率 × 采样率对 SaaS,持续观察 Usage、丢弃原因和 spike;预算只写数据类型、量级和保留目标,不把易变套餐数字写入代码。对 self-hosted,还要观察 Relay 拒绝、Kafka lag、消费者处理速率、ClickHouse 写入与查询、PostgreSQL、对象存储、磁盘水位和 iowait。降低 SENTRY_EVENT_RETENTION_DAYS 可以换取磁盘空间,却会缩短事故回溯窗口;它不是磁盘满后的唯一修复。
PII 清洗必须发生在保存之前
错误消息、URL query、request body、header、Cookie、IP、用户对象、breadcrumb、extra、局部变量、附件和 Replay 都可能携带敏感信息。sendDefaultPii: false 是起点,不是合规证明。业务代码主动写入 setUser、setContext 或异常 message 的数据,不会因为一个总开关就自动变安全。
优先在 SDK 侧删除根本不应离开进程的数据,再用 Sentry 服务端 scrubbing 做第二道保护:
Sentry.init({
dsn: process.env.SENTRY_DSN,
sendDefaultPii: false,
beforeSend(event) {
if (event.request) {
delete event.request.cookies;
delete event.request.data;
if (event.request.headers) {
const blocked = new Set(["authorization", "cookie", "set-cookie"]);
for (const name of Object.keys(event.request.headers)) {
if (blocked.has(name.toLowerCase())) {
delete event.request.headers[name];
}
}
}
}
if (event.user) {
delete event.user.email;
delete event.user.ip_address;
}
return event;
},
});Node.js 和部分框架会把请求头归一化成小写,因此删除逻辑按不区分大小写的字段名处理。测试仍不能只读配置:构造带假邮箱、假 bearer token、假 Cookie 和假卡号的事件,在 Event JSON、通知、附件和 Replay 中逐项确认不可见。服务端高级 scrubbing 规则只影响之后进入的新事件;一旦敏感数据已经存储,改规则不会清洗历史副本。
Source map 本身也可能包含完整源码和 sourcesContent。它应作为受保护构建制品处理:CI token 只存在于 Secret 存储,上传后从公开部署产物删除 .map,Sentry 项目权限按需要读取。Replay 默认遮罩策略和 SDK 升级可能变化,每次升级都重新跑含表单、支付、富文本和上传组件的脱敏回归。
DSN、auth token 与人员角色是三条权限边界
DSN 面向 SDK 数据投递;auth token 面向 API、release、commit、deploy 和 source map 自动化;组织/团队角色决定人在 UI 和项目中能读写什么。它们不能互换。
浏览器 bundle 中可以出现 DSN,但绝不能出现 SENTRY_AUTH_TOKEN。DSN 持有者可以向项目发送数据并消耗接收资源,却不能据此读取 Issue 或管理项目;auth token 是 Bearer 凭证,能做什么取决于授予它的 API scopes。CI token 使用组织级或内部集成身份,优先授予面向 CI/部署的 org:ci,或平台实际提供的等价 release/project 权限,并限定到构建所需项目;sentry-cli 还需要读取组织信息。读取事件、修改 Issue、删除 Issue 分别使用 event read/write/admin 类权限,删除能力不应交给普通构建流水线。Sentry 的API 权限说明明确区分 release 与 event 权限,并指出单个 Event 不可变,删除要以整个 Issue 为单位执行。
权限审计可以从四个问题入手:
这个凭证能否读取事件正文和附件?它能否上传或删除 Release artifact?它能否删除 Issue、项目或成员?
凭证轮换、离职回收和流水线日志脱敏由谁验证?
Token 不能写入 .env.example 的值、构建参数、Docker image layer 或前端 VITE_* 变量。CI 只在上传步骤注入它,并屏蔽命令回显。泄露后先撤销或轮换,再检查审计记录和异常 artifact;仅从 Git 删除字符串不能使已泄露 token 失效。
删除、回滚和迁移要保留不同语义
Resolve 表示团队认为故障已修复,后续再次发生可被标记为 regression;Ignore 表示暂不处理;Delete 删除的是 Issue 及其 Event 数据。删除后若同一错误再次发生,它可能作为新 Issue 回来;Delete and Discard Forever 会让未来匹配事件继续被丢弃,因此只能用于确认无价值且不会遮蔽安全/稳定性信号的噪声。
由于 Event 不可单独修改或删除,误传 PII 时要先停止继续发送,撤销相关凭证,确认受影响 Issue,再按组织权限执行删除与合规响应。删除 Issue 会损失同组其他正常 Event,正是团队必须在采集前清洗、避免过宽 fingerprint 的原因。
应用回滚不等于 Sentry 数据回滚。正确动作是部署旧制品、恢复对应 release、记录 deploy、验证新事件归属;错误动作是删除新 release 的 Issue 来让图表“恢复正常”。后者消除了证据,却没有改变线上代码。
self-hosted 官方 JSON 备份只承载组织、项目、团队等低容量配置数据,不包含历史 Event、Issue 和外部文件。官方也明确说明,对整套 Docker volumes 做快照并恢复不属于受支持的备份方案;如果团队仍选择卷级灾备,就必须把它视为自担风险的基础设施方案,在与生产同版本的隔离环境验证 PostgreSQL、ClickHouse、Kafka、对象存储及其他持久卷的一致恢复,不能把 sentry export 成功当成事件数据已经受保护。
升级前按照官方备份与恢复和升级路径分别验证受支持的配置导出、团队自建的数据保护方案及其恢复限制。Compose 文件、config.yml、sentry.conf.py 与可恢复数据必须形成可解释的一致点。先在副本上执行升级和回滚演练,再变更生产;跨多个 release 跳升时遵循官方 hop 要求,不能只回退容器镜像而保留已迁移的数据结构。
从 SaaS 迁出或从 self-hosted 迁入时,先验证 SDK DSN 切换、历史数据可移植性、Release/source map 重建、用户与项目权限、集成回调、数据保留和删除证明。双写灰度会增加配额和敏感数据副本,必须限定时间。切换完成后撤销旧 token、停止旧 DSN、保留审计证据,再按合同或内部策略删除旧平台数据。
团队如何把 Sentry 维持成可用证据系统
应用团队负责 SDK 版本、错误上下文和 Issue 修复;发布平台负责 release、commit、deploy 与 source map 原子性;安全团队维护 PII 字段策略和删除响应;平台团队维护项目、Relay 或 self-hosted 组件;成本负责人按信号审查采样、配额和保留。职责分开后,仍要用同一条发布验证把它们连起来。
每次生产发布至少自动完成四个动作:创建 release 并关联 commit,上传与待部署制品匹配的 source map,记录目标 environment 的 deploy,触发一个带固定 test_case tag 的合成错误并验证可读堆栈。验证失败时阻断或回滚这份制品,而不是先上线再等待真实用户替团队测试。
月度治理关注趋势,不使用脱离流量的万能阈值:事件量是否与业务请求量同比变化,Issue 数是否因 fingerprint 失控增长,source map 成功率是否稳定,关键环境是否出现 release 缺失,丢弃量是否长期抬升,Replay/Profile 是否超出预算,self-hosted lag、磁盘和恢复时间是否仍在容量目标内。
季度演练至少选一次“CI token 失效导致 source map 上传失败”、一次“错误循环造成配额冲击”和一次“敏感字段误传”。演练结果要能回答:第一证据在哪里、谁有权限止损、旧事件怎么处理、如何恢复采集、怎样证明修复没有把另一类重要错误一起过滤。
实验结束后,先在运行 npx serve 的终端按 Ctrl+C,再清除当前 shell 的凭证和构建变量。测试 Issue 确认不再需要后,按测试项目的数据处置规则删除;最后检查当前目录确实是两个实验目录的父目录,再删除本地文件:
unset SENTRY_DSN SENTRY_AUTH_TOKEN SENTRY_ORG SENTRY_PROJECT SENTRY_RELEASE
unset APP_RELEASE APP_ENV VITE_SENTRY_DSN VITE_SENTRY_RELEASE VITE_APP_ENV
cd ..
test -f sentry-node-lab/package.json && test -f sentry-web-lab/package.json
rm -r -- sentry-node-lab sentry-web-labPowerShell 使用 Remove-Item Env:SENTRY_DSN,Env:SENTRY_AUTH_TOKEN 等方式逐个清除当前会话变量。删除目录前同样检查两个目录中的 package.json,不要把变量拼接成递归删除目标。CI job 应使用一次性执行环境;步骤结束后仍要撤销临时 token 或确认短期凭证已经过期。
当以下证据同时成立,Sentry 才算真正接进了项目:
后端测试 Event 能按 Event ID 找到,并按预期 grouping;前端生产 bundle 的新 Event 能映射回源码,旧失败 Event 的原因可解释;release、commit、deploy 与 environment 能还原一次发布和回滚;
Error、Trace、Profile、Replay 的采样各有预算和恢复开关;假敏感数据在 Event、通知和 Replay 中均不可见;DSN、CI token、读写角色和删除权限已经分离;
SaaS 有配额与退出方案;self-hosted 已区分官方配置导出和团队自担风险的数据灾备,并完成容量、升级和恢复演练。
