Skip to content

Commit 2a506f7

Browse files
authored
feat(monitoring): 接入 Sentry 错误监控(免费 Developer tier) (#296)
1 parent fb33cbe commit 2a506f7

8 files changed

Lines changed: 1481 additions & 33 deletions

.env.sample

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,16 @@ INTERN_KEY=
2929
# 智谱 AI 开放平台 API Key,聊天与建议接口的默认免费模型 GLM-4.6V-Flash
3030
# 在 https://open.bigmodel.cn/ 注册后获取
3131
ZHIPU_API_KEY=
32+
33+
# Sentry 错误监控(Developer plan 免费 5K errors / 10K perf units / 月)
34+
# NEXT_PUBLIC_SENTRY_DSN 是浏览器端需要的公开 DSN,暴露在前端 bundle 里属于设计,
35+
# SENTRY_AUTH_TOKEN 仅用于 next build 时上传 source map,私密。
36+
# SDK 只在 NODE_ENV=production 启用,本地 dev/preview 不会产生事件、不耗配额。
37+
# Vercel 端由 Sentry 集成自动注入;本地 dev 可不填。
38+
NEXT_PUBLIC_SENTRY_DSN=
39+
SENTRY_AUTH_TOKEN=
40+
SENTRY_ORG=involutionhell
41+
SENTRY_PROJECT=sentry-bole-notebook
3242
# Neon 项目 ID
3343
NEON_PROJECT_ID=
3444
# Neon 提供的 Postgres 连接。

instrumentation.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/**
2+
* Next.js 15+ 约定的启动 hook:每个 runtime 启动时各自拉对应 Sentry 配置,
3+
* 避免在 edge runtime 错误加载 Node-only 代码。
4+
*
5+
* onRequestError 暴露给 Next.js 捕获服务端组件/路由层面的未捕获错误。
6+
*/
7+
import * as Sentry from "@sentry/nextjs";
8+
9+
export async function register() {
10+
if (process.env.NEXT_RUNTIME === "nodejs") {
11+
await import("./sentry.server.config");
12+
}
13+
if (process.env.NEXT_RUNTIME === "edge") {
14+
await import("./sentry.edge.config");
15+
}
16+
}
17+
18+
export const onRequestError = Sentry.captureRequestError;

next.config.mjs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// next.config.mjs
22
import { createMDX } from "fumadocs-mdx/next";
33
import createNextIntlPlugin from "next-intl/plugin";
4+
import { withSentryConfig } from "@sentry/nextjs";
45

56
/**
67
* IMPORTANT: remarkImage 配置已移至 source.config.ts 统一管理
@@ -150,4 +151,26 @@ const config = {
150151
},
151152
};
152153

153-
export default withNextIntl(withMDX(config));
154+
const finalConfig = withNextIntl(withMDX(config));
155+
156+
// Sentry 包裹:webpack 插件需要看到最终的 Next 配置才能上传 source map。
157+
// silent: !CI 让本地构建不刷日志,只在 Vercel CI 构建时打印。
158+
// widenClientFileUpload 扩大 source map 扫描范围,保证前端错误堆栈能解出来。
159+
// disableLogger 树摇掉 Sentry 自带 logger,减小 bundle 体积。
160+
//
161+
// 守门条件:只有在存在 SENTRY_AUTH_TOKEN 时才启用 withSentryConfig。
162+
// 贡献者 clone 仓库后没配 Sentry env 也能直接 `pnpm build` / `pnpm dev`,
163+
// 不会因为 webpack 插件缺凭据而构建失败。生产 Vercel 那边 env 齐全,正常上报。
164+
const enableSentry = Boolean(process.env.SENTRY_AUTH_TOKEN);
165+
166+
export default enableSentry
167+
? withSentryConfig(finalConfig, {
168+
org: process.env.SENTRY_ORG || "involutionhell",
169+
project: process.env.SENTRY_PROJECT || "sentry-bole-notebook",
170+
silent: !process.env.CI,
171+
widenClientFileUpload: true,
172+
disableLogger: true,
173+
// 不启用 tunnelRoute:需要加 /monitoring rewrite,和现有 rewrites 交互
174+
// 复杂;广告屏蔽对 docs 站影响小,后续真需要再打开。
175+
})
176+
: finalConfig;

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
"@radix-ui/react-radio-group": "^1.3.8",
4646
"@radix-ui/react-slot": "^1.2.3",
4747
"@radix-ui/react-tooltip": "^1.2.8",
48+
"@sentry/nextjs": "^10.49.0",
4849
"@types/mdx": "^2.0.13",
4950
"@types/pg": "^8.16.0",
5051
"@vercel/speed-insights": "^1.2.0",

pnpm-lock.yaml

Lines changed: 1384 additions & 32 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sentry.client.config.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
* Sentry 浏览器端初始化。
3+
*
4+
* 免费 tier 策略(Developer plan,5K errors/月 + 10K perf units/月):
5+
* - 只在 production 启用,避免本地 dev/preview 污染配额
6+
* - tracesSampleRate 0.1:10% 的页面 transaction 采样足够看性能趋势
7+
* - 关闭 Session Replay:它是另外的独立配额(小),开了容易炸
8+
* - 不启用 profiling(需要付费)
9+
*/
10+
import * as Sentry from "@sentry/nextjs";
11+
12+
Sentry.init({
13+
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
14+
enabled: process.env.NODE_ENV === "production",
15+
tracesSampleRate: 0.1,
16+
replaysSessionSampleRate: 0,
17+
replaysOnErrorSampleRate: 0,
18+
// 线上开 false 省日志;排障时临时改 true
19+
debug: false,
20+
});

sentry.edge.config.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/**
2+
* Sentry Edge runtime 初始化(Middleware / Edge routes)。
3+
* 当前项目未使用 edge runtime,保留配置以便未来迁移时无需补齐。
4+
*/
5+
import * as Sentry from "@sentry/nextjs";
6+
7+
Sentry.init({
8+
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
9+
enabled: process.env.NODE_ENV === "production",
10+
tracesSampleRate: 0.1,
11+
debug: false,
12+
});

sentry.server.config.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/**
2+
* Sentry Node.js runtime 初始化(Next.js API routes / Server Components / RSC)。
3+
* 与 client 同策略:仅 production 启用,traces 10%,无 replay/profiling。
4+
*/
5+
import * as Sentry from "@sentry/nextjs";
6+
7+
Sentry.init({
8+
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
9+
enabled: process.env.NODE_ENV === "production",
10+
tracesSampleRate: 0.1,
11+
debug: false,
12+
});

0 commit comments

Comments
 (0)