当前位置: 首页 > news >正文

NestJS——日志、NestJS-logger、pino、winston、全局异常过滤器

个人简介

👀个人主页: 前端杂货铺
🙋‍♂️学习方向: 主攻前端方向,正逐渐往全干发展
📃个人状态: 研发工程师,现效力于中国工业软件事业
🚀人生格言: 积跬步至千里,积小流成江海
🥇推荐学习:🍍前端面试宝典 🎨100个小功能 🍉Vue2 🍋Vue3 🍓Vue2/3项目实战 🥝Node.js实战 🍒Three.js

🌕个人推广:每篇文章最下方都有加入方式,旨在交流学习&资源分享,快加入进来吧

内容参考链接
NestJS(一)Docker入门
NestJS(二)NestJS——创建项目、编写User模块
NestJS(三)TypeScript入门
NestJS(四)编程思想——FP、OOP、FRP、AOP、IOC、DI、MVC、DTO、DAO
NestJS(五)NestJS——多环境配置方案(dotenv、config、@nestjs/config、joi配置校验)
NestJS(六)NestJS——使用TypeORM连接MySQL数据库(Docker拉取镜像、多环境适配)
NestJS(七)NestJS——使用TypeORM操作数据库、增删改查、关联查询、QueryBuilder

文章目录

    • 日志
      • 日志等级
      • 功能分类日志
      • 日志记录位置
    • NestJS - logger
    • 第三方日志模块(pino VS winston)
      • pino
      • winston
    • 全局异常过滤器
    • 总结

日志

日志等级

  • Log:通用日志,按需记录
  • Warning:警告日志,比如:尝试多次进行数据库操作
  • Error:严重日志,比如:数据库异常
  • Debug:调试日志,比如:加载数据日志
  • Verbose:详细日志,所有的操作与详细信息(非必要不打印)

功能分类日志

  • 错误日志:方便定位问题,给用户友好的提示
  • 调试日志:方便开发
  • 请求日志:记录敏感行为

日志记录位置

  • 控制台日志:方便监看(调试用)
  • 文件日志:方便回溯与追踪(24小时滚动)
  • 数据库日志:敏感操作、敏感数据记录

在这里插入图片描述

NestJS - logger

GitHub 提交记录

接下来,我们使用 @nestjs/common 中的 logger 来进行简单的日志打印。

修改 app.module.tslogging: false,以关闭 typeorm 的日志。

main.ts 中修改代码如下。

import { NestFactory } from "@nestjs/core"; // 导入 NestFactory,用于创建 Nest 应用实例
import { AppModule } from "./app.module"; // 导入应用的根模块
import { Logger } from "@nestjs/common"; // 导入 Logger,用于日志记录/*** 应用程序的入口文件* - 使用 NestFactory 创建应用实例* - 配置全局设置并启动应用*/
async function bootstrap() {const logger = new Logger(); // 创建 Logger 实例,用于记录日志// 创建应用实例,并可选配置日志级别const app = await NestFactory.create(AppModule, {// 日志级别配置(可选)// logger: ["error", "warn"], // 仅记录错误和警告日志});// 设置全局路由前缀app.setGlobalPrefix("api"); // 所有路由将以 "api" 为前缀,例如 /api/userconst port = 3000; // 定义应用监听的端口号// 启动应用并监听指定端口await app.listen(port);// 使用 Logger 记录应用启动信息logger.log(`App运行在:${port}`); // 普通日志logger.warn(`App运行在:${port}`); // 警告日志logger.error(`App运行在:${port}`); // 错误日志
}// 启动应用
bootstrap();

在这里插入图片描述

GitHub 提交记录

在模块中使用 logger 也非常简单。接下来,我们修改 user.controller.ts 文件,以下是主要修改内容。

