logrotateスクリプトの調査

Linuxでログファイルを一定期間ごとで切り出し、世代管理を行ってくれるlogrotate。
logrotateでは、単にログをローテションするだけではなく、ローテションの際にスクリプトを動作させることが可能となっています。
それらのスクリプトは、/etc/logrotate.confや、インクルードされる/etc/logrotate.d/hogehogeといったファイルに定義します。

スクリプトを動作させることが可能なタイミングは以下の4つあります。

  • firstaction
    ログローテーションプロセスの1番最初
  • prerotate
    ログファイルをローテーションする前
  • postrotate
    ログファイルをローテションした後
  • lastaction
    ログローテーションプロセスの1番最後

# 正直、firstactionとprerotate、postrotateとlastactionの違いはあまりよく分かっていません。

これらのタイミングで、何らかの処理を行うことが可能ですが、その処理の中にはログファイル自身を扱う処理も行えます。
ログファイル自身を扱うためには、その時点でのログファイルの状態(圧縮されたのか、ローテションされただけなのか、まだ何もされていないのか)などが気になります。
調べては見ましたが、特にまとまった情報も無いようですし、OSやlogrotatedのバージョンによっても挙動が変化するそうなので、今回は自分が使う環境で色々調査してみました。

ローテーション前後のファイル名

ログローテーションを実施した際のファイル名がどうなるのかおさらいしておきます。
apache2のログファイル(/var/log/httpd/access_log)を例に挙げます。

  1. ローテーション前
    access_log
  2. ローテーション後
    access_log.1
  3. 圧縮後
    access_log.1.gz

このように、ファイル名が推移していくわけです。
問題は、firstactionからlastactionのそれぞれのどの段階でファイル名が変わっているかの情報が少ないこと。

スクリプトの引数に渡されるファイル名

実は、スクリプトの中で$1という変数を使用して、スクリプトに渡されるファイル名を受け取ることができます。
具体的には次のようなスクリプトが書けます。

/var/log/httpd/*log {
	daily
	compress
	...(snip)...

	prerotate
		for infile in $1; do
			echo $1
		done
	endscript
}

/var/log/httpd/*log の各々に対してforでループを回しているわけです。
しかし、この$1もやっかいなのです。
firstactionからlastactionのそれぞれの段階で、渡される引数が異なっているようで、やっかいな代物。

検証してみる

検証環境

  • CentOS 6.3

(これだけかよ!)

スクリプトの準備

上記で示した2点、「各時点でのファイル名」と「各時点での$1引数」について、調べてみることにしました。

そのために、以下の様なlogrotateスクリプトを用意します。
元は、/etc/logrotate.d/httpdファイルで、それを修正したものになります。

/var/log/httpd/*log {
	daily
	rotate 100
	nodateext
	compress
	nodelaycompress
	ifempty
	missingok
	sharedscripts

	firstaction
		echo "firstaction start" > /root/logrotate_log
		for infile in $1; do
			echo "    \$1 is $infile" >> /root/logrotate_log
		done
		[ -f /var/log/httpd/access_log ] && echo "    exist access_log" >> /root/logrotate_log
		[ -f /var/log/httpd/access_log.1 ] && echo "    exist access_log.1" >> /root/logrotate_log
		[ -f /var/log/httpd/access_log.1.gz ] && echo "    exist access_log.1.gz" >> /root/logrotate_log
		echo "firstaction end" >> /root/logrotate_log
	endscript

	prerotate
		echo "prerotate start" >> /root/logrotate_log
		for infile in $1; do
			echo "    \$1 is $infile" >> /root/logrotate_log
		done
		[ -f /var/log/httpd/access_log ] && echo "    exist access_log" >> /root/logrotate_log
		[ -f /var/log/httpd/access_log.1 ] && echo "    exist access_log.1" >> /root/logrotate_log
		[ -f /var/log/httpd/access_log.1.gz ] && echo "    exist access_log.1.gz" >> /root/logrotate_log
		echo "prerotate end" >> /root/logrotate_log
	endscript

	postrotate
		/sbin/service httpd reload > /dev/null 2>/dev/null || true
		echo "postrotate start" >> /root/logrotate_log
		for infile in $1; do
			echo "    \$1 is $infile" >> /root/logrotate_log
		done
		[ -f /var/log/httpd/access_log ] && echo "    exist access_log" >> /root/logrotate_log
		[ -f /var/log/httpd/access_log.1 ] && echo "    exist access_log.1" >> /root/logrotate_log
		[ -f /var/log/httpd/access_log.1.gz ] && echo "    exist access_log.1.gz" >> /root/logrotate_log
		echo "postrotateend" >> /root/logrotate_log
	endscript

	lastaction
		echo "lastaction start" >> /root/logrotate_log
		for infile in $1; do
			echo "    \$1 is $infile" >> /root/logrotate_log
		done
		[ -f /var/log/httpd/access_log ] && echo "    exist access_log" >> /root/logrotate_log
		[ -f /var/log/httpd/access_log.1 ] && echo "    exist access_log.1" >> /root/logrotate_log
		[ -f /var/log/httpd/access_log.1.gz ] && echo "    exist access_log.1.gz" >> /root/logrotate_log
			echo "lastaction end" >> /root/logrotate_log
	endscript
}

これでやっていることは、次の通りです。

firstactionからlastactionの各段階で実施していることは同じです。
各段階の、最初と最後で

echo "lastaction start" >> /root/logrotate_log
echo "lastaction end" >> /root/logrotate_log

とすることで、各段階の最初と最後と示します。

次に、

for infile in $1; do
	echo "    \$1 is $infile" >> /root/logrotate_log
done

とすることで、$1にどのような値が含まれているかを表示します。

さらに、

[ -f /var/log/httpd/access_log ] && echo "    exist access_log" >> /root/logrotate_log
[ -f /var/log/httpd/access_log.1 ] && echo "    exist access_log.1" >> /root/logrotate_log
[ -f /var/log/httpd/access_log.1.gz ] && echo "    exist access_log.1.gz" >> /root/logrotate_log

とすることで、この段階で生成されているログファイル名を表示します。

結果を見る

では、このようにして修正した、/etc/logrotate.d/httpdスクリプトをlogrotatedに食わせてみます

# logrotate /etc/logrotate.d/httpd

これでOK。
結果は、/root/logrotate_logに出力されます。

結果はこんな感じになるでしょう。

firstaction start
	$1 is /var/log/httpd/access_log
	$1 is /var/log/httpd/error_log
	exist access_log
firstaction end
prerotate start
	$1 is /var/log/httpd/access_log
	$1 is /var/log/httpd/error_log
	exist access_log
prerotate end
postrotate start
	$1 is /var/log/httpd/*log
	exist access_log.1
firstaction end
lastaction start
	$1 is /var/log/httpd/*log
	exist access_log.1.gz
lastaction end

結構、意外な結果になったかと思います。

まとめるとこんな感じですね。

ファイル名 $1の引数
firstaction access_log /var/log/httpd/access_log
/var/log/httpd/error_log
prerotate access_log /var/log/httpd/access_log
/var/log/httpd/error_log
postrotate access_log.1 /var/log/httpd/*log
lastaction access_log.1.gz /var/log/httpd/*log

ただ、この結果、色々思うところがあってですね。。。
delaycompressだったらどうなるのかとか、sharedscriptじゃなかったらとか。。。
気が向けばこれらもまとめるかもしれません。