BayServerの実装時にハマった問題があったので備忘録。
以前から、TypeScript版BayServerに、「エラーが発生してプログラムが終了した場合に、原因となるエラーがログに吐き出されない」という問題があって、どうやったら直せるかずっと悩んでおりました。
これ、直さないと、ユーザーさんはエラーの報告できないですからね笑
で、直したので、BayServer 3.0 for TypeScrpt からは大丈夫ですよという話。
ちなみに、BayServerは、横浜ベイキット(https://baykit.yokohama/) で開発している、爆速WebServerです。RubyやらPHPやらいろんな言語で実装されています。
以下、コードは端折っています。
BayServerでは、設計ファイルでredirectFileを指定すると、ログは標準出力ではなくファイルに出力されます。内部的に何をやっているか説明します。
標準出力や標準エラーをリダイレクトする方法として、とりあえずcolsole.logとconsole.errorを書き換えます。オリジナルは別変数に保存しておきます。
this.originalConsoleLog = console.log
this.originalConsoleError = console.error
this.logConsole = fs.createWriteStream(redirectFile)
this.logConsole.on('open', () => {
console.log = (...args: any[]) => {
let message = args.join(' ');
this.logConsole.write(message + '\n')
};
console.error = console.log
})
redirectFileをオープンして、console.log関数を、オープンしたファイルに出力するように書き換えます。
console.errorはconsole.logと同じ動きになるように書き換えます。
後で戻せるように、元々のconsole.log, console.errorの実装は別の変数に保存しておきます。
さて、これでconsole.logやconsole.errorを呼んだ時のデータはファイルにリダイレクトされるわけですが、実はメソッドを呼んだ時点ではファイルに書き込まれず、バッファに溜まるだけなんですね。で、いつ書かれるかというと、Node.jsのイベントループで順番が回ってきた時に書かれます。
この挙動は通常あまり問題にならないですが、プログラム終了時に問題を起こします。
どんな問題かというと、プログラム終了直前に書こうとしたデータはバッファに残ったまま、ファイルに吐き出されずプログラムが終了してしまうんですね。
では、プログラム終了時に、次のようにendを呼べば良いと思うかも知れないが、残念ながらそれもダメ。
this.logConsole.end()
chatGPT先生に聞いてみたが、何度もうまくいかず、何億回とやり取りして、結局、プログラム終了時に以下の2点を行うとうまくいきました。
- console.log, console.error を元の実装に戻す
- endメソッドを呼び、finishedイベントの発生をプロミス化して待つ
何故これでうまくいくのかは全然分かんないですが笑
console.log = this.originalConsoleLog
console.error = this.originalConsoleError
this.logConsole.end()
const finishedAsync = promisify(finished)
finishedAsync(this.logConsole).then( () => {
});
というわけで、BayServerをよろしくお願いします!