はじめに

ログを日次や指定した時間でローテートすることができる、logrotateを使ってみる。
アプリケーションのログや、自作のログなどはlogrotate をすると良いので、この機会に覚えておきたい。

環境

1
2
3
Windows 10 Professional
WSL2 (Ubuntu22.04 LTS)
logrotate 3.19.0

準備

logrotate が入っていることを確認する。

1
which logrotate

ない場合は下記で入れる。

1
sudo apt-get install logrotate

logrotateの実践

設定ファイルの確認

まずは、logrotate の大元の設定ファイルを確認する。

1
cat /etc/logrotate.conf
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# see "man logrotate" for details

# global options do not affect preceding include directives

# rotate log files weekly
weekly

# use the adm group by default, since this is the owning group
# of /var/log/syslog.
su root adm

# keep 4 weeks worth of backlogs
rotate 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

# packages drop log rotation information into this directory
include /etc/logrotate.d

# system-specific logs may also be configured here.

上記のようになっている、 /etc/logrotate.dApache での設定やら、 nginx での設定など、アプリケーションごとの設定ファイルを入れるのが良い。

ちなみに、 /etc/logrotate.d は下記のようになっている。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
/etc/logrotate.d$ ll
total 52
drwxr-xr-x  2 root root 4096 May 13 14:05 ./
drwxr-xr-x 98 root root 4096 Jun 10 11:46 ../
-rw-r--r--  1 root root  120 Sep 12  2021 alternatives
-rw-r--r--  1 root root  126 Nov 12  2019 apport
-rw-r--r--  1 root root  173 Apr  8  2022 apt
-rw-r--r--  1 root root   91 Mar 18  2022 bootlog
-rw-r--r--  1 root root  130 Oct 14  2019 btmp
-rw-r--r--  1 root root  112 Sep 12  2021 dpkg
-rw-r--r--  1 root root  374 Dec 24  2021 rsyslog
-rw-r--r--  1 root root  270 Mar  8  2022 ubuntu-advantage-tools
-rw-r--r--  1 root root  209 Sep 19  2021 ufw
-rw-r--r--  1 root root  235 Feb 19  2021 unattended-upgrades
-rw-r--r--  1 root root  145 Oct 14  2019 wtmp

設定ファイルを作成する

/etc/logrotate.d に 自分が作成するアプリケーションのログを設定する。

1
$ sudo touch /etc/logrotate.d/mylog

上記で作成を行う。

1
$ sudo vim /etc/logrotate.d/mylog
1
2
3
4
5
6
7
8
/var/log/test/test.log { # 対象
    ifempty
    dateformat .%Y%m%d
    missingok
    compress
    daily
    rotate 10
}

※設定の種類 (ChatGPTに出力してもらった。)

設定オプション説明
compressローテーション後にログファイルを圧縮するかどうかを指定します。
copytruncateログファイルを切り詰めずにコピーしてから新しいファイルを作成します。
dailyローテーションの間隔を日単位で指定します。
weeklyローテーションの間隔を週単位で指定します。
monthlyローテーションの間隔を月単位で指定します。
rotate <count>ログファイルをローテーションする回数を指定します。
size <size>ログファイルのサイズが指定したサイズに達した場合にローテーションを実行します。
maxsize <size>ログファイルの最大サイズを指定します。ログファイルがこのサイズを超えた場合にローテーションを実行します。
notifemptyログファイルが空の場合にはローテーションを実行しません。
createローテーション後に新しいログファイルを作成するかどうかを指定します。既存のログファイルは空にされます。
dateextローテーションされたログファイルに日付を追加します。
dateformatローテーションされたログファイルの日付のフォーマットを指定します。
postrotateローテーション後に実行するコマンドを指定します。
prerotateローテーション前に実行するコマンドを指定します。
sharedscriptspostrotateまたはprerotateスクリプトを1回だけ実行します(すべてのローテーション対象のログファイルに対して)。
missingokローテーション対象のログファイルが存在しない場合でもエラーを発生させずに処理を続行します。

動作

  1. ログファイルが書き込まれる場所を作成する。
1
2
sudo mkdir -p /var/log/test
sudo chmod 777 /var/log/test
  1. cronで自動的にログファイルを書き込むように設定する。
1
crontab -e
  1. 1分置きに自動で書き込むようにする。 crontab
1
*/1 * * * * echo "[`date "+%Y%m%d-%H%M%S"`] Hello, World" >> /var/log/test/test.log
  1. 書き込まれているかの確認
1
less /var/log/test/test.log

ちゃんと書き込まれているっぽい。

1
[20230610-122449] Hello, World
  1. 設定ファイルでのローテートをさせる。
1
sudo logrotate -df /etc/logrotate.d/mylog

-d・・・デバッグモード -f ・・・強制的にローテートさせる。

下記のようにエラーが出た。 親ディレクトリの権限による問題みたい。

