正しいデバッグ用ログの吐き方

 普段何気なく出力しているデバッグ用のログであるが、

  • 複数プロセスからどうやって同時に書き込むのか?
  • rotationはどうやってやればいいのか?

 ということを考えると、実はこれまでライブラリ任せで、自分で書いたことがないという事実に気づいた。ので、調べてみた。
 まず、複数プロセスからのログの書き込みについてだが、これは普通にアペンドモードで開くだけで良かった。

fp = fopen("app.log", "a");

 openシステムコールにはO_APPENDというオプションがあり、これをつけると毎回の write(2) の前に lseek(2) を行ったかのように、ファイルポインタをファイルの最後に移動してくれる。fopenでファイルを開くときも、"a"で開くと内部でopenシステムコールを呼ぶ際にO_APPENDをオプションとしてつけてくれるのでこれでOK。ただ、仕様書での確認はまだしてないのでちょっと怖い。
 次に、logのローテーションだが、これは伝統的なapacheでは2つの方法がある。
 一つがlogrotateを使う方法で、これはさらに内部的に2つの方式がある。一つは再起動方式で、ログファイルをリネームした後にapacheのプロセスにSIGHUPを送りつけて再起動させる。再起動するとログファイルを開き直すので新しいファイルにログを書き込むことになり、rotationができる。もう一つはcopytruncate方式で、これは現在のログを別のファイルにコピーして、それから今のログファイルをtruncateする。それぞれ得失があり、再起動方式だと再起動の瞬間はサービスが停止する。再起動の1,2秒が許容できないサービスでは難しい。copytruncate方の場合は、コピーしてからtruncateするまでの間にapacheが書き込んだログが消える。1件でもログが消える事が許容できないサービスでは難しい。
 もう一つ、rotatelogというユーティリティを使う方法があり、これは上記で上げたどのデメリットも存在しない。ただし、apacheの子プロセスとしてrotatelog自体をずっと起動しているため、その分ちょっとメモリ消費が多くなる。
 と言う風に、様々な方式にはそれぞれ得失があるようだ。お手軽に済ませるなら自分で書いたプログラムには特に手を入れずにlogrotateを使うのもいいだろうし、気合を入れて作るならば、rotatelogと同じ機能をプログラム本体に組み込んでもいいだろう。