logrotateでcopytruncateしてもサイズが大きいまま
logrotateには色んなオプションがある。(logrotateが何をするツールかはググって下さい)。そのうちの1つに copytruncate というものがある。動作としては以下の通り。
- 対象ログがa.logだとすると、それを a.log.0 あるいは a.log-yyyymmddにコピー
- a.logをtruncate (内部的には cp /dev/null /path/to/a.log みたいな処理)
ログ・ファイルのサイズが大きいままになる問題
ログファイル名は a.log とする。logrotate の設定で copytruncate になっているとすると、ローテートの際に、前述の通り a.log が a.log.0 にコピーされる。a.log が 100KBだとすると、a.log.0 は100KBで、その後 a.log は truncate されて 0バイトになるはず。
だが、以下の様な現象が起こる場合がある。
- a.log が a.log-0 にコピーされる (a.log-0 のサイズは100KB)。
- a.log が truncate される。
- a.log のサイズが 100KBに戻る。
a.log の最初の100KBはNULL byte(\0)で埋められ、その後からログが書き込まれる。
想像がつくと思うけど、ログファイルに書き込むプロセスが保持しているファイルポインターが100KBの位置のままの場合、a.log が truncate されてサイズが0になっても、その後のログ書き込みで100KBの位置に書き込まれ、結果として最初の100KBがNULLで埋められる。
原因、解決方法
ログファイルを開く際に O_APPEND オプションが付いていない場合にこうした現象が起こる。シェル(sh や bash)の場合、
$ command > a.log
の場合、O_APPEND 無しでファイルが開かれるので、上のような現象が発生する。それに対して、以下の場合には問題が発生しない。
$ command >> a.log
ということで、シェルスクリプトからログファイルにログを書き込む際に > でリダイレクトをしている場合、logrotate の copytruncate で上のような問題が発生するので、 >> を使うようにすると良い。
なんで > と >> で挙動が違うかは、歴史的背景らしい。