はじめに
Railsアプリのログは、初期設定だとひとつのファイルに出力し続けるようになっています。また、古いログが削除されずに延々と蓄積されていくことになるので、そのままにしておくとログが肥大化してディスク領域を圧迫してしまいます。
$ ls -lh /var/www/autovice/current/log/
合計 2.2G
-rw-rw-r-- 1 user user 1.2K 10月 10 2019 development.log
-rw-r--r-- 1 nginx root 154M 3月 4 14:59 nginx.access.log
-rw-r--r-- 1 nginx root 13M 3月 4 12:45 nginx.error.log
-rw-rw-r-- 1 user user 2.1G 3月 4 14:59 production.log
-rw-rw-r-- 1 user user 395K 2月 19 16:16 unicorn.log
これは当ポートフォリオサイトのログディレクトリの状況です。リリース以来、ずっと初期設定のまま運用してきたため、ひとつのファイルが2.1GBにまで膨れ上がってしまっています。このようなファイルは開くだけでも時間がかかるので良い運用とは言えません。
本記事では、Railsアプリのログを一定周期でローテーションさせ、古いログは削除する方法について説明します。
ログローテーションの種類
Railsアプリでログローテーションを行う方法は2種類あります。
Railsの設定で行う方法
Railsの設定でログローテーションを行うには、config/environments/production.rb
に以下を追記します。
# ファイルサイズでローテート
config.logger = Logger.new("log/production.log", 5, 10 * 1024 * 1024)
# 日数でローテート
config.logger = Logger.new("log/production.log", 'daily')
この方法でもいいのですが、後述するLinuxのlogrotate
を使ったほうが自由度が高いのであまりおすすめしません。
Linuxコマンドを使う方法
Linuxコマンドのlogrotate
を使ってログローテーションを行うことができます。logrotate
では古いログファイルの圧縮ができたり保持する世代を指定できたり、Railsの設定でログローテーションを行う方法と比べてより詳細な設定ができるのが特徴です。Linux以外で運用しているなどの理由でlogrotate
が使えないという場合でない限り、こちらの方法を使うことをおすすめします。
logrotateでログローテーションを行う
logrotateの設定
logrotate
でログローテーションを行う方法は簡単です。/etc/logrotate.d/
配下に設定ファイルを作成するだけです。ファイル名は何でも構いません。
$ sudo touch /etc/logrotate.d/rails
作成したファイルにlogrotate
の設定を記述します。
$ sudo vi /etc/logrotate.d/rails
/var/www/autovice/current/log/*log {
daily
missingok
rotate 30
dateext
dateformat _%Y%m%d
compress
copytruncate
ifempty
su user user
}
今回設定したオプションは以下のとおりです。
オプション | 意味 |
---|---|
daily | 毎日ローテーションを行う。 |
missingok | ログファイルがなくてもエラーメッセージを出力しない。 |
rotate 30 | 30世代まで保持する。daily を指定しているので、30日(1ヶ月)保持するということ。 |
dateext | 番号の代わりに年月日(YYYYMMDD)を付加する。 |
dateformat _%Y%m%d | 付加する年月日の形式を指定する。 |
compress | ローテーションしたログファイルを圧縮する。 |
copytruncate | 元のログファイルをコピーし、中身を空にする。 |
ifempty | ログファイルが空でもローテーションを行う。 |
su user user | ローテーションを行うユーザーとグループを指定する。 |
この他にもたくさんのオプションがあります。詳しい説明については以下を参照してください。
logrotateのテスト実行
logrotate
の設定をしたものの想定通りに実行されるのかすぐに確認したい場合があると思います。logrotate
は手動で実行することもできます。また、引数に-d
を指定することで、実際のログファイルには影響を与えずに実行することができます。
$ sudo /usr/sbin/logrotate -d /etc/logrotate.d/rails
引数に-d
を指定しない場合は、ログファイルを一時ディレクトリなどにコピーして、コピーしたログに対して実行することをおすすめします。運用中のログに対して実行してしまうと取り返しのつかないことになる可能性があるので。
logrotate.stateについて
logrotate
は、ローテーションの状態を保持しておくlogrotate.state
に基づいて実行されます。
$ sudo cat /var/lib/logrotate/logrotate.status
logrotate state -- version 2
"/var/www/autovice/current/log/production.log" 2021-2-3-0:0:0
"/var/www/autovice/current/log/nginx.access.log" 2021-2-3-0:0:0
"/var/www/autovice/current/log/nginx.error.log" 2021-2-3-0:0:0
(以下略)
logrotate
は初回実行時、logrotate.state
に対象ログファイルの書き込みだけ行い、ローテーションは行いません。そして2回目の実行時、logrotate.state
に対象ログファイルがあることを確認して初めてローテーションを行います。
logrotate.state
に対象ログファイルがない状態でlogrotate
のテスト実行を行うと、以下のようなメッセージが表示されます。
$ sudo /usr/sbin/logrotate -d /etc/logrotate.d/rails
(中略)
log does not need rotating (log has been already rotated)
このようなメッセージが表示されてローテーションされない場合、logrotate.state
に対象ログファイルと前回実行時(前日など)を書き込んでから実行してみてください。
肥大化したログファイルの扱い
ログローテーションを設定するのが遅くて既に以下のような肥大化したログファイルができてしまっているという場合はどうすればいいでしょうか。
$ ls -lh /var/www/autovice/current/log/
合計 2.2G
-rw-rw-r-- 1 user user 1.2K 10月 10 2019 development.log
-rw-r--r-- 1 nginx root 154M 3月 4 14:59 nginx.access.log
-rw-r--r-- 1 nginx root 13M 3月 4 12:45 nginx.error.log
-rw-rw-r-- 1 user user 2.1G 3月 4 14:59 production.log
-rw-rw-r-- 1 user user 395K 2月 19 16:16 unicorn.log
このような肥大化したログファイルは、一度ローテーションが実行されてから削除してしまっても構いません。手動で削除してもいいですし、logrotate
で自動削除されるのを待つのでもいいです(30世代保持する設定の場合、31世代が作られる頃に1世代は削除される)。
まとめ
ログファイルのローテーション設定は優先度が低いため後回しにしてしまいがちです。しかし、放っておくとRailsアプリにも影響を与えかねません。できることなら初期リリースの時点で設定しておきたいですね。
本記事を参考にしてログローテーションを設定していただければと思います。