1. 安装依赖:
npm install winston winston-daily-rotate-file chalk
winston
:日志库
winston-daily-rotate-file
:按日期存储日志
chalk
:控制台日志颜色美化
2.生成logger模块:
nest g module logger --no-spec nest g provider logger --no-spec
其中在LoggerModule中导出Logger,以便在其他模块中共享它:
import { Module } from '@nestjs/common' import { Module } from './logger' @Module({ providers: [Logger], exports: [Logger] }) export class LogerModule {}
3.配置 Winston + Chalk:
import * as winston from 'winston'; import 'winston-daily-rotate-file'; import chalk from 'chalk'; // 日志级别颜色 const chalkColors = { error: chalk.red.bold, warn: chalk.yellow.bold, info: chalk.cyan.bold, debug: chalk.blue.bold, verbose: chalk.magenta.bold, }; // 格式化日志 const logFormat = winston.format.printf(({ level, message, timestamp }) => { const colorizer = chalkColors[level] || chalk.white; // 颜色 return colorizer(`[${timestamp}] [${level.toUpperCase()}]: ${message}`); }); // 创建 Winston 实例 const logger = winston.createLogger({ level: 'debug', // 最低日志级别 format: winston.format.combine( winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), // 添加时间戳 winston.format.printf(({ timestamp, level, message }) => { return `[${timestamp}] [${level.toUpperCase()}]: ${message}`; }), ), transports: [ // 控制台输出(带颜色) new winston.transports.Console({ format: winston.format.combine( winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), logFormat, ), }), // 文件持久化存储(每日滚动) new winston.transports.DailyRotateFile({ filename: 'logs/application-%DATE%.log', datePattern: 'YYYY-MM-DD', maxSize: '10m', maxFiles: '14d', zippedArchive: true, }), ], }); export default Logger;
4.使用Winston日志起,并关闭内置日志器件:
async function bootstrap() { const app = await NestFactory.create(AppModule, { logger: false, }); app.useLogger(app.get(Logger)); await app.listen(3000); }
5.使用Logger:
在AppService中注入Logger类,并在getHello路由方法中调用日志方法打印结果,代码如下:
6.接口请求日志:
在实际业务环境中,日志记录是一种关键的监控和问题诊断工具。虽然在某些特殊业务场景中,开发人员可能会选择手动记录日志,但在处理请求、响应和捕获服务异常等常见场景时,手动记录日志可能效率较低。为了减少冗余的日志代码并统一日志格式,通常会采用全局日志记录策略,这基于面向切面编程(Aspect-Oriented Programming,AOP)的思想来实现。
创建Logger中间件:
import { Injectable, NestMiddleware } from '@nestjs/common'; import dayjs from 'dayjs'; import { NextFunction, Request, Response } from 'express'; import { Logger } from './logger' @Injectable() export class LoggerMiddleware implements NestMiddleware { private logger = new Logger(); use(req: Request, res: Response, next: NextFunction) { // 记录开始时间 const start = Date.now(); // 获取请求信息 const { method, originalUrl, ip, httpVersion, headers } = req; // 获取响应信息 const { statusCode } = res; res.on('finish', () => { // 记录结束时间 const end = Date.now(); // 计算时间差 const duration = end - start; // 这里可以根据自己需要组装日志信息:[timestamp] [method] [url] HTTP/[httpVersion] [client IP] [status code] [response time]ms [user-agent] const logFormat = `${dayjs().valueOf()} ${method} ${originalUrl} HTTP/${httpVersion} ${ip} ${statusCode} ${duration}ms ${headers['user-agent']}`; // 根据状态码,进行日志类型区分 if (statusCode >= 500) { this.logger.error(logFormat, originalUrl); } else if (statusCode >= 400) { this.logger.warn(logFormat, originalUrl); } else { this.logger.log(logFormat, originalUrl); } }); next(); } }
在AppModule中全局注册中间件:
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common'; import { LoggerMiddleware } from '@/middleware/logger.middleware'; // 全局日志中间件 @Module({ imports: [], }) export class AppModule implements NestModule { configure(consumer: MiddlewareConsumer) { consumer.apply(LoggerMiddleware).forRoutes('*'); } }
在接口调用时,控制台就会输出信息: