5
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

WinstonでNestJSのロガーをカスタマイズしてみた

Last updated at Posted at 2022-05-22

はじめに

今回は、Node.jsのログ出力ライブラリである winston を使用して、NestJS用にログファイルやコンソールログをカスタマイズしていきたいと思います。
ログファイルは日付毎にローテートしたいので、winston-daily-rotate-file というライブラリも使用します。
※NestJSのプロジェクトがすでに作成済みであることを前提にしています。

ログ出力ライブラリをインストール

NestJSのプロジェクト内で、winston と winston-daily-rotate-file をインストールします。

$ npm i winston winston-daily-rotate-file

Service と Module を生成

NestJS CLI で、logging.service.ts と logging.module.ts を生成します。

$ nest g service logging
$ nest g module logging

ファイルが生成されたら、まずは logging.service.ts に、winstonを使用したコードを書いていきます。

src/logging/logging.service.ts
import { Injectable, LoggerService } from '@nestjs/common';
import * as winston from 'winston';
import * as winstonDailyRotateFile from 'winston-daily-rotate-file';

@Injectable()
export class LoggingService implements LoggerService {
  logger: winston.Logger;

  constructor() {
    const logger = winston.createLogger({
      format: winston.format.combine(
        winston.format.timestamp({
          format: 'YYYY-MM-DD HH:mm:ss', // タイムスタンプのフォーマットを変える
        }),
        winston.format.errors({ stack: true }), // エラー時はスタックトレースを出力する
        winston.format.printf(
          (info) => `[${info.timestamp}] [${info.level}] ${info.message}`, // 出力内容をカスタマイズする
        ),
      ),
      transports: [
        // アクセスログの出力先設定
        new winstonDailyRotateFile({
          level: 'debug', // debugを指定すると、debug以上のログが出力される
          datePattern: 'YYYY-MM-DD', // 'YYYY-MM-DD'に設定すると、ログファイルが日付毎に作られる
          filename: 'application-%DATE%.log', // 保存先ファイル名(上記のdatePatternが含まれる)
          dirname: 'logs', // ログファイルの保存先ディレクトリ名
          maxSize: '20m', // ローテートするファイルの最大サイズ
          maxFiles: '30d', // 保存するログの最大数(日数を使う場合は接尾辞として'd'を追加)
        }),
        // エラーログの出力先設定
        new winstonDailyRotateFile({
          level: 'error',
          datePattern: 'YYYY-MM-DD',
          filename: 'error-%DATE%.log',
          dirname: 'logs',
          maxSize: '20m',
          maxFiles: '30d',
        }),
      ],
    });

    // 本番環境以外ではコンソールにも出力する
    if (process.env.NODE_ENV !== 'production') {
      logger.add(
        new winston.transports.Console({
          level: 'debug',
          format: winston.format.combine(
            winston.format.colorize(), // ログを色付けする
            winston.format.simple(), // フォーマットをsimpleにする
          ),
        }),
      );
    }

    this.logger = logger;
  }

  // NestJS標準のLoggerServiceをimplementsしているため、log() error() warn() debug() verbose()を実装する
  log(message: string) {
    this.logger.log({
      level: 'info',
      message: `${message}`,
    });
  }

  error(message: string, trace: string) {
    this.logger.log({
      level: 'error',
      message: `${message}:${trace}`,
    });
  }

  warn(message: string) {
    this.logger.log({
      level: 'warn',
      message: `${message}`,
    });
  }

  debug(message: string) {
    this.logger.log({
      level: 'debug',
      message: `${message}`,
    });
  }

  verbose(message: string) {
    this.logger.log({
      level: 'verbose',
      message: `${message}`,
    });
  }
}

次は、logging.module.ts に LoggingService の設定を行います。

src/logging/logging.module.ts
import { Module } from '@nestjs/common';

import { LoggingService } from './logging.service';

@Module({
  providers: [LoggingService],
  exports: [LoggingService],
})
export class LoggingModule {}

カスタムロガーをデフォルトに設定する

main.tsを以下のように修正して、今回作成したカスタムロガーの設定を行います。

src/main.ts
import { AppModule } from './app.module';
import { LoggingService } from './logging/logging.service';

async function bootstrap() {
  const app = await NestFactory.create(AppModule, {
    logger: new LoggingService(), // ここに追加
  });

  await app.listen(3000);
}

bootstrap();

カスタムロガーを試してみる

AppService の getHello() でログを出力してみたいと思います。

src/app.service.ts
import { Injectable } from '@nestjs/common';

import { LoggingService } from './logging/logging.service';

@Injectable()
export class AppService {
  constructor(private readonly logger: LoggingService) {}
  getHello(): string {
    // ログを出力してみる
    this.logger.debug('Debug Message');
    this.logger.log('Info Message');
    this.logger.warn('Warn Message');
    this.logger.error('Error Message', 'error in AppService');

    return 'Hello World!';
  }
}

では localhost:3000 にアクセスしてみます。
すると、以下のコンソールログが出力されました。

debug: Debug Message {"timestamp":"YYYY-MM-DD HH:mm:ss"}
info: Info Message {"timestamp":"YYYY-MM-DD HH:mm:ss"}
warn: Warn Message {"timestamp":"YYYY-MM-DD HH:mm:ss"}
error: Error Message:error in AppService {"timestamp":"YYYY-MM-DD HH:mm:ss"}

logsディレクトリには、applicationログとerrorログの2つのファイルが出力されました。
それぞれのファイルの内容は以下の通りです。

logs/application-{YYYY-MM-DD}.log
[YYYY-MM-DD HH:mm:ss] [debug] Debug Message
[YYYY-MM-DD HH:mm:ss] [info] Info Message
[YYYY-MM-DD HH:mm:ss] [warn] Warn Message
[YYYY-MM-DD HH:mm:ss] [error] Error Message:error in AppService
logs/error-{YYYY-MM-DD}.log
[YYYY-MM-DD HH:mm:ss] [error] Error Message:error in AppService

errorログの方には error レベルのログのみ出力されていることが確認できるかと思います。

おわりに

このように、特に前知識がなくても手軽にカスタムロガーを作成することができました。
ログに必要な情報というのはそれぞれの案件ごとに違ってくるものですので、必要に応じて自由にカスタマイズできるのが良いですね。

5
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?