例外が発生しても異常終了しない log4js のロガーを作る
Node.js でロギングする際は log4js をよく使っているのだが、以前、ロギング処理自体に問題があってエラーを起こしてしまった。
そのコードは以下のようなコードだった。
let result = null;
try {
// API をコールしてデータを取得するテイ
result = await fetchAPI('http://example.com/users/1');
}
catch(error) {
// エラーオブジェクトを文字列化してログ出力しようとしたら…
myLog4JsLogger.error('API コールでエラー発生', JSON.stringify(error));
}
変数 myLog4JsLogger
でエラーログを出力する際、error
オブジェクトを JSON.stringify()
で文字列化して出力しようとしていた。しかしこの error
オブジェクト、循環参照が含まれており、JSON.stringify()
する際に Converting circular structure to JSON
というエラーが発生してしまっていた。catch
句の中で例外が発生するので、このエラーは外に漏れ、異常終了に繋がってしまった。
このような話は Murga の以下の記事にも書いた。
こんなことをやらかしたので、「ロガーでロギングする時に発生したエラーは握り潰せたらいいんじゃね?」と軽い気持ちで作ったのがこのラッパー。
log4js.getLogger()
で取得したロガーのインスタンスをラップする。
例えば以下のコードは、JSON.parse()
に失敗すると例外が発生してしまう。
myLogger.info( JSON.parse(jsonStr) );
このように、例外が発生しそうで怖い要素は、以下のように関数でラップして渡してやる。
myLogger.info( () => JSON.parse(jsonStr) );
// 以下と同じ
myLogger.info( () => { return JSON.parse(jsonStr); } );
myLogger.info( function() { return JSON.parse(jsonStr); } );
このように関数でラップしてやれば、まだ関数は実行されていないので、この時点での例外は発生しない。
そして、ラッパークラス内は try / catch
で全体を囲んだ状態で、引数に関数が渡っていればそれを実行しているので、例外が発生しても呼び出し元に影響しない、というワケ。
async / await
を使って、ほとんど同じ作りで、非同期処理に対応した関数も用意してある。「Promise を返す関数」をロガーのラッパーの引数に渡してやれば良い。
以上。ホントはそもそもロギングするモノをよく把握して実装して例外なんか起こすなよって話なんだけども。w