跳至内容

日志记录Logger在NestJS中的实现

在日常开发中,我们都或多或少要用到日志服务去排查Bug,尤其是线上环境的时候,很多情况是可以通过分析错误日志去解决的,如果没有打印出错误日志,在线上就不容易查出问题,下面SEO禅将借用在《Clean Architecture在NestJS中的实践》实现的代码,来加入日志打印功能。

NestJS内置Logger

我们先使用内置Logger来实现一个日志记录功能,之后再更换成Winston这种生产级别用的第三方库来替换掉内置的日志实现。首先我们要先申明一个抽象的接口,这样就不会依赖任何第三方实现:

export interface ILogger {
  debug(context: string, message: string): void;
  log(context: string, message: string): void;
  error(context: string, message: string, trace?: string): void;
  warn(context: string, message: string): void;
  verbose(context: string, message: string): void;
}

在创建一个logger.service.ts文件:

@Injectable()
export class LoggerService implements ILogger {
  logger: Logger;
  constructor() {
    this.logger = new Logger();
  }
  debug(context: string, message: string) {
    if (process.env.NODE_ENV !== 'production') {
      this.logger.debug(`[DEBUG] ${message}`, context);
    }
  }
  log(context: string, message: string) {
    this.logger.log(`[INFO] ${message}`, context);
  }
  error(context: string, message: string, trace?: string) {
    this.logger.error(`[ERROR] ${message}`, trace, context);
  }
  warn(context: string, message: string) {
    this.logger.warn(`[WARN] ${message}`, context);
  }
  verbose(context: string, message: string) {
    if (process.env.NODE_ENV !== 'production') {
      this.logger.verbose(`[VERBOSE] ${message}`, context);
    }
  }
}

把它挂载到你想使用的模块中,providers数组里面,当然也可以创建一个logger module

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

@Module({
  providers: [LoggerService],
  exports: [LoggerService],
})
export class LoggerModule {}

随便在哪里想调用的地方使用看看:

 this.logger.log('User Controller', 'get users');
[Nest] 5676  - 2023/04/24 17:29:32     LOG [User Controller] [INFO] get users // 输出

我们还可以再创建一个拦截器,拦截所有请求并记录:

@Injectable()
export class LoggingInterceptor implements NestInterceptor {
  constructor(private readonly logger: LoggerService) {}

  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    const now = Date.now();
    const httpContext = context.switchToHttp();
    const request = httpContext.getRequest();

    const ip = this.getIP(request);

    this.logger.log(
      `Incoming Request on ${request.path}`,
      `method=${request.method} ip=${ip}`,
    );

    return next.handle().pipe(
      tap(() => {
        this.logger.log(
          `End Request for ${request.path}`,
          `method=${request.method} ip=${ip} duration=${Date.now() - now}ms`,
        );
      }),
    );
  }

  private getIP(request: any): string {
    let ip: string;
    const ipAddr = request.headers['x-forwarded-for'];
    if (ipAddr) {
      const list = ipAddr.split(',');
      ip = list[list.length - 1];
    } else {
      ip = request.connection.remoteAddress;
    }
    return ip.replace('::ffff:', '');
  }
}

记得要在main.ts挂载下:

  app.useGlobalInterceptors(new LoggingInterceptor(new LoggerService()));

第三方库Winston

Winston是比较专业的日志记录第三方库,可以向文件,数据库,云服务器输出日志,也可以自定义各种格式,我们现在对接进项目,安装依赖包:

pnpm i winston
pnpm i -D @types/winston

再把我们刚才的logger service代码改下就行:

@Injectable()
export class LoggerService implements ILogger {
  logger: Logger;
  constructor() {
    this.logger = createLogger({
      level: 'info',
      format: format.combine(format.timestamp(), format.json()),
      transports: [
        new transports.Console({
          format: format.combine(format.colorize(), format.simple()),
        }),
        new transports.File({
          filename: 'logs/error.log',
          level: 'error',
        }),
        new transports.File({
          filename: 'logs/info.log',
          level: 'info',
        }),
      ],
    });
  }
  debug(context: string, message: string) {
    this.logger.debug(message);
  }
  log(context: string, message: string) {
    this.logger.info(message);
  }
  error(context: string, message: string, trace?: string) {
    this.logger.error(message);
  }
  warn(context: string, message: string) {
    this.logger.warn(message);
  }

  verbose(context: string, message: string) {
    this.logger.verbose(message);
  }
}

这里用了不同的transports向不同的文件和终端输出信息,具体如何使用参考官方文档,这里就不再过多描述,有什么不懂的,看开头那边文章里面的源码吧,有什么问题可以留言评论。

作者:SEO禅
本文链接:日志记录Logger在NestJS中的实现
版权申明:如无特殊说明,本站文章均为作者原创,著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处,谢谢!

标签:, ,

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注