log4js-nodeでログレベルごとにログを出し分ける

Node.jsでログ出力したい場合、 `console.log()`が最初の候補になるかと思います。通常の用途であればこれで十分なことも多いでしょう。

とはいえ、`console.log()` では力不足の状況もあるかと思います。出力先を変えたり複数のログを取ったりするような場合は次のような高機能なログライブラリが候補に挙がるはずです。

- [winston](https://www.npmjs.com/package/winston)
- [log4js](https://www.npmjs.com/package/log4js)
- [bunyan](https://www.npmjs.com/package/bunyan)

私はlog4jsを採用して小さいアプリを作りました。その際ログレベルごとにログを出し分ける方法がイマイチ分かりづらかったので、これを紹介します。

## log4jsの簡単な紹介

まずはlog4jsの概要を紹介します。log4jsの最小のサンプルは次のようなものです。

```javascript
const log4js = require('log4js');
const logger = log4js.getLogger();
logger.level = 'debug';
logger.debug("Some debug messages");
```

上のコードではデフォルトのロガー（コンソール出力）を取得し、ログレベルをdebugにしてからdebugレベルのログを出力しています。上のコードを実行すると次の内容がコンソールに出力されます。

```
[2019-01-14T19:30:39.578] [DEBUG] default - Some debug messages
```

### ログレベル

ログレベルは次の7段階あり、上から順に厳しいレベルのエラーを意味します。同名のメソッドを使うことで、そのログレベルのログを書き込めます。

* mark （これだけ例外で、エラーではないが常に表示しておきたい情報を書く用途だと思います）
* fatal
* error
* warn
* info
* debug
* trace

ロガーは自分のログレベル以上のログを出力します。上の例であればロガーは debug レベルかそれより厳しいログを出力するため、　`logger.trace()` で書きこんだログはどこにも出力されません。

この仕組みにより、詳細のログは低いログレベルで書き込んでおいて普段の利用の邪魔にならないようにして、デバッグ時のみロガーのログレベルを下げて詳細まで確認する、というような使い方ができます。

### Appender

log4jsではログ出力先それぞれを appender と呼んでいます。 appender は複数指定したりそれぞれの出力フォーマットを変えたりすることができます。

次の例ではコンソール出力とファイル出力の2つのappenderを指定し、1回のログ書き込みで2つのログを出力しています。

```javascript
const log4js = require('log4js');
log4js.configure({
  appenders: {
    console: { type: 'console' },
    logfile: { type: 'file', filename: 'debug.log' },
  },
  categories: { default: { appenders: ['console', 'logfile'], level: 'debug' } }
});
const logger = log4js.getLogger();
logger.debug('Some debug messages');
```

### Category

log4js ではログの種類を category と呼んでいます。 category ごとにログレベルと appender を指定することができます。カテゴリ名としてはアプリケーション名（複数アプリのログを外部ログサービスに集約する場合）やモジュール名、もしくはログの対象（例：通信関連、ファイルI/O関連）を指定するのが良いでしょう。

カテゴリに対応するロガーをインスタンス化するには `log4js.getLogger()` を使います。第一引数にカテゴリ名を指定することで複数のロガーを使い分けることができます。

`log4js.getLogger()` の引数が指定されない場合のデフォルト値は`default`ですので、ロガーが１種類しかない場合は上のように `default` カテゴリを使うのもアリだと思います。

## ログレベルごとにログを出し分ける

ようやく本題です。

今回、`debug.log` ファイルにdebugレベル以上のログを出力し、`result.log` ファイルにinfoレベル以上のログを記録したいと考えました。つまり、infoやwarnなどのログは両方のファイルに書かれることになります。

これは次のようにすれば実現できます。

```javascript
const log4js = require('log4js');
log4js.configure({
  appenders: {
    debug: { type: 'file', filename: 'debug.log' },
    result_raw: { type: 'file', filename: 'result.log' },
    result: { type: 'logLevelFilter', appender: 'result_raw', level: 'info' },
  },
  categories: { default: { appenders: ['result', 'debug'], level: 'debug' } }
});
const logger = log4js.getLogger();
logger.debug('Output to debug.log only');
logger.info('Output to both file');
```

ここでは `logLevelFilter` という appender を利用しています。これはデバッグレベルによってログをフィルタするような疑似的な appender です。

上の例でいえば `result` appender には debug レベル以上のログが流れてくるのですが info 未満のログはフィルタされてしまい、その子供である `result_raw` appender にはinfo以上のログだけが届きます。

このようにすることで `logger.debug()` の内容は `debug.log` のみに書かれ、 `logger.info()` の内容は両方のファイルに出力されるようになります。

## 参考資料

公式のマニュアルを読めば他にも細かい設定ができます。

- [log4js-node マニュアル](https://log4js-node.github.io/log4js-node/)
- [log4js\-node/docs at master · log4js\-node/log4js\-node](https://github.com/log4js-node/log4js-node/tree/master/docs)

特にログファイルを作りたい場合は [File Appender](https://log4js-node.github.io/log4js-node/file.html) , [Date Rolling File Appender](https://log4js-node.github.io/log4js-node/dateFile.html) , [Layouts](https://log4js-node.github.io/log4js-node/layouts.html) の3つを読むといいでしょう。