--- /dev/null
+# Package monitors a log file in syslog format for one or more
+# regular expressions that are classed as warnings or errors
+# The package monitors the last 20 minutes or the the last 8KB up
+# start up.
+#
+# logfile - the name of the log file that is being monitored
+# checks - list of regexp's to check for along with alert level
+#
+# procedures
+# +add_check - add a expression to check for
+# +logfile - Returns the name of a log file being checked
+#
+# 03/22/99 - Stephen L Johnson
+# Package initially created.
+# 05/19/99 - Stephen L Johnson
+# Fixed a couple of bugs in the &new_file() which cause
+# routine to not open the new file.
+
+
+package Spong::Client::logmon;
+
+use Carp;
+use IO::File;
+use POSIX qw(mktime);
+
+# Constructor
+
+sub new {
+ my ( $class, $file ) = @_;
+ my $self = {};
+ my ( $pos, $time, $daytime );
+
+ $self->{'checks'} = ();
+ $self->{'logfile'} = $file;
+
+ my ( $inode, $size, $mtime ) = (stat($file))[1,7,9];
+
+ $self->{'inode'} = $inode;
+ $self->{'size'} = $size;
+ $self->{'mtime'} = $mtime;
+
+ my ( $scantime ) = 2400;
+ my ( $scansize ) = $size < 8000 ? $size : 8000;
+
+ # Create a file handle and open the file
+ $fh = IO::File->new();
+ $self->{'fh'} = $fh;
+ $self->{'open'} = $fh->open("<$file");
+
+ # If the file was opened
+ if ($self->{'open'}) {
+ # Seek to the last scansize bytes of the file
+ $fh->seek(-$scansize,2);
+ # If not a beginning of file, read to start of next line
+ if ( $fh->tell() ) { <$fh>; }
+
+ # Position file cursor until it is within the $scantime time frame
+ while (1) {
+ $pos = $fh->tell();
+ ($_ = <$fh>) || last;
+ last unless /^([A-Z][a-z]{2})\s+(\d+)\s+([\d:]+)/;
+ $ltime = logdate_to_time($1,$2,$3);
+ last if ($ltime >= (time-$scantime));
+ }
+ $self->{'pos'} = $pos;
+ }
+
+ $self->{'stati'} = {};
+
+ bless $self;
+ return $self;
+}
+
+
+# Get methods, events() returns a list rather then just the list reference
+
+sub logfile {
+ return $_[0]->{'logfile'} }
+sub checks {
+ return @{$_[0]->{'checks'}} }
+sub stati {
+ return $_[0]->{'stati'} }
+
+# add_check() is used to add checks to the check list.
+
+sub add_check {
+ my( $self, $chk, $status, $duration, $text, $id ) = @_;
+
+ if ( ref($chk) eq "SCALAR" ) {
+
+ my $pattern = $chk;
+
+ $status =~ tr/A-Z/a-z/;
+ if ($status ne 'green' && $status ne 'yellow' && $status ne 'red') {
+ croak "status must be green, yellow or red";
+ }
+
+ push @{$self->{'checks'}},
+ { pattern=>"$pattern", status=>"$status",
+ duration=>"$duration", text=>"$text", id=>"$id" };
+ } elsif ( ref($chk) eq "HASH" ) {
+ if ( ! ( defined $chk->{'pattern'} && defined $chk->{'status'} &&
+ defined $chk->{'duration'} && defined $chk->{'text'} ) ) {
+ croak "invalid hash. It must have pattern, status, duration and text entries";
+ }
+
+ push @{$self->{'checks'}}, $chk;
+
+ } else {
+ croak "invalid parameters";
+ }
+
+}
+
+# check() - This procedure does the line by line checks of the logfile
+
+sub check {
+ my( $self ) = @_;
+
+ $fh = $self->{'fh'};
+
+ if ($self->{'open'}) {
+ my ($rep,$text);
+
+ $fh->seek($self->{'pos'},0);
+ while (<$fh>) {
+ foreach $rep (@{$self->{'checks'}}) {
+ $pattern = $rep->{'pattern'};
+ if (/$pattern/i) {
+ $text = eval '"' . $rep->{'text'} . '"';
+ $id = $text if ! $rep->{'id'};
+ $self->{'stati'}->{$id} = { item=>'logs',
+ status=>$rep->{'status'},
+ endtime=>time+($rep->{'duration'}*60),
+ text=>"$text" };
+ }
+ }
+ }
+ $self->{'pos'} = $fh->tell();
+ }
+
+
+ my $key;
+ my $rep;
+ my $stati = $self->{'stati'};
+
+ # Scan all report for duration and remove the old ones
+ foreach $key (keys %$stati) {
+ $rep = $stati->{$key};
+ $endtime = $rep->{'endtime'};
+ if ($endtime <= time) {
+ delete $$self{'stati'}->{$key};
+ }
+ }
+
+ # If there is a new log file, continue the continue th check
+ if ( $self->new_file() ) { $self->check() }
+
+}
+
+
+# Miscellaneous internal routines
+
+sub logdate_to_time {
+ my ($lmon,$lday,$ltime) = @_;
+ my (@MON) = ("Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep",
+ "Oct","Nov","Dec");
+ my ($mon,);
+ foreach ( 0..11 ) { if ($MON[$_] eq $lmon) {$mon = $_; last;} }
+
+ my($yr) = (localtime())[5];
+
+ my($hr,$min,$sec) = (split(/:/,$ltime));
+
+ return (mktime($sec,$min,$hr,$lday,$mon,$yr))
+}
+
+# Check to see if the log file has been rotated
+
+sub new_file {
+ my ( $self ) = @_;
+
+ # Stat the filie
+ my ( $inode, $size, $mtime ) = (stat($self->{'logfile'}))[1,7,9];
+
+ if ( $inode != $self->{'inode'} || ( ! $self->{'open'}) ) {
+ $self->{'fh'}->close();
+ $self->{'inode'} = $inode;
+ $self->{'size'} = $size;
+ $self->{'mtime'} = $mtime;
+ $self->{'pos'} = 0;
+ $self->{'open'} = $self->{'fh'}->open("<" . $self->{'logfile'} );
+ return $self->{'open'};
+ }
+ return 0;
+}
+
+
+# Return a return result because I'm a Perl module
+1;