今動かしているアプリケーションで起っている問題です。アプリケーションのログが肥大化するのを防ぐ為に、logrotate でログのローテーションを設定しています。
しかし、ローテーションしたあとはログが書き込まれないという問題が起こってしましました。
logrodate の設定
- /etc/logrotate.d/myapp
/home/admin/apps/myapp/log/*.log { create rotate 4 missingok compress notifempty monthly postrotate if [ -f /var/run/lighttpd.pid ]; then \ /etc/init.d/lighttpd reload > /dev/null; \ fi; \ endscript }
再現方法
- アプリケーションを起動
$ sudo /etc/init.d/lighttpd start
- 適当にブラウザで操作してproduction.log にログを書き込ませてみる
$ tail -f ~/apps/myapp/log/production.log .......
色々書き込まれているのが分かる
- ローテションを走らせてみる
$ sudo /usr/sbin/logrorate -f /etc/logrotate.d/myapp
- ログの中身を一度確認
$ cat ~/apps/myapp/log/production.log
ローテーションがかかってログファイルの中身が空になっている。
- 適当にブラウザで操作してproduction.log にログを書き込ませてみる
$ tail -f ~/apps/myapp/log/production.log
ブラウザで操作してもログに書き込まれない・・・・∃ヨヨョョ。+゚(ノД`)゚+。ョョヨヨ∃
- しかたがないので再起動
$ ps ax | grep fcgi | grep -v grep | awk '{print $1}' | xargs sudo kill -KILL; sudo /etc/init.d/lighttpd restart
こうすると正常に書き込まれるようになる。
原因
この問題の原因は、reload(つまり HUP)のシグナルがfcgi のプロセスに送られても、fcgi プロセスは新しく生成されたproduction.log を開いてくれないからです。
$ ps ax | grep fcgi 1785 ? S 0:00 /usr/local/sbin/lighttpd -f /etc/lighttpd/lighttpd.conf 31786 ? S 0:00 /usr/bin/ruby /home/admin/apps/myapp/public/dispatch.fcgi .... $ sudo /usr/sbin/logrorate -f /etc/logrotate.d/myapp $ sudo ls -al /proc/31786/fd Password: total 0 dr-x------ 2 root root 0 Oct 24 00:09 . dr-xr-xr-x 5 root root 0 Oct 23 23:55 .. lrwx------ 1 root root 64 Oct 24 00:09 0 -> socket:[248452] l-wx------ 1 root root 64 Oct 24 00:09 1 -> /dev/null lrwx------ 1 root root 64 Oct 24 00:09 2 -> /dev/pts/1 l-wx------ 1 root root 64 Oct 24 00:09 3 -> /home/admin/apps/myapp/log/production.log.1 (deleted) l-wx------ 1 root root 64 Oct 24 00:09 4 -> /home/admin/apps/myapp/log/fastcgi.crash.log.1 (deleted)
ものの見事にローテーションによって削除されたファイルのファイルディスクリプタを開いたままになっています。
考えられる解決策
- 1. 毎回ローテーションした跡で手動で再起動する
- 2. logrorate の設定ファイルを書き換え、ローテーション後に以下を実行するようにする
# /home/admin/apps/myapp/script/process/reaper -a restart
どっちもいまいちです・・・。手動は面倒だし忘れる可能性があり、逆時自動でアプリケーションが再起動して思わぬ問題が発生しないとも限りません。でも、現状は手動で対応しています・・・。
以下のような感じのシェルスクリプトとか回せばいいのだろうか。
#!/bin/sh gzip -c /home/admin/myapp/log/production.log > production.log.1.gz cat /dev/null > /home/admin/myapp/log/production.log
今度Rails 系のイベントに参加した時に、ログの管理方法に関して質問してみようかな。
追記
解決しました!copytruncate 使えばおkでした。
- /etc/logrotate.d/myapp
/home/admin/apps/myapp/log/*.log { copytruncate create rotate 4 missingok compress notifempty monthly postrotate if [ -f /var/run/lighttpd.pid ]; then \ /etc/init.d/lighttpd reload > /dev/null; \ fi; \ endscript }
- テスト
$ sudo /usr/sbin/logrotate -d -f /etc/logrotate.d/ad_counter rotating log /home/admin/apps/myapp/log/production.log, log->rotateCount is 4 renaming /home/admin/apps/myapp/log/production.log.4.gz to /home/admin/apps/myapp/log/production.log.5.gz (rotatecount 4, logstart 1, i 4), renaming /home/admin/apps/myapp/log/production.log.3.gz to /home/admin/apps/myapp/log/production.log.4.gz (rotatecount 4, logstart 1, i 3), renaming /home/admin/apps/myapp/log/production.log.2.gz to /home/admin/apps/myapp/log/production.log.3.gz (rotatecount 4, logstart 1, i 2), renaming /home/admin/apps/myapp/log/production.log.1.gz to /home/admin/apps/myapp/log/production.log.2.gz (rotatecount 4, logstart 1, i 1), renaming /home/admin/apps/myapp/log/production.log.0.gz to /home/admin/apps/myapp/log/production.log.1.gz (rotatecount 4, logstart 1, i 0), copying /home/admin/apps/myapp/log/production.log to /home/admin/apps/myapp/log/production.log.1 truncating /home/admin/apps/myapp/log/production.log running postrotate script running script with arg /home/admin/apps/myapp/log/*.log : " if [ -f /var/run/lighttpd.pid ]; then \ /etc/init.d/lighttpd reload > /dev/null; \ fi; \ " compressing log with: /bin/gzip removing old log /home/admin/apps/myapp/log/production.log.5.gz
create でやった場合との差はこんな感じで、圧縮とか他の部分の順序は変化しない模様。
$ diff create.txt truncate.txt 7,8c7,8 < renaming /home/admin/apps/myapp/log/production.log to /home/admin/apps/myapp/log/production.log.1 < creating new log mode = 0664 uid = 500 gid = 500 --- > copying /home/admin/apps/myapp/log/production.log to /home/admin/apps/myapp/log/production.log.1 > truncating /home/admin/apps/myapp/log/production.log
copytruncate してcompress を書くと、copy されたログを圧縮してくれるんですね。かしこい( ´艸`)
まぁ、copy なので一瞬ログファイルの2倍のサイズ分ディスクを使ってしまうのが難点でしょうか。
そこも考慮して、現在設定ファイルはこんな感じにしてあります。共通の設定は外に出して、ファイル毎の設定は差分のみを書いています。
- /etc/logrodate.d/myapp
rotate 4 missingok compress notifempty monthly postrotate if [ -f /var/run/lighttpd.pid ]; then \ /etc/init.d/lighttpd reload > /dev/null; \ fi; \ endscript /home/admin/apps/myapp/log/lighttpd*.log /home/admin/apps/myapp/log/fastcgi.crash.log { create } /home/admin/apps/myapp/log/production.log { copytruncate }
追々記
上の設定には問題がありました。この場合、「rotate 4」〜「endscript」の部分はグローバルな設定になってしまい、全てのローテーションのルールを書き換えてしまいます。
よって、以下のようにしました。
/home/admin/apps/myapp/log/lighttpd*.log /home/admin/apps/myapp/log/fastcgi.crash.log { create rotate 4 missingok compress notifempty monthly postrotate if [ -f /var/run/lighttpd.pid ]; then \ /etc/init.d/lighttpd reload > /dev/null; \ fi; \ endscript } /home/admin/apps/myapp/log/production.log { copytruncate rotate 4 missingok compress notifempty monthly postrotate if [ -f /var/run/lighttpd.pid ]; then \ /etc/init.d/lighttpd reload > /dev/null; \ fi; \ endscript }