【Rails】Railsアプリのログローテーション設定を行う

はじめに

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アプリにも影響を与えかねません。できることなら初期リリースの時点で設定しておきたいですね。

本記事を参考にしてログローテーションを設定していただければと思います。

関連記事

【Rails】Paranoiaを使用した論理削除(ソフトデリート)
# はじめに Paranoiaは、Railsアプリケーションで論理削除(ソフトデリート)を実現するためのGemです。 論理削除は、データベースのレコードを物理的に削除するのではなく、削除フラグを設定することで「削除済み」とみなす方法です。こ [...]
2024年7月20日 21:33
【Rails】activerecord-multi-tenantを使用したマルチテナントアプリケーションの作成
# はじめに マルチテナントアプリケーションでは、複数の顧客(テナント)が同じアプリケーションを利用するため、データの分離が必要です。 activerecord-multi-tenantは、このようなマルチテナント環境をサポートするための便 [...]
2024年7月18日 16:50
【Rails】RubyとRailsにおけるattr_reader, attr_writer, attr_accessorの概念と使用方法
# はじめに RubyとRailsの開発において、`attr_reader`,`attr_writer`,`attr_accessor`は非常に便利なメソッドです。これらは、クラス内でインスタンス変数に対するゲッターおよびセッターメソッドを簡単に [...]
2024年7月17日 18:11
【Rails】RubyとRailsにおけるyieldの概念と使用方法
# はじめに RubyとRailsにおける`yield`は、メソッドやテンプレートの中で動的にコードブロックを実行する能力を提供し、これによってコードの再利用性と拡張性が大幅に向上します。本記事では、RubyとRailsにおける`yield`の概 [...]
2024年7月17日 13:15
【Rails】AASMを使用してオブジェクトの状態遷移を効率的に管理
# はじめに Railsアプリケーションにおいて、オブジェクトの状態管理は重要な課題の一つです。AASM (Acts As State Machine) gemは、複雑な状態遷移を効率的に管理します。本記事では、AASMの基本的な使い方を解説して [...]
2024年7月16日 18:00
【Rails】RSpec + Swagger + rswagでアプリケーションのAPIをテストおよびドキュメント化する方法
# はじめに Railsアプリケーションの開発において、APIのテストとドキュメント化は重要な要素です。 RSpecはテストフレームワークとして広く利用されており、SwaggerはAPIの設計とドキュメント化を支援します。これらを統合するr [...]
2024年7月16日 14:27
【Rails】mailcatcherを使用して開発環境でメール送信をテストする方法
# はじめに mailcatcherは、開発環境でのメール送信をキャプチャするためのツールです。ローカルで送信されたメールをブラウザ上で簡単に確認できるようにします。mailcatcherをRailsアプリケーションで使用する方法について説明しま [...]
2024年7月15日 16:37
【Rails】impressionistを使用してページビューやクリック数を追跡する方法
# はじめに impressionist Gemを使用してRailsアプリケーションでページビューやクリック数を追跡する方法について説明します。 # 実装方法 ## impressionist Gemのインストール まず、impre [...]
2024年7月15日 14:18