ログローテートソフトウエア logrotate についてまとめ




最近のLinuxではデフォルトで導入されている非常にメジャーなソフトウエア。
サーバーのログファイルだけでなく、WEBアプリケーションのログファイルのローテーションにも使える。

## ファイル構成

|パス|役割|
|---|---|
|/etc/logrotate.conf|ログローテーション全体の設定ファイル|
|/etc/logrotate.d/*|各サービスごとの設定ファイル|
|/var/lib/logrotate.status or <br>/var/lib/logrotate/status or <br>/var/lib/logrotate/logrotate/status or <br>/var/lib/logrotate/logrotate.status|最後にローテーションした日時を管理するファイル。OSやバージョンによってパスが違う。|

`/etc/logrotate.conf` はデフォルトの設定が書かれている。
`/etc/logrotate.d` 以下もincludeされているので、
ここにサービスごとの設定ファイルを作成する。

※設定は後から書いたもので上書きされていく仕様。`/etc/logrotate.conf` でデフォルトの設定が書かれた後に `/etc/logrotate.d` をincludeしている。

## 設定項目一覧

### ローテーションの条件に関する設定

| ディレクティブ | オプション | オプション例 | 説明 |
| :-- | :-- | :-- | :-- |
| `hourly` | - | - | ローテーションを毎時実行する。ただし、cronでhourlyに設定しておかないと、cronで設定されているdailyになる。 |
| `daily` | - | - | ローテーションを毎日実行する。 |
| `weekly` | - | - | ローテーションを毎週実行する。 |
| `monthly` | - | - | ローテーションを毎月実行する。 |
| `yearly` | - | - | ローテーションを毎年実行する。 |
| `size` | [ファイルサイズ] | `1M` | ログファイルが指定したファイルサイズ以上になったらローテーションする。サイズの後ろに `k` をつけるとキロバイト、`M` をつけるとメガバイト、`G` をつけるとギガバイトと解釈される。時間間隔（`daily`, `weekly`, `monthly`, `yearly`）の設定は無視され、`logrotate` コマンドが実行されたときに指定サイズ以上になっているログファイルだけローテーションする。 |
| `maxsize` | [ファイルサイズ] | `1M` | ログファイルが指定したファイルサイズ以上になったらローテーションする。時間間隔（`daily`, `weekly`, `monthly`, `yearly`）も設定されている場合はファイルサイズ以上になったときも時間が経過したときも両方ローテーションされる。ただし、`logrotate` を時間単位で起動させていなければ、デフォルトでは cron の daily に設定されているので日毎が最短になる。`logrotate` のバージョン3.10から実装された機能。 |
| `minsize` | [ファイルサイズ] | `1M` | ログファイルが指定したファイルサイズ以上になったらローテーションする。時間間隔（`daily`, `weekly`, `monthly`, `yearly`）も設定されている場合は両方の条件が揃うまでローテーションされない。 |
| `ifempty` | - | - | ログファイルが空でもローテーションする。 |
| `notifempty` | - | - | ログファイルが空ならローテーションしない。この設定をした場合 `ifempty` の設定を上書きする。 |

### ログのファイル生成に関する設定

| ディレクティブ | オプション | オプション例 | 説明 |
| :-- | :-- | :-- | :-- |
| `create` | [パーミッション ユーザー名 グループ名]  | `0644 nginx nginx` | ローテーション後に空のログファイルを新規作成。ログファイルを出力するプログラムがファイルを生成出来ない場合に使用する。ファイルのパーミッション、ユーザー名、グループ名を指定可能。指定しない属性については元のファイルの属性が引き継がれる。`postrotate` スクリプトが実行される前に実行される。 |
| `nocreate` | - | - | 新たな空のログファイルを作成しない。この設定をした場合 `create` の設定を上書きする。 |
| `copytruncate` | - | - | ログファイルをコピーし、元ファイルを空にする。ログファイルのクローズができないプログラムの場合にデフォルトのままだとローテーションしたはずのアーカイブの方にログを書き続けてしまう。ログファイルをオープンしたままローテーションしたい場合に使う。※コピーしてから空にするまでに若干のタイムラグがあり、その間のログが消失するリスクがある。 |
| `nocopytruncate` | - | - | ログファイルをコピーした後、元ファイルを空にしない。この設定をした場合 `copytruncate` の設定を上書きする。 |
| `copy` | - | - | ログファイルをコピーするが、元ファイルはそのままにする。ログファイルのスナップショットを取得する場合や、他のユーティリティがファイルの切り捨てを要求している場合、ファイルの解析をする場合などに使う。この設定をした場合 `create` の設定を上書きする。 |
| `nocopy` | - | - | ログファイルをコピーしない。この設定をした場合 `copy` の設定を上書きする。 |

### ログファイルのファイル名に関する設定

| ディレクティブ | オプション | オプション例 | 説明 |
| :-- | :-- | :-- | :-- |
| `dateext` | - | - | ローテーションしたログのsuffixに番号をつけるのではなく、日付8桁(-YYYYMMDD) をつける。 |
| `dateformat` | フォーマット文字列 | `_%Y-%m-%d` | 日付のフォーマットを指定する。`%Y,%m,%d,%H,%s` が使用できる。※3.9.0より前のバージョンでは `%H:%M:%S` が使えない。 |
| `dateyesterday` | - | - | dateextで付加される日付に、今日ではなく昨日の日付を使う。これにより、ローテーションされたファイルのタイムスタンプと、ファイル名の日付が一致する。 |
| `extension` | 拡張子 | `.log` | ログのローテーション後も、指定された拡張子が維持される。圧縮を使用すると拡張子の後に圧縮の拡張子が続く。例えば、`mylog.log` という名前のログファイルがあり、それを `mylog.log.1.gz` ではなく、 `mylog.1.log.gz` のように番号の後ろに拡張子を付けてローテーションさせることができる。 |
| `start` | 数字 | `5` | ローテーションファイルの末尾に付加するナンバーの始まり。デフォルトは `1`。 例えば `5` を指定すると、`a.log` => `a.log.5` => `a.log.6` => … とログファイル名が `5` から始まる。 |
| `tabooext` | [+] ext[,ext,…] | `+ .test` | このオプションで指定された拡張子を持つファイルは処理されなくなる。デフォルトは `.rpmsave, .rpmorig, ~, .disabled, .dpkg-old, .dpkg-dist, .dpkg-new, .cfsaved, .ucf-old, .ucf-dist, .ucf-new, .rpmnew, .swp, .cfsaved, .rhn-cfg-tmp-*` が除外されている。`+` を記述すると指定した拡張子は既存の拡張子リストに追加される。そうでない場合は既存のリストを置き換える。 |

### ログファイルのディレクトリに関する設定

| ディレクティブ | オプション | オプション例 | 説明 |
| :-- | :-- | :-- | :-- |
| `olddir` | ディレクトリ名 | `/var/backup/log` | ローテーション時に指定したディレクトリに移動される。移動先は元と同じデバイス上でなければならない。絶対パス、相対パス、どちらも指定可能。この設定をした場合 `noolddir` の設定を上書きする。 |
| `noolddir` | - | - | 同じディレクトリでログをローテーションする。※デフォルト |
| `createolddir` | [パーミッション ユーザー名 グループ名]  | `0644 nginx nginx` | `olddir` で指定されたディレクトリが存在しなかった場合、新しくディレクトリを作成する。 |
| `nocreateolddir` | - | - | `olddir` で指定されたディレクトリが存在しない場合、ディレクトリを作成しない。 |

### ログの圧縮に関する設定

| ディレクティブ | オプション | オプション例 | 説明 |
| :-- | :-- | :-- | :-- |
| `compress` | - | - | ローテーションしたログをgzipで圧縮。 |
| `compresscmd` | 圧縮コマンド | `/usr/bin/bzip2` | gzip以外で圧縮したい場合に圧縮するのに用いるコマンドを指定する。`compresscmd` で圧縮プログラムを変更した場合は `compressext` の値も変更しなければエラーになる可能性が高い。 |
| `compressext` | 拡張子 | `.bz2` | 圧縮されたログファイルの拡張子を指定する。デフォルトでは、設定された圧縮コマンドに従う。 |
| `compressoptions` | 圧縮オプション | `--best` | 圧縮プログラムに渡すコマンドラインオプションを指定する。デフォルトの gzip では `-6`（圧縮率優先）が指定されている。 |
| `nocompress` | - | - | ローテーションしたログを圧縮しない。 |
| `delaycompress` | - | - | 1世代目のログは圧縮しない。ログファイルのクローズができないプログラムの場合にデフォルトのままだとローテーションしたはずのアーカイブの方にログを書き続けてしまうが、圧縮されていると書き込めないため、その場合を想定して遅らせる。`compress` が指定されていなければ無視される。 |
| `delaycompress` | - | - | ログは1世代目から圧縮する。`compress` が指定されていなければ無視される。 |

### ログファイルのローテーション数および削除に関する設定

| ディレクティブ | オプション | オプション例 | 説明 |
| :-- | :-- | :-- | :-- |
| `rotate` | 回数 | `7` | 指定した回数分のログを確保し、それより古いログファイルを削除する。 |
| `maxage` | 日数 | `30` | 指定した日数分のログを確保し、それより古いログファイルを削除する。 |
| `shred` | - | - | `unlink()` の代わりに、`shred -u` を使用してログファイルを削除する。※`shred` は削除するファイルを乱数で埋めてデータが復旧できないようにしてから削除する。 |
| `shredcycles` | 回数 | `5` | 削除前にログファイルを上書きする回数を `shred` に指示する。この設定が省略された場合は `shred` のデフォルト値が採用される。 |
| `noshred` | - | - | 古いログファイルを削除するときに `shred` を使用しない。※デフォルトの設定 |

### ログローテーション時のメールに関する設定

| ディレクティブ | オプション | オプション例 | 説明 |
| :-- | :-- | :-- | :-- |
| `mail` | メールアドレス | `mail@example.com` | ログがローテーションによって存在期間を超えた場合に、指定したメールアドレスに送信される。 |
| `mailfirst` | - | - | `mail` ディレクティブを設定している場合に、存在期間が切れるファイルではなく、たった今ローテーションされたファイルが送信される。 |
| `maillast` | - | - | `mail` ディレクティブを設定している場合に、たった今ローテーションされたファイルではなく、存在期間が切れたファイルが送信される。※`mail` ディレクティブを使用した場合のデフォルトの設定 |
| `nomail` | - | - | ログファイルをメール送信しない。 |

### ログローテーション時のエラーに関する設定

| ディレクティブ | 説明 |
| :-- | :-- |
| `missingok` | ログファイルが存在しなくてもエラーを出さずに処理を続行する。 |
| `nomissingok` | ログファイルが存在しない場合にエラーを出す。※デフォルトの設定 |

### ログローテーション時に実行するスクリプトの設定

| ディレクティブ | 説明 |
| :-- | :-- |
| `prerotate/endscript` | 記述されたコマンドをログローテーションの前に実行する。`sharedscripts` が指定されている場合、すべてのファイルへの絶対パスがスクリプトに渡される。`sharedscripts` が指定されていなければ、対象ログファイル毎にコマンドが実行される。 |
| `postrotate/endscript` | 記述されたコマンドをログローテーションの後に実行する。`sharedscripts`が指定されている場合、すべてのファイルへの絶対パスがスクリプトに渡される。`sharedscripts` が指定されていなければ、対象ログファイル毎にコマンドが実行される。 |
| `firstaction/endscript` | 記述されたコマンドをログローテーションの最初に実行する。ログファイルのパスがワイルドカードだった場合、ワイルドカードの絶対パスがスクリプトに渡される。 |
| `lastaction/endscript` | 記述されたコマンドをログローテーションの最後に実行する。ログファイルのパスがワイルドカードだった場合、ワイルドカードの絶対パスがスクリプトに渡される。 |
| `preremove/endscript` | ログファイルの削除前に実行される。実行後に削除されるファイルの名前がスクリプトに渡される。 |
| `sharedscripts` | デフォルトでは `prerotate` と `postrotate` スクリプトはローテーションされるログファイルごとに実行され、そのログファイルへの絶対パスがスクリプトの最初の引数として渡される。`sharedscripts` が指定された場合、ワイルドカード指定にどんなに多くのファイルがマッチしたとしても、スクリプトは一度だけ実行される。 |
| `nosharedscripts` | `prerotate` と `postrotate` スクリプトをローテーションされるログファイルごとに実行する。そのログファイルへの絶対パスがスクリプトの最初の引数として渡される。 |

- 実行するコマンドは `/bin/sh` が使用される。
- ローテーションが必要なものが一つもない場合はスクリプトは実行されない。

### その他の設定

| ディレクティブ | オプション | オプション例 | 説明 |
| :-- | :-- | :-- | :-- |
| `su` | [ユーザー名 グループ名] | `nginx nginx` | 指定されたユーザーとグループでログファイルをローテーションする。デフォルトはいずれもrootになっている。`logrotate` のバージョン3.8.0から導入された機能。 |
| `include` | ファイルまたはディレクトリ | - | 引数に与えられたファイルを、`include` ディレクティブがある位置に読み込む。ディレクトリが指定された場合、ディレクトリ内のほとんどのファイル（ディレクトリや名前付きパイプ、`tabooext` ディレクティブで指定された名前で終わるファイルは除外される）がアルファベット順に読み込まれる。 |


## ログローテーションの実行順序

1. firstaction/endscriptの実行
2. ローテーション済みファイルのローテーション(例：log.1.gz->log.2.gz)
3. prerotate/endscriptの実行
4. 対象ファイルのローテーション(例：log->log.1)
5. postrotate/endscriptの実行
6. ファイルの圧縮(例：log.1->log.1.gz)
7. preremove/endscriptの実行※
8. 圧縮元ファイルの削除(例：log.1の削除)※
9. preremove/endscriptの実行※
10. 期限切れファイルの削除(例：log.3.gzの削除)※
11. lastaction/endscriptの実行

※preremoveはローテーション対象ファイル数だけ繰り返し実行される。

## 設定例

### apacheの設定例
```bash
/var/log/httpd/*log {   # ログファイルのパスをここで指定している。ワイルドカードが使える。
    missingok           # 指定のログファイルがなくても処理続行。
    notifempty          # ログファイルが空ならスキップ。
    sharedscripts       # 複数指定したログファイルに対し、postrotateまたはprerotateで記述したコマンドを１度だけ実行。
    delaycompress       # 1世代目のログは圧縮しない。
    postrotate          # 記述されたコマンドをログローテーション後に実行。ここではapacheに設定の再読み込みをさせている。
        /sbin/service httpd reload > /dev/null 2>/dev/null || true
    endscript
}
```

### syslogの設定例
```bash
# 4つのログファイルをまとめて設定
/var/log/cron
/var/log/messages
/var/log/secure
/var/log/spooler
{
    missingok           # 指定のログファイルがなくても処理続行。
    sharedscripts       # 複数指定したログファイルに対し、postrotateまたはprerotateで記述したコマンドを１度だけ実行。
    postrotate          # 記述されたコマンドをログローテーション後に実行。syslogdの動作中にファイルを移動すると、syslogdは出力すべきログファイルを見失ってしまうため、syslogdにHUPシグナルを送って設定を再読み込みさせている。
        /bin/kill -HUP `cat /var/run/syslogd.pid 2> /dev/null` 2> /dev/null || true
    endscript
}
```

### WEBアプリケーションのログのローテーション設定例
```bash
# ログファイルのパス
/var/www/html/application/logs/log.php {
    compress             # ローテーションしたログをgzipで圧縮
    daily                # 毎日実行する
    dateext              # ローテーションしたログのsuffixに番号をつけるのではなく、日付8桁(-YYYYMMDD) をつける。
    dateformat -%Y-%m-%d # 日付フォーマットを指定。
    delaycompress        # 1世代目のログは圧縮しない。
    extension .php       # 拡張子をログに合わせる。
    missingok            # 指定のログファイルがなくても処理続行。
    rotate 60            # 60日間保持
    su www-data www-data # ユーザーとグループは環境に合わせて変更してください。
    create 666 www-data www-data
    lastaction
        # 所有者に読み込みと書き込みの権限を付与
        chmod u+rw /var/www/html/application/logs/*.php
    endscript
}
```

## コマンド

### 書式

```bash
logrotate [-dv] [-f|--force] [-s|--state ステータスファイル] 設定ファイル ..
```

### オプション

| オプション | 説明 |
| :-- | :-- |
| `-?` or `–help` | ヘルプメッセージを表示する。 |
| `-d` or `–debug` | デバッグモードをオンにし、-vを暗黙的に指定する。デバッグモードではログやlogrotateのステータスファイルは変更されない。 |
| `-f` or `–force` | 強制的にログローテーションを実行する。 |
| `-m コマンド` or <br>`–mail コマンド` | ログをメール送信する際に使用するコマンドを指定する。 |
| `-s ステータスファイル` or <br>`–state ステータスファイル` | 代替のステータスファイルを使うように指示する。 |
| `–usage` | 簡潔な使用法を表示する。 |
| `+-v` or `–verbose` | 詳細表示モードをオンにする。 |

## コマンド例

ログローテーションの設定をしたあとにデバッグモードで動作確認するときのコマンド。
※実際にはログローテーションはされない。

```bash
# logrotate -d /etc/logrotate.conf
```

条件が揃っていないログファイルも強制的にローテーションさせた場合の動作確認をデバッグモードで実施するときのコマンド。
※実際にはログローテーションはされない。

```bash
# logrotate -fd /etc/logrotate.conf
```

実際に実行して確かめるときのコマンド。
※条件が揃っていれば実際にログローテーションはされる。

```bash
# logrotate -v /etc/logrotate.conf
```

条件が揃っていないログファイルも強制的にローテーションさせるときのコマンド。

```bash
# logrotate -f /etc/logrotate.conf
```

ローテーションされたログファイルの日付を確認する。
※[OSやバージョンによってステータスのファイルのパスが違う](#ファイル構成)ので注意。

```bash
# cat /var/lib/logrotate.status
```


## 注意点

### 実行権限について

logrotate は cron で実行されているので、cron の設定および logrotate の設定ファイルでユーザーを変更していない限り、root 権限で実行される。

ログファイルの親ディレクトリが `o+w` かグループが root 以外で `g+w` である場合はロテートしてくれない。

また、`preremove/endscript` や `postremove/endscript` などでスクリプトを実行する場合もroot権限で実行されるので、不適切なスクリプトだと想定外のファイルを削除してしまったり、最悪の場合はサーバが破損してしまう恐れがある。

この場合は、`su` オプションで権限を変更するか、検証環境で動作確認してから行う。

### ワイルドカードについて

ログファイルをワイルドカードで指定している場合、ローテーションされたログまで含まれてしまっているとログファイルがねずみ算的に増えていってしまうことになる。

ワイルドカードは `~/*` というような形ではなく、`~/*.log` のように拡張子を付けてログとアーカイブを判別できるようにしておく。

### copytruncateについて

`copytruncate` オプションはコピーしてから空にするまでに若干のタイムラグがあり、その間のログが消失するリスクがある。

特別な理由がない限りは使わない方が良い。

### dateformatについて

ログファイル名が日付フォーマットの場合、logrotateはローテーション時にどのファイルが最も古いかをファイル名でソートして判断しているため、日付のフォーマットが `%d-%m-%Y` とかになっていると新しいログを削除してしまう可能性がある。

`-%Y-%m-%d` のように年月日の順になるようにする。まあ、日本人なら逆にする人はいないでしょうが。

### hourlyについて

logrotate はデフォルトでは cron.daily で実行されるように設定されている。そのままだと `hourly` を指定しても daily でローテーションされることになる。

`hourly` でローテーションしたい場合は `cron.hourly` で logrotate が実行されるようにする。

logrotateのcronの設定を、dailyからhourlyに移動。

```bash
# mv /etc/cron.daily/logrotate /etc/cron.hourly/
```

## 参照
https://hackers-high.com/linux/man-jp-logrotate/#nocopy
https://isleofhoso.com/linux-logrotate-size/
https://www2.filewo.net/wordpress/2013/03/31/logrotate%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88%E3%81%AE%E8%AA%BF%E6%9F%BB/
https://qiita.com/hdtkkj/items/85d8d3bdfc45ffeaba89
https://mogu2itachi.hatenablog.com/entry/2020/01/26/145929
http://1000k.github.io/2010/05/20/usage-of-logrotate/