sentry-nestjs-sdk by getsentry/sentry-for-ai
npx skills add https://github.com/getsentry/sentry-for-ai --skill sentry-nestjs-sdk一个经过精心设计的向导,用于扫描您的 NestJS 项目并指导您完成完整的 Sentry 设置。
@sentry/nestjs 或 Sentry + NestJS注意: 下面的 SDK 版本和 API 反映了
@sentry/nestjs10.x(支持 NestJS 8–11)。在实施前,请务必对照 docs.sentry.io/platforms/node/guides/nestjs/ 进行验证。
在提出建议之前,运行以下命令来了解项目:
# 确认是 NestJS 项目
grep -E '"@nestjs/core"' package.json 2>/dev/null
# 检查 NestJS 版本
node -e "console.log(require('./node_modules/@nestjs/core/package.json').version)" 2>/dev/null
# 检查是否已存在 Sentry
grep -i sentry package.json 2>/dev/null
ls src/instrument.ts 2>/dev/null
grep -r "Sentry.init\|@sentry" src/main.ts src/instrument.ts 2>/dev/null
# 检查是否存在 Sentry DI 包装器(企业级 NestJS 中常见)
grep -rE "SENTRY.*TOKEN|SentryProxy|SentryService" src/ libs/ 2>/dev/null
# 检查是否基于配置类进行初始化(相对于基于环境变量)
grep -rE "class SentryConfig|SentryConfig" src/ libs/ 2>/dev/null
# 检查 SentryModule.forRoot() 是否已在共享模块中注册
grep -rE "SentryModule\.forRoot|SentryProxyModule" src/ libs/ 2>/dev/null
# 检测 HTTP 适配器(默认为 Express)
grep -E "FastifyAdapter|@nestjs/platform-fastify" package.json src/main.ts 2>/dev/null
# 检测 GraphQL
grep -E '"@nestjs/graphql"|"apollo-server"' package.json 2>/dev/null
# 检测微服务
grep '"@nestjs/microservices"' package.json 2>/dev/null
# 检测 WebSockets
grep -E '"@nestjs/websockets"|"socket.io"' package.json 2>/dev/null
# 检测任务队列 / 定时作业
grep -E '"@nestjs/bull"|"@nestjs/bullmq"|"@nestjs/schedule"|"bullmq"|"bull"' package.json 2>/dev/null
# 检测数据库
grep -E '"@prisma/client"|"typeorm"|"mongoose"|"pg"|"mysql2"' package.json 2>/dev/null
# 检测 AI 库
grep -E '"openai"|"@anthropic-ai"|"langchain"|"@langchain"|"@google/generative-ai"|"ai"' package.json 2>/dev/null
# 检查是否存在配套的前端项目
ls -d ../frontend ../web ../client ../ui 2>/dev/null
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
需要注意的事项:
@sentry/nestjs 是否已经安装?如果是,检查 instrument.ts 是否存在以及是否调用了 Sentry.init() —— 可能只需要进行功能配置。SENTRY_PROXY_TOKEN)后面。在所有运行时 Sentry 调用(startSpan、captureException、withIsolationScope)中使用注入的代理,而不是在控制器、服务和处理器中直接导入 @sentry/nestjs。只有 instrument.ts 应该直接导入 @sentry/nestjs。Sentry.init() 的选项(例如从 YAML 或 @nestjs/config 加载)。任何新的 SDK 选项都必须添加到配置类型中 —— 不要硬编码那些应该按环境配置的值。SentryModule.forRoot() 已经注册? → 如果它在一个共享模块中(例如一个 Sentry 代理模块),不要在 AppModule 中再次添加 —— 这会导致拦截器重复注册。SentryGlobalFilter 原生支持它。@nestjs/schedule? → 建议使用定时任务监控。prismaIntegration()。根据您的发现,提出具体方案。不要问开放式问题 —— 直接给出建议:
始终建议(核心覆盖):
检测到则建议:
@sentry/profiling-node)@nestjs/schedule、Bull 或 BullMQ建议矩阵:
| 功能 | 何时建议... | 参考 |
|---|---|---|
| 错误监控 | 始终 — 不可或缺的基线 | ${SKILL_ROOT}/references/error-monitoring.md |
| 追踪 | 始终 — NestJS 生命周期自动检测 | ${SKILL_ROOT}/references/tracing.md |
| 性能分析 | 生产环境 + CPU 敏感型工作负载 | ${SKILL_ROOT}/references/profiling.md |
| 日志记录 | 始终;增强用于结构化日志聚合 | ${SKILL_ROOT}/references/logging.md |
| 指标 | 自定义业务 KPI 或 SLO 跟踪 | ${SKILL_ROOT}/references/metrics.md |
| 定时任务监控 | 检测到 @nestjs/schedule、Bull 或 BullMQ | ${SKILL_ROOT}/references/crons.md |
| AI 监控 | 检测到 OpenAI/Anthropic/LangChain 等 | ${SKILL_ROOT}/references/ai-monitoring.md |
提议:“我建议错误监控 + 追踪 + 日志记录。还需要性能分析、定时任务监控或 AI 监控吗?”
# 核心 SDK(始终需要 — 包含 @sentry/node)
npm install @sentry/nestjs
# 包含性能分析支持(可选)
npm install @sentry/nestjs @sentry/profiling-node
⚠️ 请勿同时安装
@sentry/node和@sentry/nestjs—@sentry/nestjs重新导出了@sentry/node的所有内容。同时安装两者会导致重复注册。
NestJS 需要一个特定的三文件初始化模式,因为 Sentry SDK 必须在 NestJS 加载 Node.js 模块之前通过 OpenTelemetry 对它们进行补丁。
在创建新文件之前,检查第一阶段的结果:
- 如果
instrument.ts已存在 → 修改它,不要创建新的。- 如果由配置类驱动
Sentry.init()→ 从配置中读取选项,而不是硬编码环境变量。- 如果存在 Sentry DI 包装器 → 在服务/控制器中使用它进行运行时调用,而不是直接导入
@sentry/nestjs。
src/instrument.tsimport * as Sentry from "@sentry/nestjs";
// 可选:添加性能分析
// import { nodeProfilingIntegration } from "@sentry/profiling-node";
Sentry.init({
dsn: process.env.SENTRY_DSN,
environment: process.env.SENTRY_ENVIRONMENT ?? "production",
release: process.env.SENTRY_RELEASE,
sendDefaultPii: true,
// 追踪 — 在高流量生产环境中降低到 0.1–0.2
tracesSampleRate: 1.0,
// 性能分析(需要 @sentry/profiling-node)
// integrations: [nodeProfilingIntegration()],
// profileSessionSampleRate: 1.0,
// profileLifecycle: "trace",
// 结构化日志(SDK ≥ 9.41.0)
enableLogs: true,
});
基于配置的 Sentry.init(): 如果第一阶段发现了一个类型化的配置类(例如 SentryConfig),则从其中读取选项,而不是使用原始的 process.env。这在使用了 @nestjs/config 或自定义配置加载器的 NestJS 应用中很常见:
import * as Sentry from "@sentry/nestjs";
import { loadConfiguration } from "./config";
const config = loadConfiguration();
Sentry.init({
dsn: config.sentry.dsn,
environment: config.sentry.environment ?? "production",
release: config.sentry.release,
sendDefaultPii: config.sentry.sendDefaultPii ?? true,
tracesSampleRate: config.sentry.tracesSampleRate ?? 1.0,
profileSessionSampleRate: config.sentry.profilesSampleRate ?? 1.0,
profileLifecycle: "trace",
enableLogs: true,
});
当添加新的 SDK 选项(例如 sendDefaultPii、profileSessionSampleRate)时,请将它们添加到配置类型中,以便可以按环境进行配置。
src/main.ts 中首先导入 instrument.ts// instrument.ts 必须是第一个导入 — 在 NestJS 或任何其他模块之前
import "./instrument";
import { NestFactory } from "@nestjs/core";
import { AppModule } from "./app.module";
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// 启用优雅关闭 — 在 SIGTERM/SIGINT 时刷新 Sentry 事件
app.enableShutdownHooks();
await app.listen(3000);
}
bootstrap();
为什么必须是第一个? OpenTelemetry 必须在
http、express、数据库驱动和其他模块加载之前对它们进行猴子补丁。任何在instrument.ts之前加载的模块都不会被自动检测。
src/app.module.ts 中注册 SentryModule 和 SentryGlobalFilterimport { Module } from "@nestjs/common";
import { APP_FILTER } from "@nestjs/core";
import { SentryModule, SentryGlobalFilter } from "@sentry/nestjs/setup";
import { AppController } from "./app.controller";
import { AppService } from "./app.service";
@Module({
imports: [
SentryModule.forRoot(), // 全局注册 SentryTracingInterceptor
],
controllers: [AppController],
providers: [
AppService,
{
provide: APP_FILTER,
useClass: SentryGlobalFilter, // 捕获所有未处理的异常
},
],
})
export class AppModule {}
每个部分的作用:
SentryModule.forRoot() — 将 SentryTracingInterceptor 注册为全局 APP_INTERCEPTOR,启用 HTTP 事务命名SentryGlobalFilter — 扩展 BaseExceptionFilter;捕获 HTTP、GraphQL(重新抛出 HttpException 但不报告)和 RPC 上下文中的异常⚠️ 请勿重复注册
SentryModule.forRoot()。 如果第一阶段发现它已经在一个共享库模块中导入(例如SentryProxyModule或AnalyticsModule),不要在AppModule中再次添加。重复注册会导致每个跨度被拦截两次,使追踪数据膨胀。
⚠️ 两个入口点,不同的导入:
@sentry/nestjs→ SDK 初始化、捕获 API、装饰器(SentryTraced、SentryCron、SentryExceptionCaptured)@sentry/nestjs/setup→ NestJS DI 构造(SentryModule、SentryGlobalFilter)
切勿从
@sentry/nestjs(主入口点)导入SentryModule—— 它会在 OpenTelemetry 修补之前加载@nestjs/common,从而破坏自动检测。
对于 ESM 应用,使用 --import 替代文件导入:
// instrument.mjs
import * as Sentry from "@sentry/nestjs";
Sentry.init({
dsn: process.env.SENTRY_DSN,
tracesSampleRate: 1.0,
});
// package.json
{
"scripts": {
"start": "node --import ./instrument.mjs -r ts-node/register src/main.ts"
}
}
或者通过环境变量:
NODE_OPTIONS="--import ./instrument.mjs" npm run start
选择适合您现有架构的方法:
SentryGlobalFilter(推荐)已在上述步骤 3 中涵盖。这是最简单的选项。
@SentryExceptionCaptured() 装饰器import { Catch, ExceptionFilter, ArgumentsHost } from "@nestjs/common";
import { SentryExceptionCaptured } from "@sentry/nestjs";
@Catch()
export class YourExistingFilter implements ExceptionFilter {
@SentryExceptionCaptured() // 包装 catch() 方法以自动报告异常
catch(exception: unknown, host: ArgumentsHost): void {
// 您现有的错误处理逻辑保持不变
}
}
import { ArgumentsHost, Catch } from "@nestjs/common";
import { BaseExceptionFilter } from "@nestjs/core";
import * as Sentry from "@sentry/nestjs";
@Catch(ExampleException)
export class ExampleExceptionFilter extends BaseExceptionFilter {
catch(exception: ExampleException, host: ArgumentsHost) {
Sentry.captureException(exception);
super.catch(exception, host);
}
}
import { Catch, RpcExceptionFilter, ArgumentsHost } from "@nestjs/common";
import { Observable, throwError } from "rxjs";
import { RpcException } from "@nestjs/microservices";
import * as Sentry from "@sentry/nestjs";
@Catch(RpcException)
export class SentryRpcFilter implements RpcExceptionFilter<RpcException> {
catch(exception: RpcException, host: ArgumentsHost): Observable<any> {
Sentry.captureException(exception);
return throwError(() => exception.getError());
}
}
@SentryTraced(op?) — 检测任何方法import { Injectable } from "@nestjs/common";
import { SentryTraced } from "@sentry/nestjs";
@Injectable()
export class OrderService {
@SentryTraced("order.process")
async processOrder(orderId: string): Promise<void> {
// 自动包装在 Sentry 跨度中
}
@SentryTraced() // 默认为 op: "function"
async fetchInventory() { ... }
}
@SentryCron(slug, config?) — 监控定时作业import { Injectable } from "@nestjs/common";
import { Cron } from "@nestjs/schedule";
import { SentryCron } from "@sentry/nestjs";
@Injectable()
export class ReportService {
@Cron("0 * * * *")
@SentryCron("hourly-report", {
// @SentryCron 必须在 @Cron 之后
schedule: { type: "crontab", value: "0 * * * *" },
checkinMargin: 2, // 标记为错过前的分钟数
maxRuntime: 10, // 最大运行时间(分钟)
timezone: "UTC",
})
async generateReport() {
// 在开始/成功/失败时自动发送签到
}
}
后台作业共享默认的隔离作用域 —— 使用 Sentry.withIsolationScope() 包装以防止交叉污染:
import * as Sentry from "@sentry/nestjs";
import { Injectable } from "@nestjs/common";
import { Cron, CronExpression } from "@nestjs/schedule";
@Injectable()
export class JobService {
@Cron(CronExpression.EVERY_HOUR)
handleCron() {
Sentry.withIsolationScope(() => {
Sentry.setTag("job", "hourly-sync");
this.doWork();
});
}
}
将 withIsolationScope 应用于:@Cron()、@Interval()、@OnEvent()、@Processor() 以及任何在请求生命周期之外的代码。
一些 NestJS 项目为了可测试性和解耦,将 Sentry 封装在依赖注入令牌(例如 SENTRY_PROXY_TOKEN)后面。如果第一阶段检测到这种模式,请使用注入的服务进行所有运行时 Sentry 调用 —— 不要在控制器、服务或处理器中直接导入 @sentry/nestjs。
import { Controller, Inject } from "@nestjs/common";
import { SENTRY_PROXY_TOKEN, type SentryProxyService } from "./sentry-proxy";
@Controller("orders")
export class OrderController {
constructor(
@Inject(SENTRY_PROXY_TOKEN) private readonly sentry: SentryProxyService,
private readonly orderService: OrderService,
) {}
@Post()
async createOrder(@Body() dto: CreateOrderDto) {
return this.sentry.startSpan(
{ name: "createOrder", op: "http" },
async () => this.orderService.create(dto),
);
}
}
在以下情况下直接导入 @sentry/nestjs 仍然是正确的:
instrument.ts — 始终使用 import * as Sentry from "@sentry/nestjs" 进行 Sentry.init()添加一个测试端点来确认事件能到达 Sentry:
import { Controller, Get } from "@nestjs/common";
import * as Sentry from "@sentry/nestjs";
@Controller()
export class DebugController {
@Get("/debug-sentry")
triggerError() {
throw new Error("My first Sentry error from NestJS!");
}
@Get("/debug-sentry-span")
triggerSpan() {
return Sentry.startSpan({ op: "test", name: "NestJS Test Span" }, () => {
return { status: "span created" };
});
}
}
访问 GET /debug-sentry 并在几秒钟内检查 Sentry Issues 仪表板。
逐个功能进行指导。加载参考文件,按照其步骤操作,在继续之前进行验证:
| 功能 | 参考文件 | 何时加载 |
|---|---|---|
| 错误监控 | ${SKILL_ROOT}/references/error-monitoring.md | 始终(基线) |
| 追踪 | ${SKILL_ROOT}/references/tracing.md | 始终(NestJS 路由自动追踪) |
| 性能分析 | ${SKILL_ROOT}/references/profiling.md | CPU 密集型生产应用 |
| 日志记录 | ${SKILL_ROOT}/references/logging.md | 需要结构化日志聚合 |
| 指标 | ${SKILL_ROOT}/references/metrics.md | 自定义 KPI / SLO 跟踪 |
| 定时任务监控 | ${SKILL_ROOT}/references/crons.md | 定时作业或任务队列 |
| AI 监控 | ${SKILL_ROOT}/references/ai-monitoring.md | 检测到 OpenAI/Anthropic/LangChain |
对于每个功能:Read ${SKILL_ROOT}/references/<feature>.md,严格按照步骤操作,验证其是否有效。
Sentry.init() 选项| 选项 | 类型 | 默认值 | 用途 |
|---|---|---|---|
dsn | string | — | 如果为空则禁用 SDK;环境变量:SENTRY_DSN |
environment | string | "production" | 例如 "staging";环境变量:SENTRY_ENVIRONMENT |
release | string | — | 例如 "myapp@1.0.0";环境变量:SENTRY_RELEASE |
sendDefaultPii | boolean | false | 包含 IP 地址和请求头 |
tracesSampleRate | number | — | 事务采样率;undefined 禁用追踪 |
tracesSampler | function | — | 自定义的每事务采样(覆盖采样率) |
tracePropagationTargets | `Array<string | RegExp>` | — |
profileSessionSampleRate | number | — | 持续性能分析会话率(SDK ≥ 10.27.0) |
profileLifecycle | `"trace" | "manual"` | "trace" |
enableLogs | boolean | false | 发送结构化日志到 Sentry(SDK ≥ 9.41.0) |
ignoreErrors | `Array<string | RegExp>` | [] |
ignoreTransactions | `Array<string | RegExp>` | [] |
beforeSend | function | — | 用于修改或丢弃错误事件的钩子 |
beforeSendTransaction | function | — | 用于修改或丢弃事务事件的钩子 |
beforeSendLog | function | — | 用于修改或丢弃日志事件的钩子 |
debug | boolean | false | 详细的 SDK 调试输出 |
maxBreadcrumbs | number | 100 | 每个事件的最大面包屑数量 |
| 变量 | 映射到 | 说明 |
|---|---|---|
SENTRY_DSN | dsn | 如果 init() 未传递 dsn 则使用 |
SENTRY_RELEASE | release | 也可以从 git SHA、Heroku、CircleCI 自动检测 |
SENTRY_ENVIRONMENT | environment | 回退到 "production" |
SENTRY_AUTH_TOKEN | CLI/源映射 | 用于 npx @sentry/wizard@latest -i sourcemaps |
SENTRY_ORG | CLI/源映射 | 组织 slug |
SENTRY_PROJECT | CLI/源映射 | 项目 slug |
当检测到相关包时,这些集成会自动激活 —— 无需 integrations: [...]:
| 自动启用 | 说明 |
|---|---|
httpIntegration | 通过 http/https/fetch 发出的传出 HTTP 调用 |
expressIntegration | Express 适配器(默认 NestJS) |
nestIntegration | NestJS 生命周期(中间件、守卫、管道、拦截器、处理程序) |
onUncaughtExceptionIntegration | 未捕获的异常 |
onUnhandledRejectionIntegration | 未处理的 Promise 拒绝 |
openAIIntegration | OpenAI SDK(当安装时) |
anthropicAIIntegration | Anthropic SDK(当安装时) |
langchainIntegration | LangChain(当安装时) |
graphqlIntegration | GraphQL(当存在 graphql 包时) |
postgresIntegration | pg 驱动 |
mysqlIntegration | mysql / mysql2 |
mongoIntegration | MongoDB / Mongoose |
redisIntegration | ioredis / redis |
| 集成 | 何时添加 | 代码 |
|---|---|---|
nodeProfilingIntegration | 需要性能分析 | import { nodeProfilingIntegration } from "@sentry/profiling-node" |
prismaIntegration | 使用 Prisma ORM | integrations: [Sentry.prismaIntegration()] |
consoleLoggingIntegration | 捕获控制台输出 | integrations: [Sentry.consoleLoggingIntegration()] |
localVariablesIntegration | 在错误中捕获局部变量值 | integrations: [Sentry.localVariablesIntegration()] |
测试 Sentry 是否正在接收事件:
// 添加一个测试端点(在生产环境前移除)
@Get("/debug-sentry")
getError() {
throw new Error("My first Sentry error!");
}
或者在不崩溃的情况下发送测试消息:
import * as Sentry from "@sentry/nestjs";
Sentry.captureMessage("NestJS Sentry SDK test");
如果没有显示任何内容:
Sentry.init() 中设置 debug: true — 将 SDK 内部信息打印到 stdoutSENTRY_DSN 环境变量import "./instrument" 是否是 main.ts 中的第一行SentryModule.forRoot() 已在 AppModule 中导入https://<key>@o<org>.ingest.sentry.io/<project>完成 NestJS 设置后,检查配套的前端是否缺少 Sentry:
ls -d ../frontend ../web ../client ../ui 2>/dev/null
cat ../frontend/package.json ../web/package.json 2>/dev/null \
| grep -E '"react"|"svelte"|"vue"|"next"|"nuxt"'
如果存在未配置 Sentry 的前端,建议相应的技能:
| 检测到的前端 | 建议的技能 |
|---|---|
| Next.js | sentry-nextjs-sdk |
| React | sentry-react-sdk |
| Svelte / SvelteKit | sentry-svelte-sdk |
| Vue / Nuxt | 使用 @sentry/vue — 参见 docs.sentry.io/platforms/javascript/guides/vue/ |
| React Native / Expo | sentry-react-native-sdk |
| 问题 | 解决方案 |
|---|---|
| 事件未出现 | 设置 debug: true,验证 SENTRY_DSN,检查 instrument.ts 是否首先导入 |
| DSN 格式错误 | 格式:https://<key>@o<org>.ingest.sentry.io/<project> |
| 异常未捕获 | 确保 SentryGlobalFilter 通过 AppModule 中的 APP_FILTER 注册 |
| 自动检测不工作 | instrument.ts 必须是 main.ts 中的第一个导入 — 在所有 NestJS 导入之前 |
| 性能分析未启动 | 需要 tracesSampleRate > 0 + profileSessionSampleRate > 0 + 已安装 @sentry/profiling-node |
enableLogs 不工作 | 需要 SDK ≥ 9.41.0 |
| 没有追踪出现 | 验证 tracesSampleRate 是否已设置(不是 undefined) |
| 事务过多 | 降低 tracesSampleRate 或使用 tracesSampler 丢弃健康检查 |
| Fastify + GraphQL 问题 | 已知的边缘情况 — 参见 GitHub #13388;对于 GraphQL 建议使用 Express |
| 后台作业事件混合 | 使用 Sentry.withIsolationScope(() => { ... }) 包装作业体 |
| Prisma 跨度缺失 | 在 Sentry.init() 中添加 integrations: [Sentry.prismaIntegration()] |
| ESM 语法错误 | 设置 registerEsmLoaderHooks: false(禁用 ESM 钩子;同时禁用对 ESM 模块的自动检测) |
SentryModule 破坏检测 | 必须从 @sentry/nestjs/setup 导入,绝不能从 @sentry/nestjs 导入 |
| RPC 异常未捕获 | 添加专用的 SentryRpcExceptionFilter(参见异常过滤器部分的选项 D) |
| WebSocket 异常未捕获 | 在网关的 handleConnection/handleDisconnect 上使用 @SentryExceptionCaptured() |
@SentryCron 未触发 | 装饰器顺序很重要 — @SentryCron 必须在 @Cron 之后 |
| TypeScript 路径别名问题 | 确保 tsconfig.json 中的 paths 已配置,以便 instrument 能从 main.ts 的位置解析 |
import * as Sentry ESLint 错误 | 许多项目禁止命名空间导入。使用命名导入(import { startSpan, captureException } from "@sentry/nestjs")或改用项目的 DI 代理 |
profilesSampleRate 与 profileSessionSampleRate | profilesSampleRate 在 SDK 10.x 中已弃用。改用 profileSessionSampleRate + profileLifecycle: "trace" |
| 每个请求出现重复跨度 | SentryModule.forRoot() 在多个模块中注册。确保只调用一次 — 检查共享/库模块 |
instrument.ts 中配置属性不被识别 | 当使用类型化的配置类时,新的 SDK 选项必须添加到配置类型定义中,并且项目在 TypeScript 识别它们之前需要重新构建 |
| 功能 | 最低 SDK 版本 |
|---|---|
@sentry/nestjs 包 | 8.0.0 |
@SentryTraced 装饰器 | 8.15.0 |
@SentryCron 装饰器 | 8.16.0 |
| 事件发射器自动检测 | 8.39.0 |
SentryGlobalFilter(统一) | 8.40.0 |
Sentry.logger API(enableLogs) | 9.41.0 |
profileSessionSampleRate | 10.27.0 |
| Node.js 要求 | ≥ 18 |
Node.js 用于 ESM --import | ≥ 18.19.0 |
| NestJS 兼容性 | 8.x – 11.x |
每周安装量
169
仓库
GitHub 星标数
82
首次出现
2026年3月4日
安全审计
安装于
codex168
gemini-cli164
cursor164
amp163
cline163
github-copilot163
All Skills > SDK Setup > NestJS SDK
Opinionated wizard that scans your NestJS project and guides you through complete Sentry setup.
@sentry/nestjs or Sentry + NestJSNote: SDK versions and APIs below reflect
@sentry/nestjs10.x (NestJS 8–11 supported). Always verify against docs.sentry.io/platforms/node/guides/nestjs/ before implementing.
Run these commands to understand the project before making recommendations:
# Confirm NestJS project
grep -E '"@nestjs/core"' package.json 2>/dev/null
# Check NestJS version
node -e "console.log(require('./node_modules/@nestjs/core/package.json').version)" 2>/dev/null
# Check existing Sentry
grep -i sentry package.json 2>/dev/null
ls src/instrument.ts 2>/dev/null
grep -r "Sentry.init\|@sentry" src/main.ts src/instrument.ts 2>/dev/null
# Check for existing Sentry DI wrapper (common in enterprise NestJS)
grep -rE "SENTRY.*TOKEN|SentryProxy|SentryService" src/ libs/ 2>/dev/null
# Check for config-class-based init (vs env-var-based)
grep -rE "class SentryConfig|SentryConfig" src/ libs/ 2>/dev/null
# Check if SentryModule.forRoot() is already registered in a shared module
grep -rE "SentryModule\.forRoot|SentryProxyModule" src/ libs/ 2>/dev/null
# Detect HTTP adapter (default is Express)
grep -E "FastifyAdapter|@nestjs/platform-fastify" package.json src/main.ts 2>/dev/null
# Detect GraphQL
grep -E '"@nestjs/graphql"|"apollo-server"' package.json 2>/dev/null
# Detect microservices
grep '"@nestjs/microservices"' package.json 2>/dev/null
# Detect WebSockets
grep -E '"@nestjs/websockets"|"socket.io"' package.json 2>/dev/null
# Detect task queues / scheduled jobs
grep -E '"@nestjs/bull"|"@nestjs/bullmq"|"@nestjs/schedule"|"bullmq"|"bull"' package.json 2>/dev/null
# Detect databases
grep -E '"@prisma/client"|"typeorm"|"mongoose"|"pg"|"mysql2"' package.json 2>/dev/null
# Detect AI libraries
grep -E '"openai"|"@anthropic-ai"|"langchain"|"@langchain"|"@google/generative-ai"|"ai"' package.json 2>/dev/null
# Check for companion frontend
ls -d ../frontend ../web ../client ../ui 2>/dev/null
What to note:
@sentry/nestjs already installed? If yes, check if instrument.ts exists and Sentry.init() is called — may just need feature config.SENTRY_PROXY_TOKEN) for testability. Use the injected proxy for all runtime Sentry calls (startSpan, captureException, withIsolationScope) instead of importing @sentry/nestjs directly in controllers, services, and processors. Only instrument.ts should import @sentry/nestjs directly.Based on what you found, present a concrete proposal. Don't ask open-ended questions — lead with a recommendation:
Always recommended (core coverage):
Recommend when detected:
@sentry/profiling-node)@nestjs/schedule, Bull, or BullMQ detectedRecommendation matrix:
| Feature | Recommend when... | Reference |
|---|---|---|
| Error Monitoring | Always — non-negotiable baseline | ${SKILL_ROOT}/references/error-monitoring.md |
| Tracing | Always — NestJS lifecycle is auto-instrumented | ${SKILL_ROOT}/references/tracing.md |
| Profiling | Production + CPU-sensitive workloads | ${SKILL_ROOT}/references/profiling.md |
| Logging | Always; enhanced for structured log aggregation | ${SKILL_ROOT}/references/logging.md |
| Metrics |
Propose: "I recommend Error Monitoring + Tracing + Logging. Want Profiling, Crons, or AI Monitoring too?"
# Core SDK (always required — includes @sentry/node)
npm install @sentry/nestjs
# With profiling support (optional)
npm install @sentry/nestjs @sentry/profiling-node
⚠️ Do NOT install
@sentry/nodealongside@sentry/nestjs—@sentry/nestjsre-exports everything from@sentry/node. Installing both causes duplicate registration.
NestJS requires a specific three-file initialization pattern because the Sentry SDK must patch Node.js modules (via OpenTelemetry) before NestJS loads them.
Before creating new files , check Phase 1 results:
- If
instrument.tsalready exists → modify it, don't create a new one.- If a config class drives
Sentry.init()→ read options from the config instead of hardcoding env vars.- If a Sentry DI wrapper exists → use it for runtime calls instead of importing
@sentry/nestjsdirectly in services/controllers.
src/instrument.tsimport * as Sentry from "@sentry/nestjs";
// Optional: add profiling
// import { nodeProfilingIntegration } from "@sentry/profiling-node";
Sentry.init({
dsn: process.env.SENTRY_DSN,
environment: process.env.SENTRY_ENVIRONMENT ?? "production",
release: process.env.SENTRY_RELEASE,
sendDefaultPii: true,
// Tracing — lower to 0.1–0.2 in high-traffic production
tracesSampleRate: 1.0,
// Profiling (requires @sentry/profiling-node)
// integrations: [nodeProfilingIntegration()],
// profileSessionSampleRate: 1.0,
// profileLifecycle: "trace",
// Structured logs (SDK ≥ 9.41.0)
enableLogs: true,
});
Config-drivenSentry.init(): If Phase 1 found a typed config class (e.g. SentryConfig), read options from it instead of using raw process.env. This is common in NestJS apps that use @nestjs/config or custom config loaders:
import * as Sentry from "@sentry/nestjs";
import { loadConfiguration } from "./config";
const config = loadConfiguration();
Sentry.init({
dsn: config.sentry.dsn,
environment: config.sentry.environment ?? "production",
release: config.sentry.release,
sendDefaultPii: config.sentry.sendDefaultPii ?? true,
tracesSampleRate: config.sentry.tracesSampleRate ?? 1.0,
profileSessionSampleRate: config.sentry.profilesSampleRate ?? 1.0,
profileLifecycle: "trace",
enableLogs: true,
});
When adding new SDK options (e.g. sendDefaultPii, profileSessionSampleRate), add them to the config type so they can be configured per environment.
instrument.ts FIRST in src/main.ts// instrument.ts MUST be the very first import — before NestJS or any other module
import "./instrument";
import { NestFactory } from "@nestjs/core";
import { AppModule } from "./app.module";
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// Enable graceful shutdown — flushes Sentry events on SIGTERM/SIGINT
app.enableShutdownHooks();
await app.listen(3000);
}
bootstrap();
Why first? OpenTelemetry must monkey-patch
http,express, database drivers, and other modules before they load. Any module that loads beforeinstrument.tswill not be auto-instrumented.
SentryModule and SentryGlobalFilter in src/app.module.tsimport { Module } from "@nestjs/common";
import { APP_FILTER } from "@nestjs/core";
import { SentryModule, SentryGlobalFilter } from "@sentry/nestjs/setup";
import { AppController } from "./app.controller";
import { AppService } from "./app.service";
@Module({
imports: [
SentryModule.forRoot(), // Registers SentryTracingInterceptor globally
],
controllers: [AppController],
providers: [
AppService,
{
provide: APP_FILTER,
useClass: SentryGlobalFilter, // Captures all unhandled exceptions
},
],
})
export class AppModule {}
What each piece does:
SentryModule.forRoot() — registers SentryTracingInterceptor as a global APP_INTERCEPTOR, enabling HTTP transaction namingSentryGlobalFilter — extends BaseExceptionFilter; captures exceptions across HTTP, GraphQL (rethrows HttpException without reporting), and RPC contexts⚠️ Do NOT register
SentryModule.forRoot()twice. If Phase 1 found it already imported in a shared library module (e.g. aSentryProxyModuleorAnalyticsModule), do not add it again inAppModule. Duplicate registration causes every span to be intercepted twice, bloating trace data.
⚠️ Two entrypoints, different imports:
@sentry/nestjs→ SDK init, capture APIs, decorators (SentryTraced,SentryCron,SentryExceptionCaptured)@sentry/nestjs/setup→ NestJS DI constructs (SentryModule,SentryGlobalFilter)
Never import
SentryModulefrom@sentry/nestjs(main entrypoint) — it loads@nestjs/commonbefore OpenTelemetry patches it, breaking auto-instrumentation.
For ESM applications, use --import instead of a file import:
// instrument.mjs
import * as Sentry from "@sentry/nestjs";
Sentry.init({
dsn: process.env.SENTRY_DSN,
tracesSampleRate: 1.0,
});
// package.json
{
"scripts": {
"start": "node --import ./instrument.mjs -r ts-node/register src/main.ts"
}
}
Or via environment:
NODE_OPTIONS="--import ./instrument.mjs" npm run start
Choose the approach that fits your existing architecture:
SentryGlobalFilter (recommended)Already covered in Step 3 above. This is the simplest option.
@SentryExceptionCaptured() decoratorimport { Catch, ExceptionFilter, ArgumentsHost } from "@nestjs/common";
import { SentryExceptionCaptured } from "@sentry/nestjs";
@Catch()
export class YourExistingFilter implements ExceptionFilter {
@SentryExceptionCaptured() // Wraps catch() to auto-report exceptions
catch(exception: unknown, host: ArgumentsHost): void {
// Your existing error handling continues unchanged
}
}
import { ArgumentsHost, Catch } from "@nestjs/common";
import { BaseExceptionFilter } from "@nestjs/core";
import * as Sentry from "@sentry/nestjs";
@Catch(ExampleException)
export class ExampleExceptionFilter extends BaseExceptionFilter {
catch(exception: ExampleException, host: ArgumentsHost) {
Sentry.captureException(exception);
super.catch(exception, host);
}
}
import { Catch, RpcExceptionFilter, ArgumentsHost } from "@nestjs/common";
import { Observable, throwError } from "rxjs";
import { RpcException } from "@nestjs/microservices";
import * as Sentry from "@sentry/nestjs";
@Catch(RpcException)
export class SentryRpcFilter implements RpcExceptionFilter<RpcException> {
catch(exception: RpcException, host: ArgumentsHost): Observable<any> {
Sentry.captureException(exception);
return throwError(() => exception.getError());
}
}
@SentryTraced(op?) — Instrument any methodimport { Injectable } from "@nestjs/common";
import { SentryTraced } from "@sentry/nestjs";
@Injectable()
export class OrderService {
@SentryTraced("order.process")
async processOrder(orderId: string): Promise<void> {
// Automatically wrapped in a Sentry span
}
@SentryTraced() // Defaults to op: "function"
async fetchInventory() { ... }
}
@SentryCron(slug, config?) — Monitor scheduled jobsimport { Injectable } from "@nestjs/common";
import { Cron } from "@nestjs/schedule";
import { SentryCron } from "@sentry/nestjs";
@Injectable()
export class ReportService {
@Cron("0 * * * *")
@SentryCron("hourly-report", {
// @SentryCron must come AFTER @Cron
schedule: { type: "crontab", value: "0 * * * *" },
checkinMargin: 2, // Minutes before marking missed
maxRuntime: 10, // Max runtime in minutes
timezone: "UTC",
})
async generateReport() {
// Check-in sent automatically on start/success/failure
}
}
Background jobs share the default isolation scope — wrap with Sentry.withIsolationScope() to prevent cross-contamination:
import * as Sentry from "@sentry/nestjs";
import { Injectable } from "@nestjs/common";
import { Cron, CronExpression } from "@nestjs/schedule";
@Injectable()
export class JobService {
@Cron(CronExpression.EVERY_HOUR)
handleCron() {
Sentry.withIsolationScope(() => {
Sentry.setTag("job", "hourly-sync");
this.doWork();
});
}
}
Apply withIsolationScope to: @Cron(), @Interval(), @OnEvent(), @Processor(), and any code outside the request lifecycle.
Some NestJS projects wrap Sentry behind a dependency injection token (e.g. SENTRY_PROXY_TOKEN) for testability and decoupling. If Phase 1 detected this pattern, use the injected service for all runtime Sentry calls — do not import @sentry/nestjs directly in controllers, services, or processors.
import { Controller, Inject } from "@nestjs/common";
import { SENTRY_PROXY_TOKEN, type SentryProxyService } from "./sentry-proxy";
@Controller("orders")
export class OrderController {
constructor(
@Inject(SENTRY_PROXY_TOKEN) private readonly sentry: SentryProxyService,
private readonly orderService: OrderService,
) {}
@Post()
async createOrder(@Body() dto: CreateOrderDto) {
return this.sentry.startSpan(
{ name: "createOrder", op: "http" },
async () => this.orderService.create(dto),
);
}
}
Where direct@sentry/nestjs import is still correct:
instrument.ts — always uses import * as Sentry from "@sentry/nestjs" for Sentry.init()Add a test endpoint to confirm events reach Sentry:
import { Controller, Get } from "@nestjs/common";
import * as Sentry from "@sentry/nestjs";
@Controller()
export class DebugController {
@Get("/debug-sentry")
triggerError() {
throw new Error("My first Sentry error from NestJS!");
}
@Get("/debug-sentry-span")
triggerSpan() {
return Sentry.startSpan({ op: "test", name: "NestJS Test Span" }, () => {
return { status: "span created" };
});
}
}
Hit GET /debug-sentry and check the Sentry Issues dashboard within seconds.
Walk through features one at a time. Load the reference, follow its steps, verify before moving on:
| Feature | Reference file | Load when... |
|---|---|---|
| Error Monitoring | ${SKILL_ROOT}/references/error-monitoring.md | Always (baseline) |
| Tracing | ${SKILL_ROOT}/references/tracing.md | Always (NestJS routes are auto-traced) |
| Profiling | ${SKILL_ROOT}/references/profiling.md | CPU-intensive production apps |
| Logging | ${SKILL_ROOT}/references/logging.md | Structured log aggregation needed |
| Metrics | ${SKILL_ROOT}/references/metrics.md |
For each feature: Read ${SKILL_ROOT}/references/<feature>.md, follow steps exactly, verify it works.
Sentry.init() Options| Option | Type | Default | Purpose |
|---|---|---|---|
dsn | string | — | SDK disabled if empty; env: SENTRY_DSN |
environment | string | "production" | e.g., "staging"; env: SENTRY_ENVIRONMENT |
| Variable | Maps to | Notes |
|---|---|---|
SENTRY_DSN | dsn | Used if dsn not passed to init() |
SENTRY_RELEASE | release | Also auto-detected from git SHA, Heroku, CircleCI |
SENTRY_ENVIRONMENT | environment |
These integrations activate automatically when their packages are detected — no integrations: [...] needed:
| Auto-enabled | Notes |
|---|---|
httpIntegration | Outgoing HTTP calls via http/https/fetch |
expressIntegration | Express adapter (default NestJS) |
nestIntegration | NestJS lifecycle (middleware, guards, pipes, interceptors, handlers) |
onUncaughtExceptionIntegration | Uncaught exceptions |
| Integration | When to add | Code |
|---|---|---|
nodeProfilingIntegration | Profiling desired | import { nodeProfilingIntegration } from "@sentry/profiling-node" |
prismaIntegration | Prisma ORM used | integrations: [Sentry.prismaIntegration()] |
consoleLoggingIntegration | Capture console output | integrations: [Sentry.consoleLoggingIntegration()] |
Test that Sentry is receiving events:
// Add a test endpoint (remove before production)
@Get("/debug-sentry")
getError() {
throw new Error("My first Sentry error!");
}
Or send a test message without crashing:
import * as Sentry from "@sentry/nestjs";
Sentry.captureMessage("NestJS Sentry SDK test");
If nothing appears:
debug: true in Sentry.init() — prints SDK internals to stdoutSENTRY_DSN env var is set in the running processimport "./instrument" is the first line in main.tsSentryModule.forRoot() is imported in AppModulehttps://<key>@o<org>.ingest.sentry.io/<project>After completing NestJS setup, check for a companion frontend missing Sentry:
ls -d ../frontend ../web ../client ../ui 2>/dev/null
cat ../frontend/package.json ../web/package.json 2>/dev/null \
| grep -E '"react"|"svelte"|"vue"|"next"|"nuxt"'
If a frontend exists without Sentry, suggest the matching skill:
| Frontend detected | Suggest skill |
|---|---|
| Next.js | sentry-nextjs-sdk |
| React | sentry-react-sdk |
| Svelte / SvelteKit | sentry-svelte-sdk |
| Vue / Nuxt | Use @sentry/vue — see docs.sentry.io/platforms/javascript/guides/vue/ |
| React Native / Expo | sentry-react-native-sdk |
| Issue | Solution |
|---|---|
| Events not appearing | Set debug: true, verify SENTRY_DSN, check instrument.ts is imported first |
| Malformed DSN error | Format: https://<key>@o<org>.ingest.sentry.io/<project> |
| Exceptions not captured | Ensure SentryGlobalFilter is registered via APP_FILTER in AppModule |
| Auto-instrumentation not working |
| Feature | Minimum SDK Version |
|---|---|
@sentry/nestjs package | 8.0.0 |
@SentryTraced decorator | 8.15.0 |
@SentryCron decorator | 8.16.0 |
| Event Emitter auto-instrumentation | 8.39.0 |
SentryGlobalFilter (unified) | 8.40.0 |
Sentry.logger API (enableLogs) | 9.41.0 |
Weekly Installs
169
Repository
GitHub Stars
82
First Seen
Mar 4, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
codex168
gemini-cli164
cursor164
amp163
cline163
github-copilot163
Convex性能审计指南 - 诊断修复Convex应用性能问题与优化方案
16,300 周安装
Sentry.init() options (e.g. loaded from YAML or @nestjs/config). Any new SDK options must be added to the config type — do not hardcode values that should be configurable per environment.SentryModule.forRoot() already registered? → If it's in a shared module (e.g. a Sentry proxy module), do not add it again in AppModule — this causes duplicate interceptor registration.SentryGlobalFilter handles it natively.@nestjs/schedule? → Recommend crons.prismaIntegration().| Custom business KPIs or SLO tracking |
${SKILL_ROOT}/references/metrics.md |
| Crons | @nestjs/schedule, Bull, or BullMQ detected | ${SKILL_ROOT}/references/crons.md |
| AI Monitoring | OpenAI/Anthropic/LangChain/etc. detected | ${SKILL_ROOT}/references/ai-monitoring.md |
| Custom KPIs / SLO tracking |
| Crons | ${SKILL_ROOT}/references/crons.md | Scheduled jobs or task queues |
| AI Monitoring | ${SKILL_ROOT}/references/ai-monitoring.md | OpenAI/Anthropic/LangChain detected |
release | string | — | e.g., "myapp@1.0.0"; env: SENTRY_RELEASE |
sendDefaultPii | boolean | false | Include IP addresses and request headers |
tracesSampleRate | number | — | Transaction sample rate; undefined disables tracing |
tracesSampler | function | — | Custom per-transaction sampling (overrides rate) |
tracePropagationTargets | `Array<string | RegExp>` | — |
profileSessionSampleRate | number | — | Continuous profiling session rate (SDK ≥ 10.27.0) |
profileLifecycle | `"trace" | "manual"` | "trace" |
enableLogs | boolean | false | Send structured logs to Sentry (SDK ≥ 9.41.0) |
ignoreErrors | `Array<string | RegExp>` | [] |
ignoreTransactions | `Array<string | RegExp>` | [] |
beforeSend | function | — | Hook to mutate or drop error events |
beforeSendTransaction | function | — | Hook to mutate or drop transaction events |
beforeSendLog | function | — | Hook to mutate or drop log events |
debug | boolean | false | Verbose SDK debug output |
maxBreadcrumbs | number | 100 | Max breadcrumbs per event |
Falls back to "production" |
SENTRY_AUTH_TOKEN | CLI/source maps | For npx @sentry/wizard@latest -i sourcemaps |
SENTRY_ORG | CLI/source maps | Organization slug |
SENTRY_PROJECT | CLI/source maps | Project slug |
onUnhandledRejectionIntegration |
| Unhandled promise rejections |
openAIIntegration | OpenAI SDK (when installed) |
anthropicAIIntegration | Anthropic SDK (when installed) |
langchainIntegration | LangChain (when installed) |
graphqlIntegration | GraphQL (when graphql package present) |
postgresIntegration | pg driver |
mysqlIntegration | mysql / mysql2 |
mongoIntegration | MongoDB / Mongoose |
redisIntegration | ioredis / redis |
localVariablesIntegration | Capture local var values in errors | integrations: [Sentry.localVariablesIntegration()] |
instrument.ts must be the first import in main.ts — before all NestJS imports |
| Profiling not starting | Requires tracesSampleRate > 0 + profileSessionSampleRate > 0 + @sentry/profiling-node installed |
enableLogs not working | Requires SDK ≥ 9.41.0 |
| No traces appearing | Verify tracesSampleRate is set (not undefined) |
| Too many transactions | Lower tracesSampleRate or use tracesSampler to drop health checks |
| Fastify + GraphQL issues | Known edge cases — see GitHub #13388; prefer Express for GraphQL |
| Background job events mixed | Wrap job body in Sentry.withIsolationScope(() => { ... }) |
| Prisma spans missing | Add integrations: [Sentry.prismaIntegration()] to Sentry.init() |
| ESM syntax errors | Set registerEsmLoaderHooks: false (disables ESM hooks; also disables auto-instrumentation for ESM modules) |
SentryModule breaks instrumentation | Must import from @sentry/nestjs/setup, never from @sentry/nestjs |
| RPC exceptions not captured | Add dedicated SentryRpcExceptionFilter (see Option D in exception filter section) |
| WebSocket exceptions not captured | Use @SentryExceptionCaptured() on gateway handleConnection/handleDisconnect |
@SentryCron not triggering | Decorator order matters — @SentryCron MUST come after @Cron |
| TypeScript path alias issues | Ensure tsconfig.json paths are configured so instrument resolves from main.ts location |
import * as Sentry ESLint error | Many projects ban namespace imports. Use named imports (import { startSpan, captureException } from "@sentry/nestjs") or use the project's DI proxy instead |
profilesSampleRate vs profileSessionSampleRate | profilesSampleRate is deprecated in SDK 10.x. Use profileSessionSampleRate + profileLifecycle: "trace" instead |
| Duplicate spans on every request | SentryModule.forRoot() registered in multiple modules. Ensure it's only called once — check shared/library modules |
Config property not recognized in instrument.ts | When using a typed config class, new SDK options must be added to the config type definition and the project rebuilt before TypeScript recognizes them |
profileSessionSampleRate | 10.27.0 |
| Node.js requirement | ≥ 18 |
Node.js for ESM --import | ≥ 18.19.0 |
| NestJS compatibility | 8.x – 11.x |