Skip to content

nest 记录日志

前言

作为服务端应用,有一个完善的日志系统非常有必要,日志是用于排查解决问题的依据,我们平时开发中经常使用console.log来打印日志信息,非常方便,但是它在终端关闭后就没了,nest 考虑到这一点,也提供了一套解决方案。

nest 默认的日志

nest-cli创建一个新项目

sh
nest new nest-log-demo -p pnpm

提示

-p--package-manager的别名,表示指定包管理器。使用 npm 、 yarn 或 pnpm 。包管理器必须全局安装。

然后启动项目pnpm run start:dev,能看到 nest 默认打印的几行日志

使用 nest 默认的日志来打印日志

nest 内置了日志打印功能,可以在需要打印日志的地方直接使用,例如:controller service

ts
import { Controller, Get, Logger } from "@nestjs/common";
import { AppService } from "./app.service";

@Controller()
export class AppController {
  logger: Logger; 

  constructor(private readonly appService: AppService) {
    this.logger = new Logger(AppController.name); 
  }

  @Get()
  getHello(): string {
    this.logger.log("aaaaaaaaaa"); 
    this.logger.debug("aaaaaaaaaa"); 
    this.logger.error("aaaaaaaaaa"); 
    this.logger.fatal("aaaaaaaaaa"); 
    this.logger.verbose("aaaaaaaaaa"); 
    this.logger.warn("aaaaaaaaaa"); 
    return this.appService.getHello();
  }
}

效果如下:

log debug等表示日志级别

这个日志是受 Nest 控制的,可以在创建应用的时候指定是否开启:

ts
import { NestFactory } from "@nestjs/core";
import { AppModule } from "./app.module";

async function bootstrap() {
  const app = await NestFactory.create(AppModule, {
    logger: false, // 关闭日志
  });

  await app.listen(3000);
}
bootstrap();

关闭后的效果:

自定义日志打印

- 实现 LoggerService 接口

除了上面的直接使用 nest 内置 logger 来打印日志之外,还可以自定义日志打印的方式,定义一个实现 LoggerService 接口的类

ts
// src/myLogger.ts
import { LoggerService } from "@nestjs/common";

// MyLogger 必须实现 log warn error三个方法
export class MyLogger implements LoggerService {
  log(message: string, context?: string) {
    console.log(`---log---[${context}]---`, message);
  }

  warn(message: string, context?: string) {
    console.log(`---warn---[${context}]---`, message);
  }

  error(message: string, context?: string) {
    console.log(`---error---[${context}]---`, message);
  }
}

在创建应用时指定这个 logger

ts
// main.ts
import { NestFactory } from "@nestjs/core";
import { AppModule } from "./app.module";
import { MyLogger } from "./myLogger"; 

async function bootstrap() {
  const app = await NestFactory.create(AppModule, {
    logger: new MyLogger(), 
  });
  await app.listen(3000);
}
bootstrap();

效果:

- 继承 LoggerService

你也可以不自己实现 LoggerService 的全部方法,而是继承 ConsoleLogger,重写一些方法:

ts
import { ConsoleLogger } from "@nestjs/common";

export class MyLogger extends ConsoleLogger {
  log(message: string, context: string) {
    console.log(`[${context}]`, message);
  }
}

因为 ConsoleLogger 实现了 LoggerService 接口,这样我们没重写的方法就是原来的:

Logger 依赖注入

上述的日志打印可以正常使用,但是没办法实现依赖注入,因为 Logger 是在容器外面手动 new 的对象,接下来对其进行改造

先给 MyLogger 添加 @Injectable() 装饰器,代表这是一个 provider, 可以注入和被注入。 !

假如要注入到 appService 里面, 需要再 appModule 内 providers 注册一下,然后再在 appSercice 里面 Inject 一下;

最后改造入口

提示

bufferLogs 就是先不打印日志,把它放到 buffer 缓冲区,直到用 useLogger 指定了 Logger 并且应用初始化完毕。

app.get 就是从容器中取这个类的实例的

现在打印的日志长这样 很明显,已经成功注入 appService

设置成模块

也可以单独将日志模块弄成一个当独的模块,例如全局模块动态模块

  • 动态模块,每次 imports 的时候配置下
ts
import { DynamicModule, Global, Module } from "@nestjs/common";
import { MyLogger } from "./MyLogger";

@Module({})
export class LoggerModule {
  static register(options): DynamicModule {
    return {
      module: LoggerModule,
      providers: [
        MyLogger,
        {
          provide: "LOG_OPTIONS",
          useValue: options,
        },
      ],
      exports: [MyLogger, "LOG_OPTIONS"],
    };
  }
}

每次 imports 的时候传入不同的配置:

在 AppService 里注入下:

ts
import { Inject, Injectable } from "@nestjs/common";
import { MyLogger } from "./logger2/MyLogger";

@Injectable()
export class AppService {
  @Inject(MyLogger)
  private logger: MyLogger;

  getHello(): string {
    this.logger.log("yyy", AppService.name);

    return "Hello World!";
  }
}

小结

日志打印可以用 Nest 的 Logger,它支持在创建应用的时候指定 logger 是否开启,打印的日志级别,还可以自定义 logger。

自定义 Logger 需要实现 LoggerService 接口,或者继承 ConsoleLogger 然后重写部分方法。

如果想在 Logger 注入一些 provider,就需要创建应用时设置 bufferLogs 为 true,然后用 app.useLogger(app.get(xxxLogger)) 来指定 Logger。

你可以把这个自定义 Logger 封装到全局模块,或者动态模块里。

当然,一般情况下,直接使用 Logger 就可以了。