日本の山岳一覧・百名山 API を開発しましたCLICK !

NestJS | winston・winston-daily-rotate-fileでログファイルを出力する

NestJS windton カスタムロガー
  • URLをコピーしました!

NestJSでLoggerライブラリwinstonとwinston-daily-rotate-fileを使って、カスタムロガーを作成します!

目次

NestJSのLoggerをwinstonでカスタムする

NestJSのLoggerに関するリファレンスには、以下のような記載があります。

For more advanced logging functionality, you can make use of any Node.js logging package, such as Winston, to implement a completely custom, production grade logging system.

https://docs.nestjs.com/techniques/logger

DeepLで翻訳すると…

より高度なロギング機能については、Winston などの任意の Node.js ロギング・パッケージを使用して、完全にカスタムでプロダクション・グレードのロギング・システムを実装することができます。

製品に組み込むような高度なロギングはカスタムしてくださいね、
ライブラリはwinstonがおすすめだよ(そう書いてはないけど笑)とのことです。

winston

Node.jsのLoggerライブラリであるwinstonを使用します。

他にもライブラリはありますが、NestJSで名前が挙がっているので選定しました。
TypeScript製のライブラリなのでNestJSとの相性も良いのかなと思います。

winston-daily-rotate-file

今回は日付毎にログファイルをローテートしたいので、
winston-daily-rotate-fileというライブラリもインストールしていきます。

カスタムロガーを作成する – customizing Logger

NestJSのアプリ作成済みであることが前提で実装していきます。

npm install

必要なライブラリをインストールします。

npm i winston winston-daily-rotate-file

moduleとserviceを作成する

NestJSを触ったことのある方はよく使うであろうコマンドで、moduleとserviceを作成します。

nest g module logging
nest g service logging

Web APIのエンドポイントを特に設ける必要がないので、コントローラは作成しません。

logging.service.tsを実装する

logging.service.tsでロガーを実装していきます。

import { Injectable, LoggerService } from '@nestjs/common';
import { createLogger, format, Logger, transports } from 'winston';
import * as DailyRotateFile from 'winston-daily-rotate-file';

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

    constructor() {
        // infoログ(通常ログ)の出力先を定義
        const applicationLogTransport: DailyRotateFile = new DailyRotateFile({
            level: 'info',
            filename: 'application-%DATE%.log',
            dirname: 'logs/application',
            datePattern: 'YYYYMMDD',
            maxFiles: '7d',
        });

        // errorログの出力先を定義
        const errorLogTransport: DailyRotateFile = new DailyRotateFile({
            level: 'error',
            filename: 'error-%DATE%.log',
            dirname: 'logs/error',
            datePattern: 'YYYYMMDD',
            maxFiles: '7d',
        });

        this.logger = createLogger({
            level: 'info',
            format: format.combine(
                format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
                format.errors({ stack: true }), // errorログではstackを表示
                format.splat(),
                format.json(),
            ),
            defaultMeta: { service: 'logging', funcName: '' }, // デフォルトの出力項目を指定
            transports: [applicationLogTransport, errorLogTransport],
        });

        // envでログをコンソール出力するか決定する
        // コンソールへの出力を追加しないと開発がしづらいので注意
        if (process.env.ENV_NAME !== 'prod') {
            this.logger.add(
                new transports.Console({
                    level: 'debug',
                    format: format.combine(format.colorize(), format.simple()),
                }),
            );
        }
    }

    // デフォルトのLoggerServiceを継承しているため、log・error・warnは実装しないといけない
    log(message: string, service: string, funcName: string): void {
        this.logger.info(message, {
            service: service,
            funcName: funcName,
            notDefaultMeta: 'デフォルトにないの情報も出力可能です!',
        });
    }

    error(message: string, err: Error, service: string, funcName: string): void {
        this.logger.error(message, err, {
            service: service,
            funcName: funcName,
        });
    }

    warn(message: string): void {
        this.logger.warn(message);
    }
}

dirnameが未指定の場合、プロジェクトのルートディレクトリ上にログが出力されます。

logging.module.ts

exportsを定義します。

import { Module } from '@nestjs/common';
import { LoggingService } from './logging.service';

@Module({
    providers: [LoggingService],
    exports: [LoggingService], // 追加する
})
export class LoggingModule {}

main.tsでデフォルトからカスタムロガーに切り替える

main.tsを修正する。

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { LoggingService } from './logging/logging.service';

async function bootstrap() {
    const app = await NestFactory.create(AppModule, {
        logger: false, // loggerをオフ -> デバッグ時のコンソール出力がなくなる
    });
    // カスタムロガーをセットする
    app.useLogger(app.get(LoggingService));
    await app.listen(3000);
}

