2015年1月31日土曜日

logrotateでnginxのログを1時間ごとにローテートをする

このエントリーをはてなブックマークに追加 はてなブックマーク - logrotateでnginxのログを1時間ごとにローテートをする

logrotateについて全然知らなかったので勉強した時のメモ。

やりたいこととしては

  • 特定のファイルだけ時間でローテートしたい
  • ローテート対象のファイルはgzipで圧縮したい
  • ローテート対象ファイルのファイル名はYYYYmmddHHMMとしたい

という感じ。

今回はnignxのログを時間ごとにローテートするようにしてみました。

使ったのが初めてだったので基本的な概要は以下で勉強しました

logrotateによるログのローテーション

さっき気づいたこと

logrotateの3.8.5からhourly対応したらしい。

logrotate-Changelog

3.8.5未満の場合の話

以下がとても参考になりました。

apacheのlogrotateを1時間毎に吐き出そうとした記録

logrotateを1時間ごとに実行させる

1時間に1回logrotateが動くように/etc/cron.dailyの中のlogrotatedというファイルを/etc/cron.hourlyにコピーします。

$sudo cp /etc/cron.daily/logrotate /etc/cron.hourly/

これによってlogrotateコマンド自体が1時間に1回実行されますが、ローテートの周期としてweeklyなどの設定をしていれば何回実行されても1週間経過しないとローテートされないはずなので時間ごとにローテートしたくなくファイル群は特に影響は受けないかと思います。(1週間経過しているかの判定はデフォルトでは/var/lib/logrotate.statusの日時を参照します)

/etc/logrotate.d/nginxを編集する

/etc/logrotate.d/配下にあるアプリケーションごとの設定でsizenodateexを指定します。また、compressをコメントアウトします。(理由は後述)

$sudo vi /etc/logrotate.d/nginx

/var/log/nginx/*log {
    create 0644 nginx nginx
    daily
    rotate 10
    missingok
    notifempty
    #compress
    size 1
    nodateext
    sharedscripts
    postrotate
        /etc/init.d/nginx reopen_logs
    endscript
}

size指定することによって指定されたsizeを越えたらローテートします。(manでgrow bigger thanなので以上ではなく超えるだと思います、多分)なお、minsizeというオプションもありこちらを指定するとローテーション周期(daily,weeklyなど)とのAND条件となるので注意。logrotate の size と minsize の違い

また、nodateextを追記します。これによってローテートするときに日付をファイル名に付けるのではなく、hoge.1というようなファイル名が連番になるようにします。/etc/logrotate.confでdateextを指定していなければこちらで明示的に指定する必要はありません。

この状態で正常に動作するか確認します。

$sudo /usr/sbin/logrotate /etc/logrotate.conf
Reopening nginx logs:                                      [  OK  ]

$wget http://localhost/

$sudo /usr/sbin/logrotate /etc/logrotate.conf
Reopening nginx logs:                                      [  OK  ]

$sudo ls -la /var/log/nginx/
total 16
drwx------ 2 nginx nginx 4096 Jan 31 13:20 .
drwxr-xr-x 6 root  root  4096 Jan 25 03:13 ..
-rw-r--r-- 1 nginx nginx    0 Jan 31 13:17 access.log
-rw-r--r-- 1 nginx nginx  101 Jan 31 13:17 access.log.1
-rw-r--r-- 1 nginx nginx  101 Jan 31 13:16 access.log.2
-rw-r--r-- 1 nginx nginx    0 Jan 13 03:14 error.log

ファイル形式を変更して圧縮する

dateformatというオプションはあるのですが、Only %Y %m %dとなっているので時間が恐らく使えません。そのため、 ログローテーション後にスクリプトを実行できるpostrotate〜endscriptを使ってリネームを行います。また、その中で圧縮も行います。(compressオプションはpostrotate-endscriptの後にローテートした元々のファイルを圧縮しようとするようでリネームを独自でやると正しく動作しないようだった)

/var/log/nginx/*log {
    create 0644 nginx nginx
    daily
    rotate 10
    missingok
    notifempty
    #compress
    size 1
    nodateext
    sharedscripts
    postrotate
        EXT=`date +%Y%m%d%H%M`
        for f in $1
        do
          ls $f.1 >/dev/null 2>&1
          if [ $? -eq 0 ] ; then
            mv $f.1 $f.$EXT
            gzip $f.$EXT
          fi
        done
        /etc/init.d/nginx reopen_logs
    endscript
}

postrotateの中では/bin/shが実行されるようでシェルスクリプトが記述できます。 詳細な仕様が書いてある所を発見できなかったのですが、動作確認したところ以下のようでした。

  • 指定されているログファイル群の一つでもローテートの条件に合致する場合にスクリプトは実行される(access.logが0バイトを超えた場合でerror.logのファイルサイズが0バイトでもスクリプトが実行される)

  • 第一引数に対象のファイル名が配列で渡される。ただし、ローテーション対象外のものも含まれる(access.logが0バイトを超えた場合でerror.logのファイルサイズが0バイトでもaccess.logとerror.logを含む配列が渡される)

あんまり綺麗に書けている自信がありませんが、動作的には大丈夫そうです。

1 件のコメント:

  1. 以下のような方法では如何でしょうか?
    ---
    /var/log/nginx/*log {
    create 0644 nginx nginx
    rotate 10
    dateext
    dateformat -%Y%m%d%d%d%d
    compress
    sharedscripts
    lastaction
    EXT1=`/bin/date +%Y%m%d%d%d%d`
    EXT2=`/bin/date +%Y%m%d%H%M%S`
    for f in ${1}; do
    if [ -f ${f}-${EXT1}.gz ]; then
    /bin/mv -f ${f}-${EXT1}.gz ${f}-${EXT2}.gz
    fi
    done
    /etc/init.d/nginx reopen_logs
    endscript
    }
    ---
    1.postrotate だと圧縮処理の前ですが、lastaction だと圧縮処理の後になるので、自前圧縮処理を書かずに済む
    2.dateext は指定できるルールが限定されているので、正規表現的に似たもので指定しておいて、lastaction側でリネーム
    似たルールにしておくことで、logrotate 側の「旧ログファイル名マッチング」が上手く動作するようになるので、"rotate 10" 等の世代管理指定が正しく作用する

    返信削除