乐闻世界logo
搜索文章和话题

Nestjs 如何使用日志服务?

7 个月前提问
3 个月前修改
浏览次数139

6个答案

1
2
3
4
5
6

在 NestJS 中使用日志服务可以通过多种方式实现,最常见的是使用 NestJS 内置的日志器(Logger)服务,或者集成第三方日志库(如 Winston 或 Pino)。以下是如何在 NestJS 中使用内置的 Logger 服务以及集成 Winston 作为日志服务的基本步骤。

使用 NestJS 内置 Logger 服务

  1. 导入 Logger 服务: NestJS 提供了一个内置的 Logger 类,您可以在任何服务或控制器中直接使用它。

    typescript
    import { Logger } from '@nestjs/common';
  2. 实例化 Logger: 在您的服务或控制器中创建一个 Logger 实例。

    typescript
    private readonly logger = new Logger(MyService.name);
  3. 使用 Logger: 现在可以在类的任何方法中使用这个 logger 来记录日志。

    typescript
    someMethod() { this.logger.log('Some informative message'); this.logger.error('An error occurred', error.stack); this.logger.warn('A warning message'); this.logger.debug('Some debug information'); }
  4. 自定义 Logger: 如果需要更改日志级别或自定义日志行为,可以通过继承 Logger 类并重写其方法来完成。

    typescript
    import { Logger, Injectable, Scope } from '@nestjs/common'; @Injectable({ scope: Scope.TRANSIENT }) class MyLogger extends Logger { // 自定义日志的逻辑 }

集成第三方日志库(以 Winston 为例)

  1. 安装 Winston 相关依赖:

    sh
    npm install winston @nestjs/common @nestjs/core
  2. 创建一个 Winston 模块: 创建一个模块来封装 Winston 的配置和提供器。

    typescript
    import { Module } from '@nestjs/common'; import { WinstonModule } from 'nest-winston'; import * as winston from 'winston'; @Module({ imports: [ WinstonModule.forRoot({ // Winston 的配置 transports: [ new winston.transports.Console(), // 其他 transports 如文件或远程日志服务 ], }), ], exports: [WinstonModule], }) export class LoggerModule {}
  3. 在应用中使用 Winston: 在其他模块中导入 LoggerModule 并在服务中注入 WINSTON_MODULE_PROVIDER 作为日志器。

    typescript
    import { Inject, Injectable } from '@nestjs/common'; import { Logger } from 'winston'; import { WINSTON_MODULE_PROVIDER } from 'nest-winston'; @Injectable() class MyService { constructor( @Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger ) {} someMethod() { this.logger.info('Some informative message'); this.logger.error('An error occurred'); } }

使用自定义的日志级别和格式

NestJS 的内置日志器或第三方日志库允许您定义自定义的日志级别和格式。这可以通过修改配置来实现,例如,在使用 Winston 时,您可以自定义 transportsformat 选项以改变日志的输出格式和目的地。

typescript
transports: [ new winston.transports.Console({ format: winston.format.combine( winston.format.timestamp(), winston.format.printf(info => `${info.timestamp} ${info.level}: ${info.message}`) ), }), // 其他 transports ]

在实际生产环境中,您可能还需要考虑日志的持久化存储、日志的分析、监控警报等高级功能,这通常需要与相关的基础设施和服务集成,如 ELK(Elasticsearch, Logstash, Kibana)堆栈、AWS CloudWatch、GCP Stackdriver 等。

以上是在 NestJS 中使用日志服务的一些基础步骤和实践,当然根据具体的业务需求和系统复

2024年6月29日 12:07 回复

最佳实践

比静态访问更好的Logger是为您的类创建一个实例:

shell
@Controller() export class AppController { private readonly logger = new Logger(AppController.name); @Get() async get() { this.logger.log('Getting stuff'); } }

为什么这样更好?

  1. 您可以在构造函数中提供上下文,new Logger(AppController.name)以便类名(或其他任何内容)将成为此类中所有日志消息的一部分。

  2. 如果您在某些时候想要扩展或替换默认值LoggerService,除了设置新记录器之外,您不需要更改任何应用程序代码。您的新记录器将自动被使用。如果您静态访问它,它将继续采用默认实现。

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

  3. Logger您可以在测试中模拟:

    module.useLogger(new NoOpLogger());

2024年6月29日 12:07 回复

首先需要将 Logger 导入到您的 Class 中:

shell
import { Logger } from '@nestjs/common';

然后就可以开始记录日志:

shell
Logger.log('info') Logger.warn('warning') Logger.error('something went wrong! ', error)
2024年6月29日 12:07 回复

最佳实践是注入现有的记录器。

app.module.ts

shell
import { Logger, Module } from '@nestjs/common'; import { AppController } from './app.controller'; import { AppService } from './app.service'; @Module({ imports: [], controllers: [AppController], providers: [AppService, Logger], }) export class AppModule {}

并在_app.service.ts中_

shell
import { Injectable, Logger } from '@nestjs/common'; @Injectable() export class AppService { constructor(private readonly logger: Logger) {} sayHello() { this.logger.log('Hello world!') } }
2024年6月29日 12:07 回复

这个答案可能对尝试使用 CustomLogger 实现的其他人有用。我试图展示一个示例自定义记录器实现以及如何将其注入到 Nestjs 框架中。

据我所知,Nestjs 本质上使用 pino 记录器。这只是记录器服务的自定义实现(您可以用bunyan、winston 等替换它。)这是我使用的文件夹结构:

shell
> src / > modules / > database / > ... > database.module.ts > api / > services / > controllers / > interceptors / > middlewares / > models / > schemas / > shared / > services / > app.util.service.ts > pino.logger.service.ts > utils / > interceptors / > filters / > main.ts > app.controller.ts > app.service.ts > server.util.service.ts

这是它的主要要点。所以logger服务的实现如下

shell
import {Injectable, LoggerService, Scope} from "@nestjs/common"; import * as pino from 'pino'; import {AppUtilService} from "./app.util.service"; import * as os from "os"; import {APP_LOG_REDACT, APP_MESSAGE_KEY} from "../utils/app.constants"; @Injectable({ scope: Scope.DEFAULT }) export class PinoLoggerService implements LoggerService{ constructor(private appUtilService: AppUtilService) { } logService = (fileNameString): pino.Logger => { return pino({ useLevelLabels: true, prettyPrint: this.appUtilService.isDevEnv(), // tslint:disable-next-line: object-literal-sort-keys messageKey: APP_MESSAGE_KEY, level: this.appUtilService.getLogLevel(), redact: { paths: APP_LOG_REDACT, censor: '**SECRET-INFO**' }, base: { hostName: os.hostname(), platform: os.platform(), processId: process.pid, timestamp: this.appUtilService.getCurrentLocaleTimeZone(), // tslint:disable-next-line: object-literal-sort-keys fileName: this.appUtilService.getFileName(fileNameString), }, }); } debug(message: any, context?: string): any { } error(message: any, trace?: string, context?: string): any { } log(message: any, context?: string): any { } warn(message: any, context?: string): any { } }

自定义实现是使用 pinojs github 中我的特定选项实现的,我使用 fastifyjs 而不是express(再次满足我的项目需求)。所以我在 fastify js 服务器选项中添加了记录器。如果您使用的是 Express,最好在 Nest 应用程序适配器中指定新的自定义实现,如上所述。

我的 util 服务负责实现 fastify 服务器

shell
import * as fastify from "fastify"; import {Http2Server, Http2ServerRequest, Http2ServerResponse} from "http2"; import {DocumentBuilder, SwaggerModule} from "@nestjs/swagger"; import * as fs from "fs"; import * as path from "path"; import * as uuid from "uuid"; import * as qs from "query-string"; import {PinoLoggerService} from "./modules/shared/services/pino.logger.service"; import {AppUtilService} from "./modules/shared/services/app.util.service"; import {AppConstantsService} from "./modules/shared/services/app.constants.service"; import {AppModel} from "./modules/shared/model/app.model"; import {Reflector} from "@nestjs/core"; export class ServerUtilService { private logService; private appConstantsService; private appUtilServiceInstance: AppUtilService; private fastifyInstance: fastify.FastifyInstance<Http2Server, Http2ServerRequest, Http2ServerResponse>; constructor() { this.appUtilServiceInstance = new AppUtilService(); this.logService = new PinoLoggerService(this.appUtilServiceInstance); this.appConstantsService = new AppConstantsService(this.appUtilServiceInstance); } retrieveAppConstants(): AppModel { return this.appConstantsService.getServerConstants(); } retrieveAppUtilService(): AppUtilService { return this.appConstantsService; } createFastifyServerInstance = (): fastify.FastifyInstance<Http2Server, Http2ServerRequest, Http2ServerResponse> => { const serverConstants = this.appConstantsService.getServerConstants(); const httpsOptions = { cert: fs.readFileSync(path.join(process.cwd() + '/https-keys/cert.pem')), key: fs.readFileSync(path.join(process.cwd() + '/https-keys/key.pem')), allowHTTP1: true, rejectUnauthorized: true, }; this.fastifyInstance = fastify({ http2: true, https: httpsOptions, bodyLimit: 26214400, pluginTimeout: 20000, genReqId: () => { return uuid.v4().toString(); }, requestIdHeader: serverConstants.requestIdHeader, modifyCoreObjects: true, trustProxy: serverConstants.trustProxy, ignoreTrailingSlash: true, logger: this.logService, querystringParser: (str) => { return qs.parse(str); }, }); this.addContentTypeParser(); return this.fastifyInstance; }; private addContentTypeParser() { this.fastifyInstance.addContentTypeParser('*', (req, done) => { let data = ''; req.on('data', chunk => { console.log('inside data listener event'); return data += chunk; }); req.on('end', () => { done(null,data); }) }); } } export const ServerUtilServiceInstance = new ServerUtilService();

在我的 main.ts 中

shell
async function bootstrap() { const fastifyServerInstance = ServerUtilServiceInstance.createFastifyServerInstance(); const serverConstants = ServerUtilServiceInstance.retrieveAppConstants(); const app: NestFastifyApplication = await NestFactory.create<NestFastifyApplication>( AppModule, new FastifyAdapter(fastifyServerInstance) ); .... ... // global filters, interceptors, pipes .... await app.listen(serverConstants.port, '0.0.0.0'); }
2024年6月29日 12:07 回复

您可以简单地使用记录器来满足您的要求(对于错误,对于警告)。这是它的示例代码。

shell
import {Logger, Injectable} from '@nestjs/common'; @Injectable() export class EmployersService { private readonly logger = new Logger(EmployersService.name); findAll() { this.logger.log('info message'); //for info this.logger.warn('warn message'); //for warn this.logger.error('error message'); //for error } }

然后输出:

在此输入图像描述

2024年6月29日 12:07 回复

你的答案