You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NestJS+AWS Lambda集成Sentry:部分错误未稳定上报问题求助

问题:AWS Lambda上NestJS项目Sentry错误上报不稳定

我基于NestJS创建了一个极简项目,仅包含一个抛出错误的控制器。同时自定义了Sentry拦截器、SentryService,并在模块中完成配置。将项目部署到AWS Lambda并关联API网关后,连续发起10次请求时,日志每次都显示sending error to sentry account,但Sentry后台仅能收到3-9条错误(每次测试数量不同)。本地非Lambda环境下所有错误均能正常上报,尝试添加Sentry.flush(2000)及使用第三方NestJS-Sentry集成模块后问题依旧。推测是Lambda执行结束前Sentry未完成错误发送,请问如何实现稳定上报?


项目代码

控制器代码

@Controller
export class TestAppController {

  @Get()
  doSmth() {
     throw new Error('Error just happened')
  }
}

Sentry拦截器代码

@Injectable()
export class SentryApiInterceptor implements NestInterceptor {
  constructor(private sentryService: SentryService) {}

  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    const request = context.switchToHttp().getRequest();

    const span = this.sentryService.getRequestSpan(request, {
      op: `Route Handler`,
    });

    return next.handle().pipe(
      catchError((error) => {
        const skipCapture =
          error instanceof HttpException && error.getStatus() < 500;
        if (!skipCapture) {
          console.log(`sending error to sentry account`);
          Sentry.captureException(error, span.getTraceContext());
        }
        return throwError(() => error);
      }),
      finalize(async () => {
        span.finish();
      }),
    );
  }
}

SentryService代码

@Injectable()
export class SentryService {
  get span(): Span {
    return Sentry.getCurrentHub().getScope().getSpan();
  }

  public getRequestSpan(request: Request, spanContext: SpanContext) {
    const { method, headers, url } = request;

    const transaction = Sentry.startTransaction({
      name: `Route: ${method} ${url}`,
      op: 'transaction',
    });

    Sentry.getCurrentHub().configureScope((scope) => {
      scope.setSpan(transaction);

      scope.setContext('http', {
        method,
        url,
        headers,
      });
    });

    const span = Sentry.getCurrentHub().getScope().getSpan();

    span.startChild(spanContext);

    return span;
  }

  startChild(spanContext: SpanContext) {
    return this.span.startChild(spanContext);
  }
}

模块配置代码

@Module({
  providers: [
    SentryService,
    {
       provide: APP_INTERCEPTOR,
       useClass: SentryApiInterceptor,
    },
  ],
  controllers: [TestAppController],
})
export class TestAppModule {}

Lambda处理函数代码

let cachedServer: Server;

Sentry.init({
  dsn: 'my-dsn',
  debug: true,
});

async function bootstrapServer(): Promise<Server> {
  if (!cachedServer) {
    const expressApp = express();
    const nestApp = await NestFactory.create(
      TestAppModule,
      new ExpressAdapter(expressApp),
    );
    nestApp.use(eventContext());
    await nestApp.init();
    cachedServer = createServer(expressApp, undefined, undefined);
  }
  return cachedServer;
}

export const handler: Handler = async (event: any, context: Context) => {
  cachedServer = await bootstrapServer();
  return proxy(cachedServer, event, context, 'PROMISE').promise;
};

解决方案

核心问题分析

AWS Lambda的执行环境是短暂且可能复用的,Sentry默认采用异步上报机制,Lambda执行结束时会直接冻结进程,导致未完成的上报请求被中断。即使调用Sentry.flush(),如果时机不对或未等待其完成,依然会丢失上报数据。

具体修正步骤

1. 错误捕获后等待上报完成

修改拦截器的catchError逻辑,将回调改为异步函数,在捕获错误并调用captureException后,等待Sentry.flush()完成再抛出错误:

catchError(async (error) => {
  const skipCapture = error instanceof HttpException && error.getStatus() < 500;
  if (!skipCapture) {
    console.log(`sending error to sentry account`);
    Sentry.captureException(error, span.getTraceContext());
    // 设置足够长的超时时间,确保上报完成
    await Sentry.flush(5000);
  }
  return throwError(() => error);
}),

2. 清理并重置Sentry上下文

Lambda会复用执行环境,当前的事务创建逻辑可能导致上下文污染。在拦截器中添加Scope的推送与清理:

intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
  const request = context.switchToHttp().getRequest();
  // 推送新的Scope,隔离当前请求上下文
  Sentry.getCurrentHub().pushScope();

  const span = this.sentryService.getRequestSpan(request, {
    op: `Route Handler`,
  });

  return next.handle().pipe(
    // ... 原有catchError逻辑
    finalize(async () => {
      span.finish();
      await Sentry.flush(5000);
      // 弹出当前Scope,恢复到之前的状态
      Sentry.getCurrentHub().popScope();
    }),
  );
}

3. 调整Lambda handler的事件循环配置

在handler中显式设置callbackWaitsForEmptyEventLoop为true,确保Lambda等待所有异步操作完成后再结束:

export const handler: Handler = async (event: any, context: Context) => {
  context.callbackWaitsForEmptyEventLoop = true;
  cachedServer = await bootstrapServer();
  const result = await proxy(cachedServer, event, context, 'PROMISE').promise;
  // 额外执行一次flush,兜底确保所有上报完成
  await Sentry.flush(5000);
  return result;
};

4. 优化Sentry初始化配置

针对Lambda环境调整初始化参数,开启会话追踪并关闭不必要的调试:

Sentry.init({
  dsn: 'my-dsn',
  debug: process.env.NODE_ENV === 'development',
  autoSessionTracking: true,
  tracesSampleRate: 1.0,
  environment: process.env.NODE_ENV || 'production',
});

验证方法

连续发起20次以上请求,检查Sentry后台是否能接收所有错误日志,同时查看Lambda日志确认每次请求都执行了Sentry.flush()操作。

内容的提问来源于stack exchange,提问作者Tarmo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 11:47:09