Nestjs 学习系列基础篇9 - 使用Winston实现分级日志收集

1,456 阅读2分钟

Nestjs 学习系列基础篇9 - 使用Winston实现分级日志收集

日志库的必要性

  • 传输通道
    • Console 文件
    • File 文件
    • Http 通过http传输
    • Stream 流传输
  • 格式化
    • 时间戳
    • 格式美化
  • 日志分割
    • 按文件大小分割
    • 按写入时间分割
  • 日志分级
    • DEBUG:调试,等级最低,一般生产环境会将该等级的日志关闭
    • INFO:信息,普通等级,最常用的系统日志收集等级,一般会将系统运行中的一些相关信息(非开发类敏感信息)设为该等级
    • WARN:_FBI WARNNING_大家都知道,就是警告,但又不到错误
    • ERROR:报错,一般程序报错使用该等级
    • FATAL:严重,比程序报错还严重的等级,如果没做错误等级划分的话,一般FATAL都不怎么用到,都用ERROR替代了

安装

pnpm i winston

封装

src/shared/logger/logger.service.ts

import { Logger, createLogger, format, transports } from 'winston';

export class AppLogger {
  private context?: string;
  private logger: Logger;

  public setContext(context: string): void {
    this.context = context;
  }

  constructor() {
    this.logger = createLogger({
      level: process.env.LOGGER_LEVEL,
      format: format.combine(format.timestamp(), format.prettyPrint()),
      transports: [
        new transports.Console(),
        new transports.File({ filename: 'logs/error.log', level: 'error' }),
        new transports.File({ filename: 'logs/combined.log' }),
      ],
    });
  }

  error(ctx: any, message: string, meta?: Record<string, any>): Logger {
    return this.logger.error({
      message,
      contextNmae: this.context,
      ctx,
      ...meta,
    });
  }

  warn(ctx: any, message: string, meta?: Record<string, any>): Logger {
    return this.logger.warn({
      message,
      contextNmae: this.context,
      ctx,
      ...meta,
    });
  }

  debug(ctx: any, message: string, meta?: Record<string, any>): Logger {
    return this.logger.debug({
      message,
      contextNmae: this.context,
      ctx,
      ...meta,
    });
  }

  info(ctx: any, message: string, meta?: Record<string, any>): Logger {
    return this.logger.info({
      message,
      contextNmae: this.context,
      ctx,
      ...meta,
    });
  }
}

使用

src/shared/logger/logger.module.ts

import { Module } from '@nestjs/common';
import { AppLogger } from './logger.service';

@Module({
  providers: [AppLogger],
  exports: [AppLogger],
  imports: [],
})
export class AppLoggerModule {}

src/shared/shared.module.ts

import { Module } from '@nestjs/common';
import { SystemService } from './system.service';
import { ConfigModule } from '@nestjs/config';
import { configModuleOptions } from './configs/module-options';
import { DatabaseProviders } from './database.providers';
import { AppLoggerModule } from './logger/logger.module';

@Module({
  providers: [SystemService, ...DatabaseProviders],
  // 暴露 config 模块
  exports: [SystemService, ConfigModule, AppLoggerModule, ...DatabaseProviders],
  // 注入 config 模块
  imports: [ConfigModule.forRoot(configModuleOptions), AppLoggerModule],
})
export class SharedModule {}

src/user/user.service.ts

import { MongoRepository } from 'typeorm';

export class UserService {
  constructor(
    ...
    // 注入日志
    private readonly logger: AppLogger,
  ) {
    this.logger.setContext(UserService.name);
  }

  ...

  findAll() {
    // 测试日志
    this.logger.info(null, 'test logger', { name: 'logger info' });
    this.logger.debug(null, 'test logger', { name: 'logger debug' });
    return this.userRepository.findAndCount({});
  }
}

项目仓库地址

仓库地址Github: dome-server

注意:基础篇的的代码在 base 分支下