@Controller("user") // 定义控制器的路由前缀为 "user"
export class UserController {private logger = new Logger(UserController.name); // 创建 Logger 实例,用于记录日志/*** 构造函数* - 注入用户服务* @param userService 用户服务*/constructor(private userService: UserService) {this.logger.log("UserController initialized"); // 控制器初始化时记录日志}/*** 获取所有用户* - 路由:GET /user/getAll* @returns 所有用户的列表*/@Get("getAll")getUsers(): any {this.logger.log("请求 getAll 成功"); // 记录获取所有用户的日志return this.userService.findAll(); // 调用服务层方法查询所有用户}
}

在这里插入图片描述


第三方日志模块(pino VS winston)

pino

pinio官网

// 安装 pino
pnpm install nestjs-pino// 安装 pino-pretty 便于直观显示
pnpm i pino-pretty// 安装 pino-roll,日志文件滚动
pnpm i pino-roll 

GitHub 提交记录

app.module.ts 中添加对日志模块的配置

import { LoggerModule } from "nestjs-pino";
import { join } from "path";...// 配置日志模块LoggerModule.forRoot({pinoHttp: {transport: {targets: [process.env.NODE_ENV === "development"? {level: "info", // 日志级别为 infotarget: "pino-pretty", // 使用 pino-pretty 格式化日志options: {colorize: true, // 启用日志颜色},}: {level: "info", // 日志级别为 infotarget: "pino-roll", // 使用 pino-roll 将日志写入文件options: {file: join("log", "log.txt"), // 日志文件路径frequency: "daily", // 日志文件按天滚动size: "10M", // 每个日志文件的最大大小为 10MBmkdir: true, // 如果目录不存在,则自动创建},},],},},}),...

修改 user.controller.ts 中对 pino 的使用。

