ログを “tail -f” で監視している時にログローテートや “mv” されると何も表示されなくなってしまうことに対処

2023-03-23Bash,Linux

はじめに

サーバの運用作業を行っているとき、リリース作業を行っているときにログを監視するために tail -f コマンドを利用します。

tail -f でログを監視している時にログが急に止まってしまい、「ドキッ」としたことはないでしょうか。
実際にはログローテートされていたり、ログファイルがリネームされただけだった、というケースがあります。

さて、この ログファイルがリネームされた時に tail -f で監視していたログ出力が止まってドキッとしてしまう問題 をなんとかできないものでしょうか。

今回はこの問題に対する対処法を紹介します。

動画解説

動画でも解説しています。

検証環境

$ uname -moi
x86_64 x86_64 GNU/Linux

$ bash -version | head -n 1
GNU bash, version 4.2.46(2)-release (x86_64-redhat-linux-gnu)

$ tail --version | head -n 2
tail (GNU coreutils) 8.32
Copyright (C) 2020 Free Software Foundation, Inc.

問題の再現方法

説明文だけではわかりにくいので、実際に問題を発生させてみます。

今回はターミナルを 3 つ立ち上げる必要があります。

1 つ目のターミナル – ログファイルを監視

1 つ目のターミナルではログファイルを監視します。

# 監視対象のログファイルを作成しておきましょう。
$ touch one.log

# tail -fコマンドでログファイルを監視
$ tail -f one.log

何も表示されません。まだログファイルに何も書き込んでいないためです。

2 つ目のターミナル – ログファイルに書き込み

ループをして、1 秒ごとに現在日時を one.log に書き込み続けます。

# dateコマンドを使って現在日時を書き込み続けます。
$ while :; do date >> one.log; sleep 1; done

このタイミングで「1 つ目のターミナル」に情報が表示され始めます。

$ tail -f one.log
Tue Mar 23 23:58:23 UTC 2021
Tue Mar 23 23:58:24 UTC 2021
Tue Mar 23 23:58:25 UTC 2021
Tue Mar 23 23:58:26 UTC 2021
Tue Mar 23 23:58:27 UTC 2021
Tue Mar 23 23:58:28 UTC 2021
Tue Mar 23 23:58:29 UTC 2021
Tue Mar 23 23:58:30 UTC 2021

3 つ目のターミナル – ログファイルを mv する

ここで 3 つ目のターミナルで one.log をリネームします。

$ mv one.log one.log.1

# リネーム直後
$ ls
one.log.1

# リネームをしても、2つ目のターミナルで随時 one.log にファイルを書き出し続けているので
# 新しいファイルがすぐに作成される
$ ls
one.log  one.log.1

すると、 one.log ファイルが有るにも関わらず 1 つ目のターミナルには何も表示されなくなってしまいます。

なぜ表示されないの? tail -f コマンドは何を監視しているの?

なぜ one.log を監視しているのに何も表示されなくなってしまったのでしょうか。

理由は tail -f コマンドのデフォルトの挙動に関係しています。

実は tail -f コマンドは tail --follow=descriptor というコマンドの省略形です。

このオプションを付与されていると、ファイル名が変更されても変更されたファイルを監視します。

以下のコマンドを実行すれば、「1 つ目のターミナル」にメッセージが表示されます。

# リネームされたファイルに内容を追記
$ date >> one.log.1

# もう一丁!
$ date >> one.log.1

対処法

tail コマンドの他のオプションを利用してやることで対処できます。

--follow=name--retry という 2 つのオプションを利用します。

--follow=name-f / --follow=descriptor とは異なる挙動をさせるためのオプションです。ファイルがリネームされたり、別ファイルで上書きされたりした場合は新しいファイルに追従します。

--retry はほぼ必須だと思っていただければよいです。

実行してみます。

$ tail --follow=name --retry one.log
Tue Mar 23 23:58:23 UTC 2021
Tue Mar 23 23:58:24 UTC 2021
Tue Mar 23 23:58:25 UTC 2021
Tue Mar 23 23:58:26 UTC 2021
Tue Mar 23 23:58:27 UTC 2021

ここで、「3 つ目のターミナル」でファイルをリネームします。
すると、以下のように途中でファイルが新しく作成し直されたため、新しいファイルを監視したというメッセージが表示されます。

Tue Mar 23 23:58:32 UTC 2021
Tue Mar 23 23:58:33 UTC 2021
Tue Mar 23 23:58:34 UTC 2021
Tue Mar 23 23:58:35 UTC 2021
tail: 'one.log' has been replaced;  following end of new file
Tue Mar 23 23:58:36 UTC 2021
Tue Mar 23 23:58:37 UTC 2021
Tue Mar 23 23:58:38 UTC 2021
Tue Mar 23 23:58:39 UTC 2021
Tue Mar 23 23:58:40 UTC 2021
Tue Mar 23 23:58:41 UTC 2021
Tue Mar 23 23:58:42 UTC 2021
Tue Mar 23 23:58:43 UTC 2021
Tue Mar 23 23:58:44 UTC 2021
Tue Mar 23 23:58:45 UTC 2021
Tue Mar 23 23:58:46 UTC 2021
Tue Mar 23 23:58:47 UTC 2021

2 つのオプションをまとめた -F を使うと便利

--follow=name--retry をあわせた -F というオプションがあります。

先程も言ったとおり、 --follow=name オプションと --retry オプションはほぼ一緒に使われることになりますので、これらをまとめた -F オプションが用意されています。

-F オプションを使うと、次のように書くことができます。

tail -F one.log

このコマンドを実行すると、 one.log ファイルが一時的に削除やリネームされたとしても、再び one.log という名前のファイルが作られたら、そのファイルを追跡し続けます。

ひとこと

動画による解説も始めました。

ただ、花粉症がひどくて喋りづらいです。

2023-03-23Bash,Linux