跳到主要内容

日志记录器

Nest自带一个内置的基于文本的日志记录器,在应用程序引导和其他一些情况下(例如,显示捕获的异常,即系统日志记录)中使用。 此功能通过@nestjs/common包中的Logger类提供。 您可以完全控制日志记录系统的行为,包括以下任何内容:

  • 禁用日志记录
  • 指定日志详细级别(例如,显示错误、警告、调试信息等)
  • 覆盖默认日志记录器中的时间戳(例如,使用ISO8601标准作为日期格式)
  • 完全覆盖默认日志记录器
  • 通过扩展自定义日志记录器自定义默认日志记录器
  • 利用依赖注入简化组合和测试应用程序

您还可以使用内置日志记录器,或创建自己的自定义实现,以记录自己的应用程序级事件和消息。

对于更高级的日志记录功能,您可以使用任何Node.js日志记录包(例如Winston) 来实现完全自定义的、生产级别的日志记录系统。

基本定制

要禁用日志记录,请在传递给NestFactory.create()方法的第二个参数中的(可选的) Nest应用程序选项对象中将logger属性设置为false

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

要启用特定的日志记录级别, 请将logger属性设置为指定要显示的日志级别的字符串数组,如下所示:

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

数组中的值可以是以下任意组合:'log''fatal''error''warn''debug''verbose'

备注

要在默认日志记录器的消息中禁用颜色,请将NO_COLOR环境变量设置为某个非空字符串。

自定义实现

您可以提供一个自定义的日志记录器实现, 用于Nest的系统日志记录,方法是将logger属性的值设置为满足LoggerService接口的对象。 例如,可以告诉Nest使用内置的全局JavaScript控制台对象(实现了LoggerService接口), 如下所示:

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

实现自己的自定义日志记录器非常简单。只需按照以下所示的LoggerService接口的每个方法实现即可。

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

export class MyLogger implements LoggerService {
log(message: any, ...optionalParams: any[]) {}
fatal(message: any, ...optionalParams: any[]) {}
error(message: any, ...optionalParams: any[]) {}
warn(message: any, ...optionalParams: any[]) {}
debug?(message: any, ...optionalParams: any[]) {}
verbose?(message: any, ...optionalParams: any[]) {}
}

然后,通过Nest应用程序选项对象的logger属性提供MyLogger的实例。

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

尽管这种技术很简单,但它不使用MyLogger类的依赖项注入。 这可能会带来一些挑战,尤其是在测试时,并且可能会限制MyLogger的可重用性。 有一个更好的解决方案, 请参见下面的依赖注入部分。

扩展内置日志记录器

与其从头开始编写一个日志记录器,不 如通过扩展内置的ConsoleLogger类并覆盖默认实现的选定行为来满足您的需求。

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

export class MyLogger extends ConsoleLogger {
error(message: any, stack?: string, context?: string) {
// 在这里添加您的定制逻辑
super.error(...arguments);
}
}

您可以在特性模块中使用这样的扩展日志记录器, 如下面的用于应用程序日志记录的日志记录器部分所述。

您可以通过通过应用程序选项对象的logger属性传递其实例, 或者通过下面的依赖注入部分中所示的技术来使用您的扩展日志记录器进行系统日志记录。 如果这样做,确保调用super,如上面示例代码中所示,以将特定日志方法的调用委托给父类(内置)类, 以便Nest可以依赖于它期望的内置特性。

依赖注入

对于更高级的日志记录功能,您可能希望利用依赖注入。 例如,您可能希望将ConfigService注入到您的日志记录器中进行自定义, 并将自定义日志记录器注入到其他控制器和/或提供程序中。 要启用对自定义日志记录器的依赖注入,请创建一个实现LoggerService的类, 并在某个模块中将该类注册为提供程序。例如,您可以:

  1. 定义一个MyLogger类,该类可以扩展内置的ConsoleLogger或完全覆盖它,如前面的部分所示。确保实现LoggerService接口。
  2. 创建一个LoggerModule,如下所示,并从该模块提供MyLogger
import { Module } from '@nestjs/common';
import { MyLogger } from './my-logger.service';

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

有了这个结构,您现在为任何其他模块提供了自定义日志记录器。 由于MyLogger类是模块的一部分,它可以使用依赖注入(例如,注入ConfigService)。 还需要一种技术来为Nest用于系统日志记录(例如引导和错误处理)提供此自定义日志记录器的单例实例。

由于应用程序实例化(NestFactory.create())发生在任何模块的上下文之外,它不参与初始化的常规依赖注入阶段。 因此,我们必须确保至少一个应用程序模块导入了LoggerModule,以触发Nest实例化我们的MyLogger类的单例实例。

然后,我们可以使用以下构造指示Nest使用MyLogger的相同单例实例:

const app = await NestFactory.create(AppModule, {
bufferLogs: true,
});
app.useLogger(app.get(MyLogger));
await app.listen(3000);
备注

