【nest.js】功能模块-日志服务

192 阅读1分钟
1、安装依赖,以winston为例

winston-daily-rotate-file:日志文件轮换

chalk:日志级别颜色

npm install nest-winston winston winston-daily-rotate-file chalk
2、服务配置文件

新建winston.config.ts文件

//  /config/winston.config.ts
import * as chalk from 'chalk';
import { createLogger, format, transports } from 'winston';
import * as DailyRotateFile from 'winston-daily-rotate-file';

// 定义日志级别颜色,日志级别 error > warn > info > verbose > debug > silly
const levelsColors = {
  error: 'red',
  warn: 'yellow',
  info: 'green',
  debug: 'blue',
  verbose: 'cyan',
}

const winstonLogger = createLogger({
  format: format.combine(format.timestamp(), format.errors({ stack: true }), format.splat(), format.json()),
  // defaultMeta: { service: 'log-service' }, //默认添加的信息
  transports: [
    //错误日志
    new DailyRotateFile({  
      filename: 'logs/errors/error-%DATE%.log', // 日志名称,占位符 %DATE% 取值为 datePattern 值。
      datePattern: 'YYYY-MM-DD', // 日志的格式和轮换的频率,此处表示每天。
      zippedArchive: false, // 是否通过压缩的方式归档被轮换的日志文件。
      maxSize: '20m', // 设置日志文件的最大大小,m 表示 mb 。
      maxFiles: '14d', // 保留日志文件的最大天数,此处表示自动删除超过 14 天的日志文件。
      level: 'error', // 日志类型,此处表示只记录级别最低为error的日志
    }),
    //运行时日志
    new DailyRotateFile({  
      filename: 'logs/runtime/runtime-%DATE%.log',
      datePattern: 'YYYY-MM-DD',
      zippedArchive: false,
      maxSize: '20m',
      maxFiles: '14d',
      level: 'debug',
    }),
    //控制台日志
    new transports.Console({  
      format: format.combine(
        format.colorize({
          colors: levelsColors,
        }),
        format.simple(),
        format.printf((info) => {
          //获取日志级别
          const symbols = Object.getOwnPropertySymbols(info);
          const color = levelsColors[info[symbols[0]]];
          //获取带颜色的日志
          const message = `${chalk[color](info[symbols[2]])}`;
          return message;
        }),
      ),
      level: 'debug',
    }),
  ],
})

export default winstonLogger;
//app.module.ts
import winstonLogger from './config/winston.config';

@Module({
    imports: [
        WinstonModule.forRoot({
            transports: winstonLogger.transports,
            format: winstonLogger.format,
            defaultMeta: winstonLogger.defaultMeta,
            exitOnError: false,
        })
    ]
})
3、全局中间件

新建logger.middleware.ts文件,捕获所有请求并记录

// /middleware/logger.middleware.ts
import { Injectable, Logger, NestMiddleware } from '@nestjs/common';
import * as dayjs from 'dayjs';
import { NextFunction, Request, Response } from 'express';

@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|HTTP/[httpVersion]|url|status code|[response time]ms|client IP|user-agent|request body
            const logFormat = `${dayjs().format("YYYY-MM-DD HH:mm:ss")}|${method}|HTTP/${httpVersion}|${originalUrl}|${statusCode}|${duration}ms|${ip}|${headers['user-agent']}|${JSON.stringify(method === 'GET' ? req.query : req.body)}`

            // 根据状态码,进行日志类型区分
            statusCode >= 500 ? this.logger.error(logFormat) : this.logger.log(logFormat)
        })

        next();
    }
}
//app.module.ts
import {Module, MiddlewareConsumer} from '@nestjs/common';
import {LoggerMiddleware} from './middleware/logger.middleware';

export class AppModule {
    configure(consumer: MiddlewareConsumer) {
        //应用日志中间件到所有路由
        consumer.apply(LoggerMiddleware).forRoutes('*');
    }
}
4、注册
//main.ts
import { WINSTON_MODULE_NEST_PROVIDER } from 'nest-winston';

async function bootstrap() {
    const app = await NestFactory.create(AppModule);

    //日志服务
    app.useLogger(app.get(WINSTON_MODULE_NEST_PROVIDER));

    await app.listen(3000);
}
5、使用示例
//your.service.ts
import {Injectable, Logger} from '@nestjs/common';

@Injectable()
export class yourService {
    private logger = new Logger();
    
    exampleFn(){
        this.logger.log("测试日志打印")    
    }
}