アプリケーションにとってログファイルはイベントやエラーなどを記録しておく重要なファイルです。しかし、同じファイルにログを書き込み続けていくとログファイルはどんどん肥大化していくことになります。
ログが肥大化すると次のような不都合があります。
- ディスク領域を圧迫する
- ログを確認するとき、ファイルを読み込むのも特定の場所を探すのも大変
そのためログファイルが肥大化しないように適切なログのローテーションが必要です。
Linuxではシステムにある多くのログファイルをローテートするために設計されたlogrotateプログラム(コマンド)が用意されています。
この記事ではLinuxでlogrotateがどのように設定され使われているのか、その仕組みを紐解いていきます。
なおlogrotateについては「logrotateのテスト方法」という記事も書きましたので参照いただけると幸いです。
logrotateによるログローテートの仕組み
ログのローテートとは一般に元のログファイルを別の名前で保存し、新しいログファイルに切り替えることを言います。
実際にlogrotateプログラムを使ってログローテートを体験して、ローテーションの仕組みをみていきましょう。
この章は少し長いですが、ここに書いてあることがわかればlogrotateについて7割は理解できたも同然ですので、しばらくお付き合いください。
独自のログローテーションを設定する
まずテスト用にローテートされるログファイルを作成します。
# echo "original log file" > /var/log/mytest.log # ls -l /var/log/mytest.log -rw-r--r--. 1 root root 18 Jan 6 14:33 /var/log/mytest.log
ここでは「original log file」という内容の/var/log/mytest.logというファイルを用意しました。
次にこのファイルをローテートするための設定ファイルを作成します。
# cp -p /etc/logrotate.d/syslog /etc/logrotate.d/mytest # vi /etc/logrotate.d/mytest # cat /etc/logrotate.d/mytest /var/log/mytest.log { create rotate 2 }
/etc/logrotate.d ディレクトリの中のファイルをコピーして、mytestというファイルを作成します。ここではsyslogファイルをコピーしています。
mytestを作成したら、catで表示している内容にviで編集してください。
この設定の「/var/log/mytest.log」部分はローテート対象のファイルを指定します。その後の中括弧の本体に、そのファイルをどのようにローテートするかの命令(ディレクティブという)を記述します。
デフォルトではローテーションが実行されるとオリジナルのログファイルは別の名前にリネームされます。すると元のファイル(ここではmytest.log)がなくなってしまうので、createディレクティブで元ファイルと同じ名前のファイルを新しく作成するよう指示しています。
「rotate 2」というディレクティブは、リネームされた古いファイルは2つまで残しておくという指示です。それ以上の古いファイルは削除されます。
独自のログローテーションを実行する
さてこれで準備は整いました。では実際に/var/log/mytest.logをローテートさせてみましょう。
それには次のコマンドを実行します。
logrotate -f /etc/logrotate.d/mytest
「-f」は強制的にローテートさせるためのオプションです。logrotateコマンドには先程作成した/etc/logrotate.d/mytestを引数に渡します。
ローテーションがどのように行われたか確認してみましょう。
# ls -l /var/log/mytest.log* -rw-r--r--. 1 root root 0 Jan 6 14:30 /var/log/mytest.log -rw-r--r--. 1 root root 5 Jan 6 14:29 /var/log/mytest.log.1
さっきはなかった「mytest.log.1」というファイルが確認できます。中身を見てみましょう。
# cat /var/log/mytest.log.1 original log file
このことから「mytest.log.1」は元々「mytest.log」だったことがわかります。デフォルトでは、この例のように元のファイル名の後に番号を追加した名前で保存されます。
もう一度同じように実行してみましょう。
# logrotate -f /etc/logrotate.d/mytest # ls -l /var/log/mytest.log* -rw-r--r--. 1 root root 0 Jan 6 14:39 /var/log/mytest.log -rw-r--r--. 1 root root 0 Jan 6 14:34 /var/log/mytest.log.1 -rw-r--r--. 1 root root 18 Jan 6 14:33 /var/log/mytest.log.2 # cat /var/log/mytest.log.2 original log file
一番最初のオリジナルファイルはmytest.log.2になりました。
さらにもう一度実行すると「original log file」と記載されたファイルは削除されて無くなります。
# logrotate -f /etc/logrotate.d/mytest # ls -l /var/log/mytest.log* -rw-r--r--. 1 root root 0 Jan 6 14:43 /var/log/mytest.log -rw-r--r--. 1 root root 0 Jan 6 14:39 /var/log/mytest.log.1 -rw-r--r--. 1 root root 0 Jan 6 14:34 /var/log/mytest.log.2
これは「rotate 2」という指示で、古いファイルは2世代までしか保存しないからです。それより古いファイルは削除されます。
このようにローテートするとファイルは適切なサイズで切り替えられ、古いファイルは自動的に削除されるのでディスク領域を圧迫することもありません。
最後にここでのローテートの動作をまとめます。ローテートされると次のようなことが行われています。
- 「mytest.log.2」を「mytest.log.3」にリネーム
- 「mytest.log.1」を「mytest.log.2」にリネーム
- 「mytest.log」を「mytest.log.1」にリネーム
- 「mytest.log」を新たに作成
- 「mytest.log.3」を削除
このようにファイルをローテートするとファイルはリネームされて切り替えられ、古いファイルは削除されるのが基本的な動作です。
ここでmytest.log.3 というファイルが一旦作成されるのは安全性を考慮してでしょう。必要な世代のファイルが作成されてから削除することにより、エラーなどでファイルが失われることがありません。
システムのログローテーションはcronで実行されている
先程はlogrotateコマンドを手動で実行してログローテートを行いましたが、Linuxなどのシステムでは毎日ログローテーションが行われています。
これはcronでlogrotateコマンドが毎日実行されるようにスケジュールされているからです。
/etc/cron.dailyディレクトリを覗いてみましょう。するとlogrotateというスクリプトファイルがあるはずです。
# ls -l /etc/cron.daily/ total 4 -rwxr-xr-x. 1 root root 189 Jan 4 2018 logrotate
/etc/cron.dailyディレクトにあるファイルはcronデーモンによって毎日実行されますので、 logrotateスクリプトが毎日実行されています。
logrotateスクリプト中身を見てみましょう。
# cat /etc/cron.daily/logrotate #!/bin/sh /usr/sbin/logrotate /etc/logrotate.conf ...
このようにlogrotateスクリプト実行されると「/etc/logrotate.conf」を引数としてlogrotateコマンド(ここではフルパスで指定されいる)が実行されることがわかります。
システムのlogrotate.confファイルの中身
独自のログローテートは「/etc/logrotate.d/mytest」というファイルを指定してlogrotateコマンドを実行しましたが、システムのログローテーションでは「/etc/logrotate.conf」が使われていることがわかりました。
このlogrotate.confの中身を見てみましょう。ここでは設定の意味も簡単にコメントしています。
$ cat /etc/logrotate.conf # see "man logrotate" for details # rotate log files weekly weekly ← 毎週ローテートする # keep 4 weeks worth of backlogs rotate 4 ← ローテートした古いファイルは4世代まで保持する # create new (empty) log files after rotating old ones create ← ローテートした後に新しいファイルを作成する # use date as a suffix of the rotated file dateext ← 古いファイルの名前に日付を追加する # uncomment this if you want your log files compressed #compress ← ローテートしたファイルを圧縮する # RPM packages drop log rotation information into this directory include /etc/logrotate.d ← logrotate.dの中のファイルを読み込めという指定 # system-specific logs may be also be configured here.
最後の「include /etc/logrotate.d」の記述は、/etc/logrotate.dディレクトリの中のファイルを読み込むという指示です。
個々のログファイルの設定をlogrotate.confに記述することもできますが、「/etc/logrotate.d」の下にサービスごとに個別のファイルを用意するのが一般的です。
このようにしておくと、ローテートの設定ファイルを「/etc/logrotate.d」に放り込むだけでログローテートの設定が完了できるので便利です。
したがって先程作成した/etc/logrotate.d/mytestもlogrotate.confから読み込まれ毎日実行されることになります。
そのほかlogrotare.confに記述されている裸のディレクティブはグローバルな設定です。これよりもmytestのようにファイル名の後の中括弧本体で指定したディレクティブの方が優先されます。
/etc/logrotate.dの中のサービスごとの設定ファイル
ここまでの説明でログローテートについて基本的なことはすべて説明しました。指定できるディレクティブ以外にあと知っておくことは、どのような設定ファイルの記載方法があるかです。
サンプルとして/etc/logrotate.dの中のサービスごとの設定ファイルをいくつか簡単にみてみましょう。
一つ目はシステムログ(syslog)の設定です。
# cat /etc/logrotate.d/syslog /var/log/cron /var/log/maillog /var/log/messages /var/log/secure /var/log/spooler { missingok sharedscripts postrotate /bin/kill -HUP `cat /var/run/syslogd.pid 2> /dev/null` 2> /dev/null || true endscript }
最初に複数のファイルパスがリストされています。このように複数のファイルに対して一度に同じディレクティブを設定することもできます。
ここでrotateやcreate(と衝突する)のディレクティブが指定されていないのでlogrotare.confのでrotateやcreateディレクティブにしたがってローテートされます。
次のように複数のファイルを指定するためにワイルドカードを使うこともできます。
# cat chrony /var/log/chrony/*.log { missingok nocreate ...
次はdnfの設定をみてみましょう。
# cat /etc/logrotate.d/dnf /var/log/dnf.librepo.log { missingok notifempty rotate 4 weekly create 0600 root root } /var/log/hawkey.log { missingok notifempty rotate 4 weekly create 0600 root root }
ここでは2つのファイルに対して別々にディレクティブが設定されています。
よく使うディレクティブ
最後によく使うディレクティブを簡単に説明します。
ティレクティブ | 説明 |
---|---|
compress | ローテーションした古いログファイルを圧縮する。デフォルトではgzipで圧縮する。 |
copytruncate |
元のログファイルをリネームするのではなく、コピーを作成した後に元のログファイルを空にする。 この場合、元のログファイルは存在したままなのでcreateの指定は無効になる。 |
create mode owner group, create owner group |
ローテーション直後に新しいログファイルを同じ名前で作成する。 モード、所有者、グループ所有者を指定できる。これらが省略された場合は元のログファイルと同じ値になる。 |
daily | 毎日ローテートする。 |
dateext | 単純な番号を付ける代わりに、古いログファイルにYYYYMMDDのような日付を付ける。 |
delaycompress |
1つ前のログファイルを圧縮するのを、次のローテーションまで遅らせる。 これはcompressが指定された場合のみ機能する。 |
ifempty | ログファイルが空でもローテートする。これはデフォルトの動作です。 |
include file_or_directory | fileを指定した場合はインラインで指定されたのと同様に読み取り、ディレクトリが指定された場合は、そのディレクトリの中のファイルを読み込む。 |
mail address | ログがローテーションによって存在期間を超えた場合、指定したメールアドレスに送信される。 |
maxage count |
count日よりも古いログファイルを削除する。 ログがローテートされるときのみこれがチェックされる。 |
maxsize size | 指定のサイズよりログのサイズが大きくなった場合、追加で指定されている時間間隔(daily, weekly, monthly, yearly)に関わらずローテートする。 |
minsize size | 指定のサイズよりログのサイズが大きくなった場合にローテートする。ただし、追加で指定されている時間間隔(daily, weekly, monthly, yearly)が経過するよりも前にはローテートしない。 |
missingok | ログファイルがない場合でもエラーを報告しない。 |
monthly | 毎月ローテートする(通常は月の初日)。 |
nocompress | ローテートされた古いログファイルを圧縮しない。 |
nodateext | 古いログファイルに日付を付加しない。 |
nomissingok | ログファイルが存在しないとエラーを報告する。これはデフォルトの動作です。 |
notifempty | ログファイルが空の場合、ローテーションしない。 |
olddir directory | ログをローテートすると古いファイルは指定したディレクトリに移動される。 |
postrotate/endscript | postrotateとendscriptの間のスクリプトが、ログがローテートされた後に実行される。 |
prerotate/endscript | ログがローテートされる場合に限り、prerotateとendscriptの間のスクリプトがローテート前に実行される。 |
rotate count |
ログファイルはcount回数だけローテートする。 つまりcount世代分のログファイルは保持され、それより古いファイルは削除される。countに0にすると古いファイルは1つも保持されない。 |
size size |
指定されたサイズよりもログファイルのサイズが大きい場合のみローテーションします。 サイズの後ろに”k”をつけるとキロバイト、”M”をつけるとメガバイト、”G”をつけるとギガバイトと解釈される。 |
sharedscripts |
prerotateとpostrotateスクリプトはローテートされるログファイルごとに実行され、そのログファイルへの絶対パスがスクリプトの最初の引数として渡される。 つまり、前述のsyslogやワイルドカード指定のように複数のログファイルに対してスクリプトが毎回実行されるということです。このディレクティブが指定された場合、このようなときでもスクリプトは一度だけしか実行されません。ただし、ファイルが一つもローテートされなければスクリプトは実行されない。 |
weekly | 毎週ローテートする。 |
まとめ
logrotateによるログローテートの仕組みは意外とシンプルだったでしょう。
ここで説明した以外のディレクティブについて知りたいときはmanコマンドで確認できます。