Nest.js + 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) (今回)


# ログ出力にスタック情報を追加

`log4js` にはファイル名や行・列番号の出力がサポートされていますが、 `CustomLogger` でラップしているため、うまく出力できません。
[log4js-node で行番号の付いたログを出力したい！](https://qiita.com/ww24/items/65fd1c2fc12c3bc65ee0) を参考に実装しました。

`CustomLogger` にスタック情報を設定するメソッドを追加します。

``` logger-sample/src/logger/logger.sevice.ts
import { LoggerService, Injectable, Scope } from '@nestjs/common';
import { Logger } from 'log4js';
import { Request } from 'express';
import { basename } from 'path';

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

    constructor(private readonly logger: Logger) {}

    setRequestInfo(req: Request){
        this.logger.addContext('ip',req.ip);
        this.logger.addContext('method',req.method);
        this.logger.addContext('uri',req.url);
    }

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

    private setStack(caller?: Function){
        const stack = this.getTrace(caller);
        this.logger.addContext('file',stack.file);
        this.logger.addContext('line',stack.line);
        this.logger.addContext('column',stack.column);
        this.logger.addContext('function',stack.func);
    }

    private prepareStackTrace(error, structuredStackTrace) {
        const trace = structuredStackTrace[0];
        return {
          // method name
          func: trace.getMethodName() || trace.getFunctionName() || "<anonymous>",
          // file name
          file: basename(trace.getFileName()),
          // line number
          line: trace.getLineNumber(),
          // column number
          column: trace.getColumnNumber()
        };
    }

    private getTrace(caller?: Function) {
        const original = Error.prepareStackTrace;
        const error = {stack:{func:"",file:"",line:0,column:0}};
        Error.captureStackTrace(error,caller || this.getTrace);
        Error.prepareStackTrace = this.prepareStackTrace;
        const stack = error.stack;
        Error.prepareStackTrace = original;
        return stack;
    }
}
```

* `getTrace()` と `prepareStackTrace()` でスタック情報を取得します。
* ログ出力前に出力関数自体を引数に `setStack()` を呼び出すことでログ情報にファイル名、行・列番号、関数名を出力できます。

実行結果

```
[2020/04/07 14.08.34.215],[INFO],[null],[null null],[logger.service.js 68:18 callFunction],RoutesResolver
[2020/04/07 14.08.34.222],[INFO],[null],[null null],[logger.service.js 68:18 callFunction],RouterExplorer
[2020/04/07 14.08.34.224],[INFO],[null],[null null],[logger.service.js 68:18 callFunction],NestApplication
[2020/04/07 14.12.07.398],[ERROR],[::1],[GET /],[app.service.js 19:21 getHello],Hello World!! 
```

標準ライブラリの出力は相変わらずですが、リクエストの情報は正しくログ出力できています。
ただし、このスタック情報は `javascript` にトランスパイル後の情報を元に出しているので参考程度になってしまします...

これで `Nest.js` で `log4js` を利用できるようになりました。後はプロジェクト毎にカスタマイズして利用して下さい。

今回のソースが欲しいという要望がありましたらコメント下さい。GitHubにアップします。