三层覆盖,共用一个 DSN,共用一个 opt-out 机制。
Browser (Renderer) Server (Node.js) Electron Main
│ │ │
SentryInit.tsx instrumentation.ts electron/main.ts
@sentry/browser @sentry/node @sentry/electron
│ │ │
beforeSend beforeSend Sentry.init()
(localStorage check) (strip auth headers) (marker file check)
│ │ │
└─── DSN: next.config.ts env ──── DSN: hardcoded ┘
│
~/.codepilot/sentry-disabled
(opt-out marker file)
- Renderer + Server:通过
next.config.ts的NEXT_PUBLIC_SENTRY_DSN环境变量 - Electron Main:硬编码(main process 不经过 Next.js env)
- DSN 是公开 ingest URL(Sentry 设计),安全提交到代码库
@sentry/nextjs@9.x 的 peer dep 要求 next@^13 || ^14 || ^15,CodePilot 用 Next.js 16。改用:
@sentry/browser— 客户端@sentry/node— 服务端@sentry/electron— 主进程
文件:src/components/layout/SentryInit.tsx
- 客户端组件,在
AppShell中渲染 useEffect中动态import('@sentry/browser'),DSN 不存在时不加载beforeSend检查localStorage['codepilot:sentry-disabled'],opt-out 即时生效- 去除
ui.inputbreadcrumb,删除 auth headers
文件:src/instrumentation.ts
- Next.js
register()hook,服务启动时执行一次 - 读取
~/.codepilot/sentry-disabledmarker file - 如果 marker 为
true,不初始化@sentry/node - opt-out 变更需重启应用才对 server 层生效
文件:electron/main.ts(文件最顶部,所有其他 import 之前)
- 读取
~/.codepilot/sentry-disabledmarker file - 如果 marker 为
true,不调用Sentry.init() - opt-out 变更需重启应用才对 main process 生效
文件:src/components/settings/GeneralSection.tsx — SentryToggle 组件
- 位置:Settings > General 卡片内,紧跟 Setup Center 下方
- 使用
useSyncExternalStore读取 localStorage(无 hydration mismatch) - 默认开启,用户可关闭
切换开关时同时写两个位置:
localStorage['codepilot:sentry-disabled']— browser 层即时读取~/.codepilot/sentry-disabled文件 — server + electron main 启动时读取
文件写入通过 POST /api/settings/sentry(src/app/api/settings/sentry/route.ts)。
| 层 | opt-out 生效 |
|---|---|
| Browser | 即时(beforeSend 每次检查 localStorage) |
| Server | 重启后(instrumentation.ts 只在 register() 读一次) |
| Electron Main | 重启后(main.ts 顶部只读一次) |
文案已明确提示用户"更改后需重启应用才能完全生效"。
文件:src/lib/error-classifier.ts — reportToSentry()
仅上报严重错误类别:
PROCESS_CRASH— Claude Code SDK 进程崩溃UNKNOWN— 无法分类的错误CLI_NOT_FOUND— CLI 找不到CLI_INSTALL_CONFLICT— CLI 安装冲突MISSING_GIT_BASH— Windows 缺 Git BashPROVIDER_NOT_APPLIED— Provider 未生效SESSION_STATE_ERROR— 会话状态损坏
RATE_LIMITED— 预期内,限流CONTEXT_TOO_LONG— 预期内,自动压缩处理AUTH_REJECTED/AUTH_FORBIDDEN— 用户配置问题NETWORK_UNREACHABLE— 网络问题RESUME_FAILED— 会话恢复失败(自动处理)
文件:src/components/layout/ErrorBoundary.tsx
componentDidCatch 中通过 import('@sentry/browser').then(Sentry.captureException) 上报所有未捕获的 React 渲染错误。
- 不采集 performance trace(
tracesSampleRate: 0) - 不采集 session replay
- 不采集用户输入 breadcrumb(
ui.input过滤) - 删除 auth headers(
x-api-key、authorization、anthropic-api-key) - 不含对话内容
| 文件 | 职责 |
|---|---|
next.config.ts |
DSN 环境变量(NEXT_PUBLIC_SENTRY_DSN) |
electron/main.ts |
Electron main Sentry.init() + opt-out check |
src/instrumentation.ts |
Server Sentry.init() + opt-out check |
src/components/layout/SentryInit.tsx |
Browser 动态初始化 |
src/components/layout/ErrorBoundary.tsx |
React 错误捕获 → Sentry |
src/components/layout/AppShell.tsx |
渲染 SentryInit |
src/lib/error-classifier.ts |
reportToSentry() 严重错误上报 |
src/components/settings/GeneralSection.tsx |
SentryToggle opt-out 开关 |
src/app/api/settings/sentry/route.ts |
opt-out marker 文件读写 API |
src/i18n/en.ts + zh.ts |
开关文案 + 重启提示 |
Sentry 免费版限额 5,000 错误/月。通过以下方式控制:
- 只上报
SENTRY_REPORTABLE集合中的严重错误(7 个类别) tracesSampleRate: 0— 不采性能数据replaysSessionSampleRate: 0— 不录回放- 预期内错误(限流、上下文过长)不上报
如果接近额度,可以在 Sentry Dashboard 设置 rate limiting。