个人简介
👀个人主页: 前端杂货铺
🙋♂️学习方向: 主攻前端方向,正逐渐往全干发展
📃个人状态: 研发工程师,现效力于中国工业软件事业
🚀人生格言: 积跬步至千里,积小流成江海
🥇推荐学习:🍍前端面试宝典 🎨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 |
文章目录
日志
日志等级
- Log:通用日志,按需记录
- Warning:警告日志,比如:尝试多次进行数据库操作
- Error:严重日志,比如:数据库异常
- Debug:调试日志,比如:加载数据日志
- Verbose:详细日志,所有的操作与详细信息(非必要不打印)
功能分类日志
- 错误日志:方便定位问题,给用户友好的提示
- 调试日志:方便开发
- 请求日志:记录敏感行为
日志记录位置
- 控制台日志:方便监看(调试用)
- 文件日志:方便回溯与追踪(24小时滚动)
- 数据库日志:敏感操作、敏感数据记录

NestJS - logger
接下来,我们使用 @nestjs/common
中的 logger 来进行简单的日志打印。
修改 app.module.ts
的 logging: false
,以关闭 typeorm 的日志。
在 main.ts
中修改代码如下。
javascript
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/user
const port = 3000; // 定义应用监听的端口号
// 启动应用并监听指定端口
await app.listen(port);
// 使用 Logger 记录应用启动信息
logger.log(`App运行在:${port}`); // 普通日志
logger.warn(`App运行在:${port}`); // 警告日志
logger.error(`App运行在:${port}`); // 错误日志
}
// 启动应用
bootstrap();

在模块中使用 logger 也非常简单。接下来,我们修改 user.controller.ts
文件,以下是主要修改内容。
javascript
@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
javascript
// 安装 pino
pnpm install nestjs-pino
// 安装 pino-pretty 便于直观显示
pnpm i pino-pretty
// 安装 pino-roll,日志文件滚动
pnpm i pino-roll
在 app.module.ts
中添加对日志模块的配置
javascript
import { LoggerModule } from "nestjs-pino";
import { join } from "path";
...
// 配置日志模块
LoggerModule.forRoot({
pinoHttp: {
transport: {
targets: [
process.env.NODE_ENV === "development"
? {
level: "info", // 日志级别为 info
target: "pino-pretty", // 使用 pino-pretty 格式化日志
options: {
colorize: true, // 启用日志颜色
},
}
: {
level: "info", // 日志级别为 info
target: "pino-roll", // 使用 pino-roll 将日志写入文件
options: {
file: join("log", "log.txt"), // 日志文件路径
frequency: "daily", // 日志文件按天滚动
size: "10M", // 每个日志文件的最大大小为 10MB
mkdir: true, // 如果目录不存在,则自动创建
},
},
],
},
},
}),...
修改 user.controller.ts
中对 pino 的使用。
javascript
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。
javascript
pnpm i --save nest-winston winston
// 安装日志滚动 -第三方库
pnpm install winston-daily-rotate-file
在 main.ts
中添加对 winston
的配置
javascript
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", // 日志级别为 info
format: winston.format.combine(
winston.format.timestamp(), // 添加时间戳
utilities.format.nestLike() // 格式化日志为 Nest 风格
),
}),
// 配置日志文件轮转(警告级别)
new winston.transports.DailyRotateFile({
level: "warn", // 日志级别为 warn
dirname: "logs", // 日志文件存储目录
filename: "application-%DATE%.log", // 日志文件名,包含日期占位符
datePattern: "YYYY-MM-DD-HH", // 日期格式
zippedArchive: true, // 启用压缩存档
maxSize: "20m", // 每个日志文件的最大大小为 20MB
maxFiles: "14d", // 保留日志文件的天数为 14 天
format: winston.format.combine(
winston.format.timestamp(), // 添加时间戳
winston.format.simple() // 简单格式化日志
),
}),
// 配置日志文件轮转(信息级别)
new winston.transports.DailyRotateFile({
level: "info", // 日志级别为 info
dirname: "logs", // 日志文件存储目录
filename: "info-%DATE%.log", // 日志文件名,包含日期占位符
datePattern: "YYYY-MM-DD-HH", // 日期格式
zippedArchive: true, // 启用压缩存档
maxSize: "20m", // 每个日志文件的最大大小为 20MB
maxFiles: "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/user
const port = 3000; // 定义应用监听的端口号
// 启动应用并监听指定端口
await app.listen(port);
// 使用 Winston 记录应用启动信息
instance.info(`应用已启动,监听端口:${port}`);
}
// 启动应用
bootstrap();
修改 app.module.ts
,提供全局 Logger
javascript
import { Global, Logger, Module } from "@nestjs/common";
@Global() // 声明为全局模块,所有其他模块均可直接注入
@Module({
...
providers: [Logger], // 服务提供者
exports: [Logger], // 导出 Logger 以供其他模块使用
})
在 user.controller.ts
中测试使用
javascript
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(); // 调用服务层方法查询所有用户
}
}


全局异常过滤器
修改 user.controller.ts
,给 getAll 接口设置 http 异常处理。
javascript
@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 异常过滤器。
javascript
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, // 请求的 URL
method: request.method, // 请求的方法(GET、POST 等)
message: exception.message || exception.name, // 异常信息
});
}
}
在 main.ts
中进行全局使用。
javascript
app.useGlobalFilters(new HttpExceptionFilter(logger)); // 使用全局异常过滤器处理 HTTP 异常
此时在访问一个不存在的路由(如:http://localhost:3000/api/user/getAll123)时,即可在控制台看到如下错误。

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

总结
本篇文章,我们认识了日志的作用,学习了 NestJS-logger、pino日志、winston日志,并了解了如何把日志输入到文件。最后,我们学习了全局异常过滤器,它帮助我们统一处理异常的请求,并且我们还实现了和日志相结合。
好啦,本篇文章到这里就要和大家说再见啦,祝你这篇文章阅读愉快,你下篇文章的阅读愉快留着我下篇文章再祝!
参考资料:
- DeepSeek
- NestJS 从入门到实战
