NestJS:异常抛出时如何执行Interceptor中的事件追踪
解决方案:异常场景下触发NestJS拦截器的事件追踪
这是NestJS里很典型的拦截器与全局异常过滤器执行顺序问题——当异常抛出时,全局过滤器会优先捕获并直接返回响应,跳过了拦截器中tap这类仅在流成功完成时才执行的逻辑。下面给你几个可行的解决思路,按推荐程度排序:
方案1:用RxJS的catchError + tap统一处理成功/失败追踪
修改你的EventsTrackerInterceptor,在流中加入catchError操作符来捕获异常,先记录错误追踪信息,再重新抛出异常让全局过滤器处理;同时保留tap处理正常成功的情况。这样无论成功还是失败,追踪逻辑都会被触发。
示例代码:
// events-tracker.interceptor.ts import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common'; import { Observable, throwError } from 'rxjs'; import { tap, catchError } from 'rxjs/operators'; import { EventTrackerService } from './event-tracker.service'; @Injectable() export class EventsTrackerInterceptor implements NestInterceptor { constructor(private readonly eventTrackerService: EventTrackerService) {} intercept(context: ExecutionContext, next: CallHandler): Observable<any> { // 提前获取类名和handler名称 const handlerName = context.getHandler().name; const className = context.getClass().name; return next.handle().pipe( // 正常响应时记录成功事件 tap((response) => { this.eventTrackerService.track({ className, handlerName, status: 'success', responseData: response, timestamp: new Date(), }); }), // 捕获异常时记录错误事件,再重新抛出给全局过滤器 catchError((error) => { this.eventTrackerService.track({ className, handlerName, status: 'error', errorMessage: error.message, timestamp: new Date(), }); // 必须重新抛出异常,否则全局过滤器无法捕获 return throwError(() => error); }), ); } }
这个方案的优势是:
- 成功和失败的追踪逻辑都集中在拦截器里,便于统一维护
- 完全不影响原有全局异常过滤器的功能
- 能拿到完整的响应/错误数据
方案2:在全局异常过滤器中补充追踪逻辑
如果不想修改拦截器,也可以直接在AllExceptionsFilter中注入EventTrackerService,在处理异常前先记录追踪信息。
示例代码:
// all-exception.filter.ts import { Catch, ExceptionFilter, ArgumentsHost, HttpException, HttpStatus } from '@nestjs/common'; import { EventTrackerService } from './event-tracker.service'; @Catch() export class AllExceptionsFilter implements ExceptionFilter { constructor(private readonly eventTrackerService: EventTrackerService) {} catch(exception: unknown, host: ArgumentsHost) { const ctx = host.switchToHttp(); const request = ctx.getRequest(); // 获取类名和handler名称 const handlerName = host.getHandler().name; const className = host.getClass().name; // 记录异常追踪 this.eventTrackerService.track({ className, handlerName, status: 'error', errorMessage: exception instanceof Error ? exception.message : 'Unknown error', path: request.url, timestamp: new Date(), }); // 原有标准化错误响应逻辑 const status = exception instanceof HttpException ? exception.getStatus() : HttpStatus.INTERNAL_SERVER_ERROR; ctx.getResponse().status(status).json({ statusCode: status, timestamp: new Date().toISOString(), path: request.url, message: exception instanceof Error ? exception.message : 'Internal server error', }); } }
这个方案的注意点:
- 需要确保
EventTrackerService在过滤器所在模块中可注入 - 成功追踪在拦截器,失败追踪在过滤器,逻辑分散,后续维护成本略高
- 如果有局部异常过滤器,需要重复添加追踪逻辑
方案3:用finalize操作符统一收尾
如果你不需要区分成功/失败的详细数据,只想记录“调用完成”事件,可以用RxJS的finalize操作符——它会在流无论成功还是错误终止时都执行。
示例代码:
// events-tracker.interceptor.ts import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common'; import { Observable } from 'rxjs'; import { finalize } from 'rxjs/operators'; import { EventTrackerService } from './event-tracker.service'; @Injectable() export class EventsTrackerInterceptor implements NestInterceptor { constructor(private readonly eventTrackerService: EventTrackerService) {} intercept(context: ExecutionContext, next: CallHandler): Observable<any> { const handlerName = context.getHandler().name; const className = context.getClass().name; const response = context.switchToHttp().getResponse(); return next.handle().pipe( finalize(() => { // 通过响应状态码判断成功/失败 const status = response.statusCode; this.eventTrackerService.track({ className, handlerName, status: status >= 200 && status < 300 ? 'success' : 'error', timestamp: new Date(), }); }), ); } }
这个方案的局限性是:无法直接拿到响应体或错误详情,适合只需要记录调用动作的场景。
内容的提问来源于stack exchange,提问作者Neeraj
相关产品推荐
相关产品推荐

