NestJS 过滤器实战:从入门到精通,打造高效日志处理流
前言:为什么我们需要过滤器?
核心概念:NestJS 过滤器的基本原理
实战演练:构建自定义过滤器
1. 创建过滤器类
2. 使用过滤器
3. 扩展过滤器功能
进阶应用:结合日志库和监控平台
1. 集成 Winston 日志库
2. 集成 Sentry 监控平台
总结:过滤器,你值得拥有!
前言:为什么我们需要过滤器?
兄弟们,咱们在开发过程中,是不是经常遇到各种各样的异常情况?接口请求失败、数据库连接超时、第三方服务挂掉……这些问题,如果不妥善处理,轻则影响用户体验,重则导致整个系统崩溃。而 NestJS 的过滤器(Filters),就是咱们处理这些异常的“救火队员”。
想象一下,你正在开发一个电商网站,用户下单时,突然数据库连接断开了。如果没有过滤器,你可能需要在每个涉及到数据库操作的地方,都写上一大堆 try...catch
代码,不仅代码冗余,而且容易遗漏。有了过滤器,你就可以在一个地方集中处理所有数据库相关的异常,代码更简洁,也更易于维护。
更重要的是,过滤器可以帮助我们更好地记录和分析日志。通过捕获异常信息,我们可以更清晰地了解系统运行状况,更快地定位和解决问题。这对于咱们技术团队来说,简直就是“排雷神器”啊!
核心概念:NestJS 过滤器的基本原理
NestJS 的过滤器,本质上就是一个类,它实现了 ExceptionFilter
接口。这个接口定义了一个 catch
方法,用于捕获和处理异常。当 NestJS 应用抛出异常时,过滤器就会自动“接管”,执行 catch
方法中的逻辑。
import { Catch, ExceptionFilter, ArgumentsHost, HttpException } from '@nestjs/common'; @Catch(HttpException) // 指定捕获的异常类型 export class HttpExceptionFilter implements ExceptionFilter { catch(exception: HttpException, host: ArgumentsHost) { const ctx = host.switchToHttp(); const response = ctx.getResponse(); const request = ctx.getRequest(); const status = exception.getStatus(); response .status(status) .json({ statusCode: status, timestamp: new Date().toISOString(), path: request.url, message: exception.message }); } }
上面这段代码,就是一个简单的 HTTP 异常过滤器。它捕获所有 HttpException
类型的异常,并将异常信息以 JSON 格式返回给客户端。咱们可以看到,catch
方法接收两个参数:
exception
: 当前捕获的异常对象。host
: 一个ArgumentsHost
对象,它提供了访问请求和响应对象的方法。
通过 host.switchToHttp()
,我们可以获取到 HTTP 请求的上下文,进而获取请求和响应对象。然后,我们就可以根据异常信息,自定义响应内容。
实战演练:构建自定义过滤器
了解了基本原理,咱们就可以动手构建自己的过滤器了。下面,我将带大家一步步实现一个更强大的自定义过滤器,它可以处理各种类型的异常,并提供更详细的日志记录。
1. 创建过滤器类
首先,创建一个名为 all-exceptions.filter.ts
的文件,并添加以下代码:
import { Catch, ExceptionFilter, ArgumentsHost, HttpException, HttpStatus } from '@nestjs/common'; import { Logger } from '@nestjs/common'; @Catch() // 不指定异常类型,捕获所有异常 export class AllExceptionsFilter implements ExceptionFilter { private readonly logger = new Logger(AllExceptionsFilter.name); catch(exception: unknown, host: ArgumentsHost) { const ctx = host.switchToHttp(); const response = ctx.getResponse(); const request = ctx.getRequest(); const status = exception instanceof HttpException ? exception.getStatus() : HttpStatus.INTERNAL_SERVER_ERROR; const message = exception instanceof HttpException ? exception.message : 'Internal server error'; this.logger.error(`[${request.method}] ${request.url} - ${status} - ${message}`); response.status(status).json({ statusCode: status, timestamp: new Date().toISOString(), path: request.url, message: message, }); } }
在这个过滤器中,我们做了一些改进:
@Catch()
不指定异常类型,表示捕获所有类型的异常。- 引入了
Logger
,用于记录异常日志。 - 判断异常类型,如果是
HttpException
,则获取其状态码和消息;否则,统一返回 500 状态码和 “Internal server error” 消息。 - 记录详细的日志信息,包括请求方法、URL、状态码和错误消息。
2. 使用过滤器
创建好过滤器后,我们需要将其应用到 NestJS 应用中。有三种方式可以应用过滤器:
全局范围: 在
main.ts
文件中,使用app.useGlobalFilters()
方法。import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import { AllExceptionsFilter } from './all-exceptions.filter'; async function bootstrap() { const app = await NestFactory.create(AppModule); app.useGlobalFilters(new AllExceptionsFilter()); // 应用全局过滤器 await app.listen(3000); } bootstrap(); 控制器范围: 在控制器类上使用
@UseFilters()
装饰器。import { Controller, Get, UseFilters } from '@nestjs/common'; import { AllExceptionsFilter } from './all-exceptions.filter'; @Controller('cats') @UseFilters(AllExceptionsFilter) // 应用控制器范围过滤器 export class CatsController { @Get() findAll() { throw new Error('This is a test error'); } } 方法范围: 在控制器方法上使用
@UseFilters()
装饰器。import { Controller, Get, UseFilters } from '@nestjs/common'; import { AllExceptionsFilter } from './all-exceptions.filter'; @Controller('cats') export class CatsController { @Get() @UseFilters(AllExceptionsFilter) // 应用方法范围过滤器 findAll() { throw new Error('This is a test error'); } }
根据实际需求,选择合适的应用范围。一般来说,全局范围的过滤器适用于处理所有未捕获的异常,而控制器或方法范围的过滤器适用于处理特定模块或接口的异常。
3. 扩展过滤器功能
咱们的自定义过滤器已经具备了基本的功能,但还可以进一步扩展,以满足更复杂的需求。例如:
- 自定义日志格式: 可以根据需要,自定义日志的格式,例如添加请求 ID、用户 ID 等信息。
- 异常分类: 可以根据异常类型,将异常分为不同的类别,例如数据库异常、网络异常、业务异常等,并分别处理。
- 异常通知: 可以将异常信息发送到邮件、短信、Slack 等渠道,及时通知相关人员。
- 异常重试: 对于某些可恢复的异常,可以尝试自动重试。
进阶应用:结合日志库和监控平台
为了更好地管理和分析日志,我们可以将 NestJS 的过滤器与第三方日志库和监控平台结合使用。
1. 集成 Winston 日志库
Winston 是一个流行的 Node.js 日志库,它提供了丰富的功能,例如多级别日志、多种日志输出方式(控制台、文件、数据库等)、自定义日志格式等。
首先,安装 Winston:
npm install winston
然后,修改 all-exceptions.filter.ts
文件,使用 Winston 记录日志:
import { Catch, ExceptionFilter, ArgumentsHost, HttpException, HttpStatus } from '@nestjs/common'; import * as winston from 'winston'; @Catch() export class AllExceptionsFilter implements ExceptionFilter { private readonly logger = winston.createLogger({ level: 'error', // 设置日志级别 format: winston.format.combine( winston.format.timestamp(), winston.format.json() ), // 设置日志格式 transports: [ new winston.transports.Console(), // 输出到控制台 new winston.transports.File({ filename: 'error.log' }), // 输出到文件 ], }); catch(exception: unknown, host: ArgumentsHost) { // ... 其他代码 ... this.logger.error({ method: request.method, url: request.url, status: status, message: message, stack: exception instanceof Error ? exception.stack : null, }); // ... 其他代码 ... } }
在这个例子中,我们创建了一个 Winston logger,设置日志级别为 error
,日志格式为 JSON,并将日志输出到控制台和 error.log
文件。
2. 集成 Sentry 监控平台
Sentry 是一个流行的错误跟踪和监控平台,它可以帮助我们实时监控应用的异常情况,并提供详细的错误报告、性能分析等功能。
首先,在 Sentry 官网注册账号并创建一个项目,获取 DSN(Data Source Name)。
然后,安装 Sentry 的 NestJS SDK:
npm install @sentry/node @sentry/tracing
接着,在 main.ts
文件中初始化 Sentry:
import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import { AllExceptionsFilter } from './all-exceptions.filter'; import * as Sentry from '@sentry/node'; import * as Tracing from '@sentry/tracing'; async function bootstrap() { Sentry.init({ dsn: 'YOUR_SENTRY_DSN', // 替换为你的 DSN integrations: [ new Sentry.Integrations.Http({ tracing: true }), new Tracing.Integrations.Express({}), ], tracesSampleRate: 1.0, }); const app = await NestFactory.create(AppModule); app.use(Sentry.Handlers.requestHandler()); app.use(Sentry.Handlers.tracingHandler()); app.useGlobalFilters(new AllExceptionsFilter()); app.use(Sentry.Handlers.errorHandler()); await app.listen(3000); } bootstrap();
最后,修改 all-exceptions.filter.ts
文件,将异常信息发送到 Sentry:
import { Catch, ExceptionFilter, ArgumentsHost, HttpException, HttpStatus } from '@nestjs/common'; import * as Sentry from '@sentry/node'; @Catch() export class AllExceptionsFilter implements ExceptionFilter { catch(exception: unknown, host: ArgumentsHost) { // ... 其他代码 ... Sentry.captureException(exception); // 发送异常到 Sentry // ... 其他代码 ... } }
现在,当应用发生异常时,Sentry 就会自动捕获并记录异常信息,你可以在 Sentry 平台上查看详细的错误报告和性能分析。
总结:过滤器,你值得拥有!
兄弟们,NestJS 的过滤器,是不是很强大?通过合理使用过滤器,我们可以更好地处理异常、记录日志、监控应用,从而提高系统的稳定性和可靠性。希望这篇文章能帮助大家更好地理解和使用 NestJS 过滤器,打造更健壮的应用!
如果你还有其他关于 NestJS 的问题,欢迎随时交流!