第二十一章 Nest 日志打印

在之前 我们打印日志信息的时候 都是通过 console.log 来打印信息 ,但是这样打印 存在很多缺陷 例如没有日志的不同级别的区分不能通过开关控制是否打印等等 。而本章将要学习的Nest 提供了打印日志的 api就能很好的解决这些问题

Nest 提供了打印日志的 API 与 console.log 相比具有以下优点:
1. 日志级别控制:Nest 提供了不同级别的日志记录,如调试、信息、警告和错误。这使得开发人员可以根据需要选择要记录的详细程度。与 console.log 不同,Nest 的日志 API 允许您将不同级别的日志记录到不同的目标,例如文件、数据库或外部日志服务。
2. 可配置性:Nest 的日志 API 允许您使用各种日志器(loggers)和传输器(transports)进行自定义配置。您可以根据特定需求选择适合的日志器和传输器,以便将日志记录到不同的目标,并设置格式、过滤和其他参数。
3. 上下文信息:Nest 的日志 API 允许您在日志中添加上下文信息,例如请求 ID、用户 ID 或其他自定义信息。这有助于更好地理解和追踪日志,尤其是在调试和故障排除时。
4. 集成测试:与 console.log 不同,Nest 的日志 API 可以在集成测试中更容易进行模拟和调整。您可以使用模拟的日志器来捕获和验证应用程序打印的日志,以验证预期的行为和输出。

接下来我们来详细学习一下,实现先创建一个nest项目:

nest new logger-test

1720926130576.png
运行项目:

nest start --watch 

可以看到项目启动之后也会打印了一些日志
1720926714804.png
接着我们在AppController 里创建个 logger 对象,使用它的 api 打印日志信息:

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

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) { }

  private logger = new Logger()

  @Get()
  getHello(): string {

    this.logger.log('打印信息1', AppController.name)
    this.logger.warn('打印信息2', AppController.name)
    this.logger.debug('打印信息3', AppController.name)
    this.logger.error('打印信息4', AppController.name)
    this.logger.verbose('打印信息5', AppController.name)


    return this.appService.getHello();
  }
}

游览器访问 http://localhost:3000/ 可以看到打印出了如下信息,其中 verbose、debug、log、warn、error 是日志的级别,[] 是 context(当前所在的上下文),最后是日志的内容
1720927083337.png
且使用 Logger 打印日志信息 和 console.log 的区别是 Logger能在全局控制是否打印日志 打印哪种级别的日志 例如下面:
1720927524100.png
1720927580556.png

此外 还可以自定义日志打印的方式,我们可以定义一个实现 LoggerService 接口的类 在src目录下创建 my-logger.ts

import { LoggerService, LogLevel } from "@nestjs/common";

export class MyLogger implements LoggerService {
    log(message: string, context: string) {
        console.log(`---log---[${context}]---`, message)
    }

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

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

修改 main.ts 指定自定义的 logger:

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { MyLogger } from './my-logger';

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

重新启动项目之后可以看到日志打印如下:
1720928501241.png
当然这样的样式很丑 所以我们可以不自己实现 LoggerService 的全部方法,而是继承 ConsoleLogger,重写一些方法,新增 my-logger2.ts

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

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

可以看到只有 log 类型的日志是自定义的,其他的都是原本的方法
1720930113705.png
我们可以看到logger 有3种值,但是没法注入依赖 因为 Logger 是在容器外面,手动 new 的对象
1720936215938.png
所以下面的我们将解决这个问题 首先我们创建一个 Logger 类 创建 my-logger3.ts,添加 @Injectable() 装饰器,代表这是一个 provider

import { Inject } from '@nestjs/common';
import { ConsoleLogger, Injectable } 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('----------')
    }
}

接着在全局main.ts中使用bufferLogs ,bufferLogs 的作用是先不打印日志,把它放到 buffer 缓冲区,直到用 useLogger 指定了 Logger 并且应用初始化完毕,并使用 app.useLogger 然后使用app.get 就是从容器中取这个类的实例

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { MyLogger } from './my-logger';
import { MyLogger2 } from './my-logger2';
import { MyLogger3 } from './my-logger3';

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

最后在 app.module.ts 中引入

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { MyLogger3 } from './my-logger3';

@Module({
  imports: [],
  controllers: [AppController],
  providers: [AppService, MyLogger3],
})
export class AppModule { }

可以看到最后实现的日志如下,logger 里成功注入了 appService 的依赖1720937680664.png

我们可以单独创建一个模块来放置Logger 并把这个Module 设置为全局模块,首先创建模块

nest g module logger

1720939307744.png
设置为全局模块 并使用MyLogger

import { Global, Module } from '@nestjs/common';
import { MyLogger } from 'src/my-logger';

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

我们创建一个新模块:

nest g resource test

1720937937876.png
在此模块中直接注入

import { Controller, Get, Post, Body, Patch, Param, Delete, Inject } from '@nestjs/common';
import { TestService } from './test.service';
import { CreateTestDto } from './dto/create-test.dto';
import { UpdateTestDto } from './dto/update-test.dto';
import { MyLogger } from 'src/my-logger';

@Controller('test')
export class TestController {

  @Inject(MyLogger)
  private logger: MyLogger;

  constructor(private readonly testService: TestService) { }

  @Post()
  create(@Body() createTestDto: CreateTestDto) {
    return this.testService.create(createTestDto);
  }

  @Get()
  findAll() {
    this.logger.log('测试', TestController.name)
    return this.testService.findAll();
  }

  @Get(':id')
  findOne(@Param('id') id: string) {
    return this.testService.findOne(+id);
  }

  @Patch(':id')
  update(@Param('id') id: string, @Body() updateTestDto: UpdateTestDto) {
    return this.testService.update(+id, updateTestDto);
  }

  @Delete(':id')
  remove(@Param('id') id: string) {
    return this.testService.remove(+id);
  }
}

1720938578681.png
最后访问 http://localhost:3000/test 可以看到成功注入了 MyLogger
1720939817088.png

  • 29
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

枫ゞ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值