在上面的示例中,我们将bufferLogs设置为true,以确保所有日志将被缓冲, 直到附加自定义日志记录器(在这种情况下是MyLogger),并且应用程序初始化过程完成或失败。 如果初始化过程失败,Nest将退回到原始的ConsoleLogger,以打印出任何报告的错误消息。 此外,您可以将autoFlushLogs设置为false(默认为true)来手动刷新日志(使用Logger#flush()方法)。

在这里,我们使用NestApplication实例上的get()方法检索MyLogger对象的单例实例。 这种技术本质上是一种为Nest提供日志记录器实例的方式。 app.get()调用检索MyLogger的单例实例,并依赖于该实例首先在另一个模块中被注入,如上所述。

您还可以在您的特性类中注入此MyLogger提供程序,从而确保在Nest系统日志记录和应用程序日志记录中实现一致的日志行为。 有关更多信息,请参见下面的用于应用程序日志记录的日志记录器注入自定义日志记录器部分。

使用日志记录器进行应用程序日志记录

我们可以结合上面介绍的几种技术, 以在Nest系统日志记录和我们自己的应用程序事件/消息日志记录之间提供一致的行为和格式。

一种良好的做法是在每个服务中实例化@nestjs/common包中的Logger类。 我们可以在Logger构造函数中的context参数中提供我们的服务名称,如下所示:

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

@Injectable()
class MyService {
private readonly logger = new Logger(MyService.name);

doSomething() {
this.logger.log('Doing something...');
}
}

在默认的日志记录器实现中,上下文被打印在方括号中,就像在以下示例中的NestFactory一样:

[Nest] 19096   - 12/08/2019, 7:12:59 AM   [NestFactory] Starting Nest application...

如果我们通过app.useLogger()提供了自定义日志记录器,它实际上将被Nest在内部使用。 这意味着我们的代码保持实现无关, 同时我们可以通过调用app.useLogger()轻松地用我们的自定义日志记录器替换默认的日志记录器。

这样,如果我们按照前一节的步骤调用app.useLogger(app.get(MyLogger)), 则来自MyServicethis.logger.log()的调用将导致MyLogger实例中的log方法的调用。

这对大多数情况来说应该是合适的。但如果您需要更多的定制(例如添加和调用自定义方法),请转到下一节。

注入自定义日志记录器

首先,使用以下代码扩展内置的日志记录器。我们为ConsoleLogger类的配置元数据提供了scope选项, 指定为瞬态范围,以确保我们在每个特性模块中都有一个唯一的MyLogger实例。 在此示例中,我们没有扩展单个ConsoleLogger方法(如log()warn()等),尽管您可以选择这样做。

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

@Injectable({ scope: Scope.TRANSIENT })
export class MyLogger extends ConsoleLogger {
customLog() {
this.log('Please feed the cat!');
}
}

接下来,创建一个如下所示的LoggerModule

import { Module } from '@nestjs/common';
import { MyLogger } from './my-logger.service';

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

然后,将LoggerModule导入到您的特性模块中。 由于我们扩展了默认的Logger,我们可以使用setContext方法的便利性。 因此,我们可以开始使用具有上下文感知的自定义日志记录器,如下所示:

import { Injectable } from '@nestjs/common';
import { MyLogger } from './my-logger.service';

@Injectable()
export class CatsService {
private readonly cats: Cat[] = [];

constructor(private myLogger: MyLogger) {
// 由于瞬态范围,CatsService拥有自己唯一的MyLogger实例,
// 因此在此处设置上下文不会影响其他服务中的其他实例
this.myLogger.setContext('CatsService');
}

findAll(): Cat[] {
// 您可以调用所有默认方法
this.myLogger.warn('About to return cats!');
// 和您的自定义方法
this.myLogger.customLog();
return this.cats;
}
}

最后,在您的main.ts文件中指示Nest使用自定义日志记录器的实例,如下所示。 当然,在这个例子中,我们实际上没有定制日志记录器的行为(通过扩展Logger方法如log()warn()等), 因此这一步实际上是不需要的。 但如果您向这些方法添加了自定义逻辑并希望Nest使用相同的实现,那么这一步将是必要的

const app = await NestFactory.create(AppModule, {
bufferLogs: true,
});
app.useLogger(new MyLogger());
await app.listen(3000);
备注

另外,也可以使用 logger: false 指令暂时禁用日志记录器,而不是将 bufferLogs 设置为 true。 请注意,如果向 NestFactory.create 提供 logger: false, 在调用 useLogger 之前不会记录任何信息,因此可能会错过一些重要的初始化错误。 如果你不介意使用默认日志记录器记录一些初始信息,可以省略 logger: false 选项。

使用外部日志记录器

生产应用程序通常有特定的日志记录要求,包括高级过滤、格式化和集中日志记录。 Nest 的内置日志记录器用于监控 Nest 系统行为,在开发过程中也可用于功能模块中的基本格式化文本日志记录, 但生产应用程序通常会利用 Winston 等专用日志记录模块。 与任何标准 Node.js 应用程序一样,您可以充分利用 Nest 中的此类模块