Nest探索(七)Nest 中打印日志

273 阅读4分钟

💥 在掘金写技术好文,瓜分万元现金大奖 | 4月金石计划

前言

在平常的前端开发工作和运维搬砖工作中,我们离不开日志。比如我们可以通过打印的日志去观察应用的运行状态,或者查看上下文和变量的值等,或者排查和定位线上问题等等。在 Nest 后端应用中,也不例外,学会打印和查看日志也是掌握 Nest 的很重要的一环。

这里,我们将探索 Nest 中常用的打印日志的方法。

Logger 类

在 @nestjs/common 包中包含了 Logger 类,可以用于设置日志。

在默认情况下,我们启动应用后可以看到打印了:

[Nest] 7344  - 2024/05/04 23:53:29     LOG [NestFactory] Starting Nest application...
[Nest] 7344  - 2024/05/04 23:53:29     LOG [InstanceLoader] AppModule dependencies initialized +11ms
[Nest] 7344  - 2024/05/04 23:53:29     LOG [RoutesResolver] AppController {/}: +9ms
[Nest] 7344  - 2024/05/04 23:53:29     LOG [RouterExplorer] Mapped {/, GET} route +6ms
[Nest] 7344  - 2024/05/04 23:53:29     LOG [NestApplication] Nest application successfully started +3ms

我们可以关闭 Nest 日志的打印,通过在main.ts中设置:

const app = await NestFactory.create(AppModule, {
  logger: false,
});
await app.listen(3000);

或是,设置日志打印的类型:

const app = await NestFactory.create(AppModule, {
  logger: ['error', 'warn'],
});
await app.listen(3000);

可以设置的有以下几种:'log', 'fatal', 'error', 'warn', 'debug', and 'verbose'

或是设置为logger: console,,也就是打印的是简单的样式,文本不带颜色。

与此同时,我们可以在 app.controller.ts 中,引入 Logger 打印器,设置了但访问 getHello 方法时打印相应的日志,方便后续的调试。

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

@Controller()
export class AppController {
  private logger = new Logger();

  constructor(private readonly appService: AppService) {}

  @Get()
  getHello(): string {
    this.logger.log('11', AppController.name);
    this.logger.error('22', AppController.name);
    this.logger.warn('33', AppController.name);
    return this.appService.getHello();
  }
}

LoggerService 接口

我们可以借助 LoggerService 接口去自定义一个日志打印器。

首先,新建 MyLogger.ts 文件,定义了三种级别的日志打印:

import { Injectable, LoggerService } from '@nestjs/common';

@Injectable()
export class MyLogger implements LoggerService {
  /**
   * Write a 'log' level log.
   */
  log(message: any, ...optionalParams: any[]) {
    console.log(`---log---`, message)
  }
  
  /**
   * Write an 'error' level log.
   */
  error(message: any, ...optionalParams: any[]) {
    console.log(`---error---`, message)
  }

  /**
   * Write a 'warn' level log.
   */
  warn(message: any, ...optionalParams: any[]) {
    console.log(`---warn---`, message)
  }
}

接着,在 main.ts 中引入该类,

logger: new MyLogger(),

当启动日志时,可以看到打印了

---log--- Starting Nest application...
---log--- AppModule dependencies initialized
---log--- AppController {/}:
---log--- Mapped {/, GET} route
---log--- Nest application successfully started

当访问 http://localhost:3000/ 时,打印的是:

---log--- 11 
---error--- 22
---warn--- 33

也就是生效了。

继承 ConsoleLogger

我们也可以不需要自己去实现 LoggerService 的全部方法,而是通过继承 ConsoleLogger,并重写一些方法:

import { ConsoleLogger } from '@nestjs/common';

export class MyLogger2 extends ConsoleLogger {
  log(message: string, context: string) {
    console.log(`log:`, message)
  }
  error(message: any, stack?: string, context?: string) {
    console.log(`error:`, message)
  }
}

我们查看对应的定义,可以发现 ConsoleLogger 继承了 LoggerService 接口,那么没重写的方法就是原来的打印内容和样式:

export declare class ConsoleLogger implements LoggerService {
  // ...
}

依赖注入

为了在自定义的 logger 中启用依赖注入,我们创建一个 MyLogger3 类继承了 ConsoleLogger 类,并在模块中注册为一个 provider 。具体是:

在 main.ts 中设置 bufferLogs: true ,也就是将日志缓存起来,并可以传给自定义的 logger ,也就是使用 useLogger 指定了 logger 并且等待应用初始化完毕即可。

async function bootstrap() {
  const app = await NestFactory.create(AppModule, {
    bufferLogs: true,
  });
  app.useLogger(app.get(MyLogger3));
  await app.listen(3000);
}

我们新建 MyLogger3.ts 文件,定义装饰器 MyLogger3 继承了 ConsoleLogger 类。

添加 @Injectable() 装饰器,表明这是一个 provider,需要在 Module 里引入;并通过 @Inject 注入 AppService,并在 log 方法中进行调用。

import { ConsoleLogger, Injectable, Inject } from '@nestjs/common';
import { AppService } from './app.service';

@Injectable()
export class MyLogger3 extends ConsoleLogger {
  @Inject(AppService)
  private appService: AppService;

  log(message, context) {
    console.log(this.appService.getHello());
    console.log(`[${context}]`, message);
    console.log('--------------');
  }
}

pnpm start 运行项目,可以观察到打印的日志为:

Hello World!
[NestFactory] Starting Nest application...
--------------
Hello World!
[InstanceLoader] AppModule dependencies initialized
--------------
Hello World!
[RoutesResolver] AppController {/}:
--------------
Hello World!
[RouterExplorer] Mapped {/, GET} route
--------------
Hello World!
[NestApplication] Nest application successfully started
--------------

也就是说,依赖注入成功实现了。

后记

总的来说,Nest 中的日志打印可以指定 logger 是否开启、指定打印的日志级别、自定义 logger 等。

在本文中,我们探索了 Logger 类、LoggerService 接口,以及通过继承 ConsoleLogger 以实现自定义的 logger 等。此外,我们还学习了如何实现依赖注入的方法。

在 Nest 后端应用中,学会打印和查看日志也是掌握 Nest 的很重要的一环。

参考