この記事は株式会社ネクスト(Lifull) Advent Calendar 2016の3日目の記事です。遅くなってしまいましたが、なんとか当日に間に合わせましたいました。
本記事はLinuxのlogrotateコマンドについての説明をするものであり、その他のログに関する話は一切出てきません。ご了承ください。
logrotateとはman logrotateにも書いてあることですがlogrotateは複数のログファイルを圧縮、削除、メールで送信するための機能です。これにより「ファイルサイズがN以上になったら」とか「日次・週次・月次などで分割」などが容易に可能になります。
logrotate自体はデーモンではなくcrondによって実現されています。利用する前にcrondが動いていることを確認しましょう。
crondの動作確認$ /etc/rc.d/init.d/crond status # 動いていなかったらstartしましょうcrond (pid 1939) を実行中...続いてlogrotateがインストールされているかの確認です。たぶん通常はインストールされていて、ある程度よしなに動くように設定されていると思います。
logrotateのインストール確認$ yum list installed | grep logrotatelogrotate.x86_643.7.8-26.el6_7@updatesインストールされてなかったらyumで入れればよいかと思われます。
ではlogrotateの動作を確認していきましょう
練習のための前準備練習用のログファイルを用意します。適当にapacheのログファイルを/tmp/logにコピーして置いておきました。すでにある程度logrotate済みのファイルでしたので*-yyyymmmdd形式になってます。
$ ls /tmp/log/httpd/-rwxrw-rw- 1 zom dev 2172608 11月 30 23:30 2016 access_log-rwxrw-rw- 1 zom dev 4563118 11月 30 23:30 2016 access_log-20161106-rwxrw-rw- 1 zom dev 4841917 11月 30 23:30 2016 access_log-20161113-rwxrw-rw- 1 zom dev 4760022 11月 30 23:30 2016 access_log-20161120-rwxrw-rw- 1 zom dev 4285726 11月 30 23:30 2016 access_log-20161127とりあえずこのaccess_logを対象とする設定ファイルを書いてみます。
/tmp/log/logrotate.conf/tmp/log/httpd/access_log {}設定ファイル内では対象とするログのファイルパス、もしくはディレクトリパスを指定します。ファイルパスではワイルドカードの*が利用可能で、パターンにマッチするファイルが全て対象となります。またディレクトリパスを指定した場合は該当のディレクトリ以下のファイルすべてが対象となりますので、いずれの利用も注意しておこなってください。
早速この設定でlogrotateを実行してみましょう。このとき-dオプションを付けることで実際にlogrotateは行われずに、どのように動作するのかをデバッグすることができます。-dオプションを省略すると設定次第ではログが破壊的変更を受けますのでいろいろ試すときはバックアップをとっておくと良いと思います。
$ /usr/sbin/logrotate -d /tmp/log/logrotate.confreading config file /tmp/log/logrotate.confreading config info for /tmp/log/httpd/access_logHandling 1 logsrotating pattern: /tmp/log/httpd/access_log 1048576 bytes (no old logs will be kept)empty log files are rotated, old logs are removedconsidering log /tmp/log/httpd/access_log log needs rotatingrotating log /tmp/log/httpd/access_log, log->rotateCount is 0dateext suffix '-20161203'glob pattern '-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]'renaming /tmp/log/httpd/access_log.1 to /tmp/log/httpd/access_log.2 (rotatecount 1, logstart 1, i 1),renaming /tmp/log/httpd/access_log.0 to /tmp/log/httpd/access_log.1 (rotatecount 1, logstart 1, i 0),renaming /tmp/log/httpd/access_log to /tmp/log/httpd/access_log.1disposeName will be /tmp/log/httpd/access_log.1removing old log /tmp/log/httpd/access_log.1error: error opening /tmp/log/httpd/access_log.1: そのようなファイルやディレクトリはありません記述した設定ファイルをもとに実行しようとしているのが分かります。接尾語は「-20161203」だと書いてありますが、dateextの設定(後述)をしていません。そのため古いログは.1という接尾語が採用され、元のファイル名は/tmp/log/httpd/access_log.1になったと言われています。さらにrotateオプションも使っていないため古いログ(access_log.1)が消されようとしています。しかし、そこでそんなファイルないよ、とerrorになったところでコマンドが終了しました。
では対象のログファイルがなかった場合はどのような結果になるのでしょうか?
$ cat /tmp/log/logrotate.conf/tmp/log/httpd/access_lo {# ファイル名のgを消しました}$ /usr/sbin/logrotate -d /tmp/log/logrotate.confreading config file /tmp/log/logrotate.confreading config info for /tmp/log/httpd/access_loHandling 1 logsrotating pattern: /tmp/log/httpd/access_lo 1048576 bytes (no old logs will be kept)empty log files are rotated, old logs are removedconsidering log /tmp/log/httpd/access_loerror: stat of /tmp/log/httpd/access_lo failed: そのようなファイルやディレクトリはありません対象のログファイルがないというエラーでコマンドが終了しました。
ということで何も指定せずに実行した場合、このようなことが分かります。
logrotate対象のファイルがある場合は.nという接尾語をつけてリネームする古いログは削除される(ファイルがなくてエラーを吐いて終了する)対象ファイルがない場合はファイルを開けずにエラーを吐いて終了するファイルが存在しない場合にエラーを吐かないようにするファイルがない場合にエラーを吐いて終了するのはいいこととは思えません。ですのでmissingokオプションをつけて実行してみましょう。
$ cat /tmp/log/logrotate.conf/tmp/log/httpd/access_lo {missingok}$ /usr/sbin/logrotate -d /tmp/log/logrotate.confreading config file /tmp/log/logrotate.confreading config info for /tmp/log/httpd/access_loHandling 1 logsrotating pattern: /tmp/log/httpd/access_lo 1048576 bytes (no old logs will be kept)empty log files are rotated, old logs are removedconsidering log /tmp/log/httpd/access_lo log /tmp/log/httpd/access_lo does not exist -- skipping$ echo $?0エラーではなくskippingに結果が変わりました。echo $?で終了ステータスも0(正常終了)に変わったことが確認できます。
世代管理をする次に古いログがすぐに消されるのは困りますので1世代だけ残してもらうようにしましょう。
$ cat /tmp/log/logrotate.conf/tmp/log/httpd/access_log {# gをもとに戻しましたmissingokrotate 1}$ /usr/sbin/logrotate -d /tmp/log/logrotate.conf### 長いので中略 ###renaming /tmp/log/httpd/access_log to /tmp/log/httpd/access_log.1removing old log /tmp/log/httpd/access_log.2error: error opening /tmp/log/httpd/access_log.2: そのようなファイルやディレクトリはありません先ほどから接尾語が.2に変わりました。世代管理をするようになり、さらに古いファイルから削除しようとしているようです。しかし削除するときにエラーになってしまうのもなんなので試しに該当のファイルを置いて実行してみます。
$ touch /tmp/log/httpd/access_log.2$ /usr/sbin/logrotate -d /tmp/log/logrotate.conf### 長いので中略 ###renaming /tmp/log/httpd/access_log.1 to /tmp/log/httpd/access_log.2 (rotatecount 1, logstart 1, i 1),renaming /tmp/log/httpd/access_log.0 to /tmp/log/httpd/access_log.1 (rotatecount 1, logstart 1, i 0),renaming /tmp/log/httpd/access_log to /tmp/log/httpd/access_log.1removing old log /tmp/log/httpd/access_log.2エラーが出なくなりました。では次に行きましょう。
接尾語を設定する先述のdateextの設定をしてみます。
$ cat logrotate.conf/tmp/log/httpd/access_log {missingokrotate 1dateext}$ /usr/sbin/logrotate -d /tmp/log/logrotate.conf### 長いので中略 ###removing /tmp/log/httpd/access_log-20161106removing old log /tmp/log/httpd/access_log-20161106removing /tmp/log/httpd/access_log-20161113removing old log /tmp/log/httpd/access_log-20161113removing /tmp/log/httpd/access_log-20161120removing old log /tmp/log/httpd/access_log-20161120renaming /tmp/log/httpd/access_log to /tmp/log/httpd/access_log-20161203removing old log /tmp/log/httpd/access_log-20161127rename時のファイル名が-YYYYMMDDに変わりました。でもファイル名にハイフンを使うのは好きじゃないんだよね、_で区切りたいんだよね、というケースもあるかと思います。その場合はdateformatが利用可能です。
$ cat logrotate.conf/tmp/log/httpd/access_log {missingokrotate 1dateextdateformat _%Y%m%d}$ /usr/sbin/logrotate -d /tmp/log/logrotate.conf### 長いので中略 ###dateext suffix '_20161203'glob pattern '_[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]'glob finding old rotated logs failedrenaming /tmp/log/httpd/access_log to /tmp/log/httpd/access_log_20161203dateformatには%Y%m%dか%sが利用可能です。%sですとUnixtimeになりますがシステムクロックが2001年9月9日以降に設定されている必要があります。(ってmanで出てきました)また、並び順にはDDMMYYYY形式などを指定することもできますファイル名などのソートが使えなくなる可能性があることに気をつけて使ってください(ってmanで出てきました)
古いファイルを圧縮する(&stateファイルについて)次に古いファイルはそんなに見ることはないでしょうから圧縮してファイルサイズを節約してみましょう。圧縮したい場合はcompressです。本当に圧縮できるのかを確認したいので-dオプションを消してやってみます。
$ cat logrotate.conf/tmp/log/httpd/access_log {missingokrotate 1dateextdateformat _%Y%m%dcompress}$ /usr/sbin/logrotate /tmp/log/logrotate.conferror: error creating unique temp file: 許可がありませんコケました。何か一時ファイルを作ろうとするも許可がないとのことです。(これは推測混じりですが)logrotateするときにステータスをファイルで管理しています。デフォルトでは/var/lib/logrotate.stateです。こちらがroot権限であるために一般ユーザで実行すると権限で怒られてしまうのではないかと。
$ ls /var/lib/logrotate.status-rw-r--r-- 1 root root 499 12月 3 05:05 2016 /var/lib/logrotate.statusstateファイルも引数で指定することが可能ですので指定して実行してみましょう。
$ /usr/sbin/logrotate -s /tmp/log/logrotate.state /tmp/log/logrotate.conf$ ls /tmp/log/logrotate.state-rw-r--r-- 1 zom dev 67 12月 3 22:09 2016 /tmp/log/logrotate.state$ cat /tmp/log/logrotate.statelogrotate state -- version 2"/tmp/log/httpd/access_log" 2016-12-3$ ls /tmp/log/httpd-rwxrw-rw- 1 zom dev 4563118 11月 30 23:30 2016 access_log-20161106*-rwxrw-rw- 1 zom dev 4841917 11月 30 23:30 2016 access_log-20161113*-rwxrw-rw- 1 zom dev 4760022 11月 30 23:30 2016 access_log-20161120*-rwxrw-rw- 1 zom dev 4285726 11月 30 23:30 2016 access_log-20161127*-rw-r--r-- 1 zom dev 0 12月 3 21:33 2016 access_log.2 # 先程touchしたファイル-rwxrw-rw- 1 zom dev 118394 12月 3 21:54 2016 access_log_20161203.gzstateファイルができました。最後のrotateの日付が2016-12-3になってますね。また思惑通り、ファイルの圧縮がされ1世代前のファイルがgzip圧縮されています。
新しいログファイルの設定をするおや?上の実行後にaccess_logが存在していません。これでは次のrotateに引っかかりません。(通常はapache側で再作成してくれると思いますが)新しいログファイルを作成してほしい場合はcreateの設定を使います。createではファイルパーミッションや所有者・所有グループを指定できます。とっておいたバックアップを元に再度実行し直してみます。
$ cat /tmp/log/logrotate.conf/tmp/log/httpd/access_log {missingokrotate 1dateextdateformat _%Y%m%dcompresscreate 766 zom dev}$ ls /tmp/log/httpd-rwxrw-rw- 1 zom dev0 12月 3 23:02 2016 access_log-rwxrw-rw- 1 zom dev 4563118 12月 3 23:01 2016 access_log-20161106-rwxrw-rw- 1 zom dev 4841917 12月 3 23:01 2016 access_log-20161113-rwxrw-rw- 1 zom dev 4760022 12月 3 23:01 2016 access_log-20161120-rwxrw-rw- 1 zom dev 4285726 12月 3 23:01 2016 access_log-20161127-rwxrw-rw- 1 zom dev 118394 12月 3 23:02 2016 access_log_20161203.gzgzファイルができた上にaccess_logファイルを新規に作成し直しています。
しかしログファイルはものによってはファイルハンドラを掴みっぱなしにしていてrenameができないことも考えられます。その場合はcreateではなくcopytruncateの設定の方を使います。こちらはファイルをrenameではなくcopyして、元のファイルの「内容」を消すというものです。コピー→削除の間に若干のタイムラグがあるので頻繁に書き込みが行われている場合にはデータの欠損が発生しうることがあります。(ってmanで出てきました)またcreateとcopytruncateの両方を記述しているとcopytruncateのほうが優先されます。
$ cat /tmp/log/logrotate.conf/tmp/log/httpd/access_log {missingokrotate 1dateextdateformat _%Y%m%dcompresscreate 766 zom devcopytruncate}$ /usr/sbin/logrotate -s /tmp/log/logrotate.state /tmp/log/logrotate.conf -d### 中略 ###copying /tmp/log/httpd/access_log to /tmp/log/httpd/access_log_20161203truncating /tmp/log/httpd/access_log先程のrenamingからcopyingに挙動が変わりました。createかcopytruncateかは利用場面に応じて使い分けてください。
ローテーションの間隔を指定する次はローテーションの間隔です。daily, weekly, monthlyのいずれかが指定可能です。 version3.8.5からhourlyが追加されていました。また、yearlyも存在していました。なぜこのような間隔になるのかというとcron.dailyに登録されているからです。
$ ls /etc/cron.daily/logrotate-rwx------ 1 root root 180 7月 10 04:36 2003 /etc/cron.daily/logrotate$ sudo cat /etc/cron.daily/logrotate#!/bin/sh/usr/sbin/logrotate /etc/logrotate.confEXITVALUE=$?if [ $EXITVALUE != 0 ]; then/usr/bin/logger -t logrotate "ALERT exited abnormally with [$EXITVALUE]"fiexit 0またweeklyの場合はいつrotateされるのかというと日曜日です。cronでは曜日の指定を数字でおこないますが0が日曜日で6が土曜日です。logrotateでは、この数字が前回より小さくなったときに週が変わったと認識するため日曜日にrotateされます。またmonthlyの場合は毎月1日となります。ログの増加量と相談して選ぶとよいかと思います。
しかし、日付間隔だけでrotateするというのも適切ではありません。全然ファイルサイズが増えないのであればmonthlyより広い間隔でもいいかもしれませんし、すごい勢いで増えるのであればもっと早いタイミングでrotateしたいでしょう。
その場合はsize, もしくはmaxsize, minsizeなどを使いましょう。
sizeなどにはバイト数を指定できます。さらに単位としてk(キロバイト)、M(メガバイト)、G(ギガバイト)も利用可能です。なぜかkだけ小文字です。
sizeの場合は、時間指定と排他的な設定で、指定した数値を超えた場合にrotateされるようになり、dailyなどの時間指定は無視されます。一方[max|min]sizeは時間指定と排他ではなくともに生きる設定です。
ここから記述内容に誤りがありました。正確な情報はコメント欄を参照ください
ですので2MBちょっとのaccess_logに対してrotateするまでもない、という場合に以下のようにmaxsizeを3Mと設定してみます。
/tmp/log/httpd/access_log {missingokrotate 1dateextdateformat _%Y%m%dcompresscopytruncatedailymaxsize 3M}$ /usr/sbin/logrotate -s /tmp/log/logrotate.state /tmp/log/logrotate.conf -dempty log files are rotated, log files >= 0 are rotated earlier, old logs are removedconsidering log /tmp/log/httpd/access_log log does not need rotatingするとrotateの必要はない、といった感じでrotateされていません。maxsizeを2Mにしてみるとrotateされます。
ここまで記述内容に誤りがありました。
まとめ1つずつ設定を解説していきました。本当はもっといろいろあります。圧縮をgzip以外にするとかメールで送信するとかdelaycompressとか…。時間の関係上、基本的なところしか書いていませんがご査収ください。(暇があれば追記していきたいですね)
次は4日目のkaiharaさんのService Worker Offline Cache Techniquesです。よろしくどうぞ!