📝

logrotateしたファイルをcatしてtail -fする

1 min read 3

logrotate すると some-log.1 のようなファイルに順次内容が逃がされていく。cat some-log.* すればそれらを結合して眺められるが、最新のデータも結合してかつ tail -f のようにリアルタイムに更新分が追加されて欲しい場合はどうするかというメモ。

以下のようにする。

LOG_FILE=/var/log/some-log
{ ls -r ${LOG_FILE}.* | xargs cat; tail -F ${LOG_FILE} 2> /dev/null; }

ls -r はlogrotateした過去のファイルから結合するために逆順に得られるようにしている。

cat した過去のログファイルと、最新のログファイルを tail -F したものを {} で結合して表示する。tail -f ではなく -F なのはlogrotateされた場合に同じ名前の新しいファイルを開き直すため。その際のエラーメッセージを除外するため標準エラー出力をリダイレクトする。

さらにパイプで別の処理につなげたい場合には ==> var/log/some-log <== のようなヘッダが邪魔になるので、その場合は tail -q で表示しないようにする。{ command1; command2} の出力はそのままパイプで渡せるらしい。例えばjqで時刻フィールドだけ表示させると以下のようになる。

LOG_FILE=/var/log/some-log
{ ls -r ${LOG_FILE}.* | xargs cat; tail -Fq ${LOG_FILE} 2> /dev/null; } | jq '.timestamp'

Discussion

GNU tail の場合は tail -F で出来まする... ( https://www.gnu.org/software/coreutils/manual/html_node/tail-invocation.html )

ホントダァァ😇(macOS にもありました

     -F      The -F option implies the -f option, but tail will also check to see if the
             file being followed has been renamed or rotated.  The file is closed and
             reopened when tail detects that the filename being read from has a new inode
             number.  The -F option is ignored if reading from standard input rather than
             a file.

良い休日の回り道でした。

ちょっと勘違いしていたのですが、tail -F はinodeではなく名前を追跡するオプションなんですね。今回私が欲しかったのは過去のログ(本文の例の場合は some-log.{1,2,...})もまとめて tail -f できるような方法でした。

ただ、logrotate がかかると対象のログファイルがinodeが違う新しいファイルに置き換わるというのは盲点でした。

それで色々手元で試したところ { ls -r log.* | xargs cat; tail -Fq log; } が私の欲しかったものに近い挙動をするようです。

$ ls
log  log.1  log.2  log.3

$ tail *
==> log <==
log
==> log.1 <==
log.1
==> log.2 <==
log.2
==> log.3 <==
log.3

# 途中で mv log log.old && echo new-log > log する
# 2> /dev/null でメッセージは消える
$ { ls -r log.* | xargs cat; tail -Fq log; }
log.3
log.2
log.1
log
tail: 'log' has become inaccessible: No such file or directory
tail: 'log' has appeared;  following new file
new-log
ログインするとコメントできます