アレについて記す

Nest(TypeScript)で遊んでみる 〜Logging編〜

Posted on September 09, 2018 at 22:00 (JST)

今回はNestにて log4js-node を使用する方法について記載する。
Nestの公式ドキュメントに記載されている中から、DIを利用する方法を選択し実装した。

流れは下記の通り。

  1. ライブラリを追加
  2. Loggerを作成
  3. DIするためのモジュール作成
  4. フレームワーク本体のログ出力処理に使用するための設定変更
  5. ローカル開発時は debug レベル以上のログを出力するよう設定

作成したコード [ nest-angular-sample: works/02_logging ]

動作環境

OS: macOS High Sierra ver. 10.13.4
Nodejs: v8.10.0
npm: 5.6.0
nest: 5.4.0

1. ライブラリを追加

下記コマンドで log4js をインストールする

$ npm install --save log4js

2. Loggerを作成

log4js を利用してログ出力を行うためのServiceを用意する。
フレームワーク本体のログ出力処理を統合するため、@nestjs/common/LoggerService を implements する。
log4jsの設定については log4js-nodeの公式ドキュメント を参照。

今回のサンプルは出力対象となる全てのログは標準出力、かつエラーログのみファイル出力を行なっている。
出力可能とするエラーレベルは環境変数で指定し、未指定の場合は info 以上のもののみ出力するようにしている。

[src/logger/logger.service.ts]

import { LoggerService } from '@nestjs/common';
import { configure, getLogger } from 'log4js';

const defaultLogger = getLogger();
const defaultLoggerLevel = process.env.LOGGER_LEVEL || 'info';

const layout = {
  type: 'pattern',
  pattern: '%d{ISO8601_WITH_TZ_OFFSET} [%p] %m',
};

configure({
  appenders: {
    stdout: { type: 'stdout', layout },
    file: {
      type: 'file',
      filename: 'application.error.log',
      layout
    },
  },
  categories: {
    default: { appenders: ['stdout'], level: defaultLoggerLevel },
    error: { appenders: ['stdout', 'file'], level: 'error' },
  },
});

export class Logger implements LoggerService {
  log(message: string) {
    this.info(message);
  }

  debug(message: string) {
    defaultLogger.debug(message);
  }

  info(message: string) {
    defaultLogger.info(message);
  }

  warn(message: string) {
    defaultLogger.warn(message);
  }

  error(message: string, trace?: string) {
    const errorLogger = getLogger('error');
    if (trace) {
      errorLogger.error(`${message}\n${trace}`);
    } else {
      errorLogger.error(message);
    }
  }
}

※ ログファイル出力ではファイルローテーションを考慮する必要があるが、サンプルなので割愛。

3. DIするためのモジュール作成

作成した Logger を読み込む Module を作成する。

[src/logger/logger.module.ts]

import { Module } from '@nestjs/common';
import { Logger } from './logger.service';

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

作成した LoggerModuleAppModuleimports に指定する。

[src/app.module.ts]

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { LoggerModule } from './logger/logger.module';

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

4. フレームワーク本体のログ出力処理に使用するための設定変更

下記ソースでは app.get(Logger) にてDIコンテナからLoggerインスタンスを取得している。

DIでLoggerを使用する場合でも、モジュール初期化時のログにだけは適用できない点に注意が必要。
モジュール初期化時(DIコンテナにLoggerインスタンスを格納する処理を含む)のログはNestのデフォルトLoggerを使用して出力される。

上記ログのみフォーマット等が揃わないのが気になる場合、アプリケーション初期化処理メソッドのオプションとして logger: false を指定するとモジュール初期化時のログが出力されなくなる。

[src/main.ts]

  const app = await NestFactory.create(AppModule, {
    logger: false,
  });
  app.useLogger(app.get(Logger));

src/main.hmr.ts も同様の設定を行う。

5. ローカル開発時は debug レベル以上のログを出力するよう設定

$ npm run start:hmr 実行時に環境変数へ LOGGER_LEVEL=debug を追加する。

[package.json]

  "start:hmr": "LOGGER_LEVEL=debug node dist/server",

以上。


参考URL