Nest.js + log4js ～標準 Logger を log4js で実装～ 

# 目的

`Nest.js` に `log4js` を使う方法をまとめました。
次の内容を実装します。

* [標準Loggerをlog4jsで実装](https://qiita.com/wakusan-6126/items/c03ae0866bad35a86a6c) (今回)
* [ミドルウェアでリクエスト情報をログに追加](https://qiita.com/wakusan-6126/items/f49ed378b9d05e907bad) (次回)
* [ログ出力時にスタック情報を追加](https://qiita.com/wakusan-6126/items/5cefc13c0aa12be7d3bd) (次次回)

# 事前準備

プロジェクトは Nest.js のCLIコマンドで生成します。

``` (terminal)
npm i -g @nestjs/cli
nest new logger-sample
```

以後、 **標準テンプレートのプロジェクト** をカスタマイズして実装していきます。

`log4js` をインストール

```(terminal)
npm i --save log4js
```

# 標準 Logger を log4js で実装

公式ドキュメントにやり方は書いてあります。 [Nest.js-TECHNIQUES-Logger](https://docs.nestjs.com/techniques/logger)

## LoggerServiceを実装

標準 `Logger` を書き換えるために `LoggerService` を実装します。

``` typescript:logger-sample/src/logger/logger.service.ts
import { LoggerService, Injectable, Scope } from '@nestjs/common';
import { Logger } from 'log4js';

@Injectable({ scope: Scope.TRANSIENT })
export class CustomLogger implements LoggerService{

    constructor(private readonly logger: Logger) {}

    log(message: any, context?: string) {
        this.logger.log(message,context? context : '');
    }
    error(message: any, trace?: string, context?: string) {
        this.logger.error(message,trace? trace : '',context? context : '');
    }
    warn(message: any, context?: string) {
        this.logger.warn(message,context? context : '');
    }
    debug?(message: any, context?: string) {
        this.logger.debug(message,context? context : '');
    }    
}
```

* リクエスト毎に出力情報は変わるため、スコープは `TRANSIENT` にしています。
* `log4js` の `Logger` はコンストラクタから渡すようにしました。
* `verbose()` は使わないため実装していません。

## LoggerModule を実装

 `CustomLogger` を公開するために `LoggerModule` を実装します。

 ``` typescript:logger-sample/logger/logger.module.ts
import { Module } from "@nestjs/common";
import { CustomLogger } from './logger.service';
import {configure,getLogger} from 'log4js';

const config = {
    "appenders":{
        "access":{
            "type": "file",
            "filename": "log/trace.log",
            "maxLogSize": 10485760,
            "category": "access",
            "layout": {
              "type": "pattern",
              "pattern": "[%d{yyyy/MM/dd hh.mm.ss.SSS}],[%p],[%X{ip}],[%X{method} %X{uri}],[%X{file} %X{line}:%X{column} %X{function}],%m"
            }
        },
        "console":{
            "type": "stdout",
            "layout": {
              "type": "pattern",
              "pattern": "[%d{yyyy/MM/dd hh.mm.ss.SSS}],[%p],[%X{ip}],[%X{method} %X{uri}],[%X{file} %X{line}:%X{column} %X{function}],%m"
            }
        }
    },
    "categories": {
        "default":{
            "appenders": ["access","console"],
            "level": "INFO"
        }
    }
}

const loggerFactory = {
    provide:CustomLogger,
    useFactory:()=>{
        configure(config);
        return new CustomLogger(getLogger("default"));
    }
}

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

 * `config` は `log4js` の設定情報です。次の設定をしています。
    * ファイル(logger-sample/log/trace.log)と標準出力にログを出力する。
    * リクエスト、スタック情報を追加したログフォーマットに変更
 * `loggerFactory` はカスタムインスタンスを作成する方法です。 詳しくは [公式](https://docs.nestjs.com/fundamentals/custom-providers#factory-providers-usefactory) を参照してください。

## LoggerModule を AppModule にインポートする。

`imports` に `LoggerModule` を追加するだけです。

``` typescript:logger-sample/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 {}
```

## Nest の標準 Logger を無効化して log4js を設定

`main.ts` を変更して、標準 `Logger` の無効化と `CustomLogger` を設定します。

``` typescript:logger-sample/src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { CustomLogger } from './logger/logger.service';

async function bootstrap() {
  const app = await NestFactory.create(AppModule,{logger:false});
  app.useLogger(app.get(CustomLogger));
  await app.listen(3000);
}
bootstrap();
```

`app.service.ts` で実際に `log4js` でログ出力を記述します。

``` typescript:logger-sample/src/app.service.ts
import { Injectable } from '@nestjs/common';
import { CustomLogger } from './logger/logger.service';

@Injectable()
export class AppService {

  constructor(private logger: CustomLogger){}

  getHello(): string {
    this.logger.error('Hello World!!');
    return 'Hello World!';
  }
}
```

* `constructor` で `CustomLogger` を注入します。
* `getHello()` の `this.logger.error('Hello World!!');` でログ出力しています。

実際に動かした結果

``` terminal
13:28:05 - Found 0 errors. Watching for file changes.
[2020/04/07 13.28.06.101],[INFO],[null],[null null],[null null:null null],RoutesResolver
[2020/04/07 13.28.06.106],[INFO],[null],[null null],[null null:null null],RouterExplorer
[2020/04/07 13.28.06.108],[INFO],[null],[null null],[null null:null null],NestApplication
[2020/04/07 13.28.09.370],[ERROR],[null],[null null],[null null:null null],Hello World!! 
```

`null` が多いですが、後の実装で設定しておくのでひとまずOK

これで標準 `Logger` を `log4js` に置き換えることができました。

次は [ミドルウェアでリクエスト情報をログに追加](https://qiita.com/wakusan-6126/items/f49ed378b9d05e907bad)