bootstrap();

コンパイル失敗などのログが出力されないため、logger: falseは開発時には設定しないほうが無難です。

app.service.tsでloggerを呼び出す

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 {
        // info
        this.logger.log('関数を実行します。', 'app', 'getHello');

        // error
        this.logger.error(
            'エラーが発生しました。',
            new Error('error in app.service'),
            'app',
            'getHello',
        );

        return 'Hello World!';
    }
}

ログを出力させる

NestJSアプリを起動します。

npm run start:dev

コンソールにログが出力されていることも確認しておきましょう!

NestJS winston コンソール

localhost:3000にアクセスし、ログを出力させます。

ログを確認する

infoログ

infoレベル以上のログが出力対象であるため、errorログも出力されています。

{"level":"info","message":"AppController {/}:","notDefaultMeta":"デフォルトにないの情報も出力可能です!","service":"RoutesResolver","timestamp":"2022-03-06 22:51:52"}
{"level":"info","message":"Mapped {/, GET} route","notDefaultMeta":"デフォルトにないの情報も出力可能です!","service":"RouterExplorer","timestamp":"2022-03-06 22:51:52"}
{"level":"info","message":"Nest application successfully started","notDefaultMeta":"デフォルトにないの情報も出力可能です!","service":"NestApplication","timestamp":"2022-03-06 22:51:52"}
{"funcName":"getHello","level":"info","message":"関数を実行します。","notDefaultMeta":"デフォルトにないの情報も出力可能です!","service":"app","timestamp":"2022-03-06 22:52:04"}
{"funcName":"getHello","level":"error","message":"エラーが発生しました。 error in app.service","service":"app","stack":"Error: error in app.service\n    at AppService.getHello (/sample/nestjs-logging/src/app.service.ts:15:13)\n    at AppController.getHello (/sample/nestjs-logging/src/app.controller.ts:10:32)\n    at /sample/nestjs-logging/node_modules/@nestjs/core/router/router-execution-context.js:38:29\n    at InterceptorsConsumer.intercept (/sample/nestjs-logging/node_modules/@nestjs/core/interceptors/interceptors-consumer.js:11:20)\n    at /sample/nestjs-logging/node_modules/@nestjs/core/router/router-execution-context.js:46:60\n    at /sample/nestjs-logging/node_modules/@nestjs/core/router/router-proxy.js:9:23\n    at Layer.handle [as handle_request] (/sample/nestjs-logging/node_modules/express/lib/router/layer.js:95:5)\n    at next (/sample/nestjs-logging/node_modules/express/lib/router/route.js:137:13)\n    at Route.dispatch (/sample/nestjs-logging/node_modules/express/lib/router/route.js:112:3)\n    at Layer.handle [as handle_request] (/sample/nestjs-logging/node_modules/express/lib/router/layer.js:95:5)","timestamp":"2022-03-06 22:52:04"}

errorログ

errorログのみが出力されています。

format.errors({ stack: true })でエラーの際はスタックトレースを出力するようにしています。

{"funcName":"getHello","level":"error","message":"エラーが発生しました。 error in app.service","service":"app","stack":"Error: error in app.service\n    at AppService.getHello (/sample/nestjs-logging/src/app.service.ts:15:13)\n    at AppController.getHello (/sample/nestjs-logging/src/app.controller.ts:10:32)\n    at /sample/nestjs-logging/node_modules/@nestjs/core/router/router-execution-context.js:38:29\n    at InterceptorsConsumer.intercept (/sample/nestjs-logging/node_modules/@nestjs/core/interceptors/interceptors-consumer.js:11:20)\n    at /sample/nestjs-logging/node_modules/@nestjs/core/router/router-execution-context.js:46:60\n    at /sample/nestjs-logging/node_modules/@nestjs/core/router/router-proxy.js:9:23\n    at Layer.handle [as handle_request] (/sample/nestjs-logging/node_modules/express/lib/router/layer.js:95:5)\n    at next (/sample/nestjs-logging/node_modules/express/lib/router/route.js:137:13)\n    at Route.dispatch (/sample/nestjs-logging/node_modules/express/lib/router/route.js:112:3)\n    at Layer.handle [as handle_request] (/sample/nestjs-logging/node_modules/express/lib/router/layer.js:95:5)","timestamp":"2022-03-06 22:52:04"}

まとめ

ログファイル出力の基本的な部分の実装はできました。

あとはログに必要な情報を残すように、meta情報をカスタムしてみてください!

NestJS windton カスタムロガー

この記事が気に入ったら
フォローしてね!

  • URLをコピーしました!

コメント

コメントする

目次