import { Controller, Get, Post } from "@nestjs/common"; // 导入控制器和 HTTP 请求装饰器
import { UserService } from "./user.service"; // 导入用户服务
import { User } from "./user.entity"; // 导入用户实体
import { Logger } from "nestjs-pino";@Controller("user") // 定义控制器的路由前缀为 "user"
export class UserController {/*** 构造函数* - 注入用户服务* @param userService 用户服务*/constructor(private userService: UserService,private logger: Logger) {this.logger.log("UserController initialized"); // 控制器初始化时记录日志}/*** 获取所有用户* - 路由:GET /user/getAll* @returns 所有用户的列表*/@Get("getAll")getUsers(): any {return this.userService.findAll(); // 调用服务层方法查询所有用户}
}

这样,我们在访问 getAll() 请求时即可在控制台看到相关信息的打印。

在这里插入图片描述

与此同时,log 信息写入了 log 文件夹的 log.txt.1 中。

在这里插入图片描述


winston

winston官网
nest-winston网址

安装 winston 和 nest-winston。

pnpm i --save nest-winston winston// 安装日志滚动 -第三方库
pnpm install winston-daily-rotate-file

GitHub 提交记录

main.ts 中添加对 winston 的配置

import { NestFactory } from "@nestjs/core"; // 导入 NestFactory,用于创建 Nest 应用实例
import { AppModule } from "./app.module"; // 导入应用的根模块
import { createLogger } from "winston";
import * as winston from "winston";
import { utilities, WinstonModule } from "nest-winston"; // 导入 nest-winston,用于 Nest 集成 Winston 日志库}
import "winston-daily-rotate-file"; // 导入 Winston 日志轮转文件传输器/*** 应用程序的入口文件* - 使用 NestFactory 创建应用实例* - 配置全局设置并启动应用*/
async function bootstrap() {// 创建 Winston 日志实例const instance = createLogger({transports: [// 配置控制台日志输出new winston.transports.Console({level: "info", // 日志级别为 infoformat: winston.format.combine(winston.format.timestamp(), // 添加时间戳utilities.format.nestLike() // 格式化日志为 Nest 风格),}),// 配置日志文件轮转(警告级别)new winston.transports.DailyRotateFile({level: "warn", // 日志级别为 warndirname: "logs", // 日志文件存储目录filename: "application-%DATE%.log", // 日志文件名,包含日期占位符datePattern: "YYYY-MM-DD-HH", // 日期格式zippedArchive: true, // 启用压缩存档maxSize: "20m", // 每个日志文件的最大大小为 20MBmaxFiles: "14d", // 保留日志文件的天数为 14 天format: winston.format.combine(winston.format.timestamp(), // 添加时间戳winston.format.simple() // 简单格式化日志),}),// 配置日志文件轮转(信息级别)new winston.transports.DailyRotateFile({level: "info", // 日志级别为 infodirname: "logs", // 日志文件存储目录filename: "info-%DATE%.log", // 日志文件名,包含日期占位符datePattern: "YYYY-MM-DD-HH", // 日期格式zippedArchive: true, // 启用压缩存档maxSize: "20m", // 每个日志文件的最大大小为 20MBmaxFiles: "14d", // 保留日志文件的天数为 14 天format: winston.format.combine(winston.format.timestamp(), // 添加时间戳winston.format.simple() // 简单格式化日志),}),],});// 创建应用实例,并可选配置日志级别const app = await NestFactory.create(AppModule, {// 日志级别配置(可选)// logger: ["error", "warn"], // 仅记录错误和警告日志logger: WinstonModule.createLogger({instance,}),});// 设置全局路由前缀app.setGlobalPrefix("api"); // 所有路由将以 "api" 为前缀,例如 /api/userconst port = 3000; // 定义应用监听的端口号// 启动应用并监听指定端口await app.listen(port);// 使用 Winston 记录应用启动信息instance.info(`应用已启动,监听端口:${port}`);
}// 启动应用
bootstrap();

修改 app.module.ts,提供全局 Logger

import { Global, Logger, Module } from "@nestjs/common";@Global() // 声明为全局模块,所有其他模块均可直接注入
@Module({...providers: [Logger], // 服务提供者exports: [Logger], // 导出 Logger 以供其他模块使用
})

user.controller.ts 中测试使用

import { Controller, Get, Logger, Post } from "@nestjs/common"; // 导入控制器和 HTTP 请求装饰器
import { UserService } from "./user.service"; // 导入用户服务
import { User } from "./user.entity"; // 导入用户实体
/*** 用户控制器类* - 定义与用户相关的 HTTP 路由和处理逻辑* - 使用 `UserService` 提供的业务逻辑操作用户数据*/
@Controller("user") // 定义控制器的路由前缀为 "user"
export class UserController {// private logger = new Logger(UserController.name); // 创建 Logger 实例,用于记录日志/*** 构造函数* - 注入用户服务* @param userService 用户服务*/constructor(private userService: UserService,private readonly logger: Logger // 注入日志服务) {this.logger.log("UserController initialized"); // 控制器初始化时记录日志}/*** 获取所有用户* - 路由:GET /user/getAll* @returns 所有用户的列表*/@Get("getAll")getUsers(): any {this.logger.log("Fetching all users"); // 记录获取所有用户的日志this.logger.warn("Fetching all users");this.logger.error("Fetching all users");return this.userService.findAll(); // 调用服务层方法查询所有用户}
}

在这里插入图片描述

在这里插入图片描述


全局异常过滤器

GitHub 提交记录

修改 user.controller.ts,给 getAll 接口设置 http 异常处理。

