#!/usr/local/bin/perl
# logwatch.pl Ver1.2 by fuku@rouge.gr.jp

#
# ログ監視プログラム
#
# 対象ログがまだ存在しない場合、オープンリトライを続ける。
#
# 対象ログがローテートした場合、元々のファイル名で開き直す。
# (i-node を比較することによりそれを検知する)
#
# ローテートの直前に動作させた場合、一時的に巨大な行数を返すことがある。
#
# my $lw = new logwatch('/var/log/messages', 'TAIL') ;
# while (1)
# 	{
# 	my @buf = $lw->get ;
# 	for ( my $i = 0 ; $i < @buf ; $i ++ )
# 		{
# 		print "$buf[$i]\n" ;
# 		}
# 	sleep 1 ; # 必ず何秒かのスリープを入れること!
# 	}
# 

package logwatch ;

use strict ;
use POSIX ;
use FileHandle ;

# コンストラクタ
#
sub new
{
	my $self = {} ;
	shift ;
	my($logfile,$whence) = @_ ;

	$self->{FILE} = $logfile ;		# ファイル名
	$self->{BUF} = '' ;				# 作業バッファ
	$self->{LEN} = 16384 ;			# 一度に読み込む長さ
	$self->{FH}  = new FileHandle ;	# ファイルハンドル
	$self->{INO} = '' ;				# i-node 番号
	$self->{ALIVE} = 0 ;			# ファイルオープンフラグ
	$self->{WHENCE} = uc $whence ;	# 初回シーク位置

	bless $self ;
	return $self ;
}

# デストラクタ
#
sub DESTROY
{
	my $self = shift ;

	close $self->{FH} ;
	$self->{ALIVE} = 0 ;
}

# 行取得メソッド (複数行)
#
sub get
{
	my $self = shift ;

	if (! $self->{ALIVE})
		{
		# ファイルを開く (スタート指定が無い場合、最後から監視)
		if (open $self->{FH}, "< $self->{FILE}")
			{
			$self->{INO} = (stat($self->{FH}))[1] ;	# i-node 番号を退避
			if ($self->{WHENCE} eq 'HEAD')
				{ seek($self->{FH}, 0, &POSIX::SEEK_SET) }
			else
				{ seek($self->{FH}, 0, &POSIX::SEEK_END) }
			$self->{ALIVE} = 1 ;
			}
		}

	my @line = () ;

	if ($self->{ALIVE})
		{
		my $tmp ;

		# ファイル読み込み
		$self->{BUF} .= $tmp if (read($self->{FH}, $tmp, $self->{LEN}) > 0) ;
		# ローテートチェック
		if ((stat($self->{FILE}))[1] != $self->{INO})
			{
			# ローテートされている

			# 最後尾を読み込む
			while (read($self->{FH}, $tmp, $self->{LEN}) > 0)
				{
				$self->{BUF} .= $tmp ;
				}

			# ファイルを開き直す
			close $self->{FH} ;
			open $self->{FH}, "$self->{FILE}" ;
			$self->{INO} = (stat($self->{FH}))[1] ;
			}

		# 行抽出処理
		# 改行文字 ('\n') までを 1 行とする
		while ($self->{BUF} =~ /\n/)
			{
			my $line ;
			($line, $self->{BUF}) = split /\n/, $self->{BUF}, 2 ;
			push @line, $line ;
			}
		}

	return @line ;	# 読み込んだ行を返す
}

1;