1
error: skipping "/var/log/test/test.log" because parent directory has insecure permissions (It's world writable or writable by group which is not "root") Set "su" directive in config file to tell logrotate which user/group should be used for rotation.

権限を適切なものに修正する。

1
2
sudo chmod 700 /var/log/test
sudo chmod 666 /var/log/test/test.log

また、これに伴い、crontab -eを行い、下記のように修正する。

1
*/1 * * * * sudo sh -c 'echo "[`date "+%Y%m%d-%H%M%S"`] Hello, World" >> /var/log/test/test.log'

この状態で再度実行をする。

1
sudo logrotate -df /etc/logrotate.d/mylog

ログがローテートされている!

1
2
$ sudo ls test/
test.log.1.gz

何回もローテートされることを確認。
10世代までだが、2世代まで確認する。

1
2
3
4
5
6
$ sudo sh -c 'echo "[`date "+%Y%m%d-%H%M%S"`] Hello, World" >> /var/log/test/test.log'
$ sudo logrotate -f /etc/logrotate.d/mylog
$ sudo sh -c 'echo "[`date "+%Y%m%d-%H%M%S"`] Hello, World" >> /var/log/test/test.log'

$ sudo ls test/
test.log  test.log.1.gz  test.log.2.gz

疑問に思った点

logrotateはどのように行われるのか?

日次でログファイルを切りかえる際に、いずれかなのかと考えた。

  • 既存のログファイルをリネーム→新しくログファイルを作成
  • 既存のファイルをコピー+リネームし、古いログとして作成。既存のログファイルを cp /dev/null などで空にする。

どうやら調べると、1番目がデフォルトの動作で、2番目が copytruncate を指定したときの動作とのこと。

ChatGPT に聞いてみると、下記の回答を得られた。

copytruncateは、logrotateの設定オプションの1つです。このオプションが指定された場合、logrotateはログファイルを切り詰めずに、新しいファイルを作成します。

通常、logrotateは既存のログファイルをリネーム(または削除)して新しいログファイルを作成します。しかし、copytruncateオプションが指定されると、logrotateは既存のログファイルを切り詰めずにそのままの状態でコピーし、同じ名前の新しいログファイルを作成します。

これにより、アプリケーションがログファイルに直接書き込んでいる場合でも、ログの追加は中断されずに継続されます。なお、この方法では、元のログファイルの内容は失われますが、ファイルディスクリプタは変更されないため、アプリケーションはログの書き込みを続けることができます。

copytruncateは、特定のアプリケーションがログファイルを開いたままにする必要がある場合や、ログファイルを分割してもファイルディスクリプタを維持する必要がある場合などに便利です。ただし、注意点として、切り詰められたログファイルは一部のログエントリが欠落している可能性があるため、データの完全性が重要な場合には適切なオプションを選択する必要があります。

そのため、 1番目の方法の場合は、 ローテートする際に postscript を書いてアプリケーションをリロードや再起動をして、ログファイルを新しいものに再度読み込ませる必要がある。
2番目の方法( copytruncate )では、上記のような設定はいらないが、ログエントリが欠落する可能性があるとのことだ。

postscriptはcopytruncateを指定しない場合、必ず必要か?

1点目の疑問で思った、ローテートする際に postscript を書いてアプリケーションをリロードや再起動をして、ログファイルを新しいものに再度読み込ませる必要がある。 の部分だが、これも ChatGPT に聞いてみる。

copytruncateオプションを指定しない場合、logrotateは既存のログファイルをリネーム(または削除)して新しいログファイルを作成します。そのため、アプリケーションは新しいログファイルに対してファイルディスクリプタを持っていないため、再起動やリロードが必要になる場合があります。

多くのアプリケーションでは、ログファイルへの書き込みはファイルディスクリプタを介して行われます。logrotateによって既存のログファイルがリネームされると、アプリケーションは古いファイルディスクリプタを使用し続けることになります。新しいログファイルが作成された後、アプリケーションは新しいファイルディスクリプタを取得する必要があります。

したがって、アプリケーションがログファイルのリロードや再起動をサポートしていない場合、ログファイルの切り替え後にログの書き込みが正常に継続されない可能性があります。この場合、アプリケーションを再起動するか、ログの書き込みを再開するための特定の手順を実行する必要があります。

ただし、アプリケーションによっては、logrotateの信号を受け取ってログファイルを再オープンする機能を備えている場合もあります。この場合、特別な処理や再起動は不要であり、アプリケーションは新しいログファイルに対して正常にログの書き込みを続けることができます。

アプリケーションのドキュメントやログ関連の設定を確認し、logrotateの挙動に関するアプリケーション固有の要件や推奨事項を確認することをおすすめします。

ということなので、アプリケーションによっては、 postscript で再起動、リロードがなくてもローテートしてくれるらしい。

参考

おわりに

logrotate を勉強した。
巨大な1個のログファイルとならないように、アプリケーションのログは適切にローテートする必要がある。
そのため、logrotate を設定していないログがある場合については、使用することを検討したほうが良い。