  @Get("getAll")getUsers(): any {const user = { isAdmin: false };if (!user.isAdmin) {throw new HttpException("User is not admin", HttpStatus.FORBIDDEN);}this.logger.log("Fetching all users"); // 记录获取所有用户的日志this.logger.warn("Fetching all users");this.logger.error("Fetching all users");return this.userService.findAll(); // 调用服务层方法查询所有用户}

那么我们在访问 http://localhost:3000/api/user/getAll 网址的时候将在控制台看到如下日志信息。

在这里插入图片描述

src 目录下创建 filters 文件,并在该文件下创建 http-exception-filer.ts 文件,封装它为全局的 Http 异常过滤器。

import {ArgumentsHost,Catch,ExceptionFilter,HttpException,LoggerService,
} from "@nestjs/common"; // 导入相关装饰器和类型/*** 自定义 HTTP 异常过滤器* - 捕获所有 `HttpException` 类型的异常* - 记录异常日志并返回标准化的响应*/
@Catch(HttpException) // 捕获 HttpException 类型的异常
export class HttpExceptionFilter implements ExceptionFilter {/*** 构造函数* - 注入日志服务,用于记录异常日志* @param logger 日志服务*/constructor(private logger: LoggerService) {}/*** 异常捕获处理方法* - 捕获异常并返回标准化的响应* @param exception 捕获的异常对象* @param host 参数上下文*/catch(exception: HttpException, host: ArgumentsHost) {const ctx = host.switchToHttp(); // 获取 HTTP 上下文const response = ctx.getResponse(); // 获取响应对象const request = ctx.getRequest(); // 获取请求对象const status = exception.getStatus(); // 获取异常状态码// 使用日志服务记录错误信息this.logger.error(exception.message, exception.stack);// 返回标准化的 JSON 响应response.status(status).json({code: status, // HTTP 状态码timestamp: new Date().toISOString(), // 当前时间戳path: request.url, // 请求的 URLmethod: request.method, // 请求的方法(GET、POST 等)message: exception.message || exception.name, // 异常信息});}
}

main.ts 中进行全局使用。

app.useGlobalFilters(new HttpExceptionFilter(logger)); // 使用全局异常过滤器处理 HTTP 异常

此时在访问一个不存在的路由(如:http://localhost:3000/api/user/getAll123)时,即可在控制台看到如下错误。

在这里插入图片描述

此时在日志文件中也记录了相关错误日志。

在这里插入图片描述


总结

本篇文章,我们认识了日志的作用,学习了 NestJS-logger、pino日志、winston日志,并了解了如何把日志输入到文件。最后,我们学习了全局异常过滤器,它帮助我们统一处理异常的请求,并且我们还实现了和日志相结合。

好啦,本篇文章到这里就要和大家说再见啦,祝你这篇文章阅读愉快,你下篇文章的阅读愉快留着我下篇文章再祝!


参考资料:

  1. DeepSeek
  2. NestJS 从入门到实战

在这里插入图片描述

http://www.xdnf.cn/news/505081.html

相关文章:

  • ORACLE数据库实例报错ORA-00470: LGWR process terminated with error宕机问题分析报告
  • JavaScript 的编译与执行原理
  • IT运维的365天--026 视频下载相关
  • 常见平方数和立方数的计算
  • 简单网络交换、路由-华三RRPP以太环网
  • 电商项目-品牌管理微服务开发
  • OpenHarmony外设驱动使用 (二),Camera
  • 【大模型面试每日一题】Day 21:对比Chain-of-Thought(CoT)与Self-Consistency在复杂推理任务中的优劣
  • 线程同步学习
  • 8天Python从入门到精通【itheima】-11~13
  • SpringBootAdmin:全方位监控与管理SpringBoot应用
  • nt!MiInitializePfn函数分析之nt!MiPfPutPagesInTransition函数的关键一步
  • Golang 范型
  • 编程日志5.10
  • QLoRA: Efficient Finetuning of Quantized LLMs
  • acwing5579 增加模数
  • 深入了解 VPC 端点类型 – 网关与接口
  • Stacking(堆叠):集成学习中的“超级英雄团队”
  • STM32+ESP8266连接onenet新平台
  • 【嵌入式DIY实例-Arduino篇】-OLED实现乒乓游戏
  • Seata源码—5.全局事务的创建与返回处理二
  • nodejs特性解读
  • 小刚说C语言刷题—1230蝴蝶结
  • 业务系统上线为什么这么难
  • 【Unity 2023 新版InputSystem系统】新版InputSystem 如何进行项目配置并安装
  • 【RocketMQ Broker 相关源码】- 清除不活跃的 broker
  • JavaScript【6】事件
  • windows 11安装Python3.9、mujoco200、mujoco_py2.0.2.8、metaworld
  • 51单片机仿真突然出问题
  • 如何在 Windows 11 或 10 的 CMD 中检查固件