]> git.etc.gen.nz Git - spong.git/commitdiff
Initial revision
authorStephen L Johnson <sjohnson@monsters.org>
Wed, 19 May 1999 16:07:39 +0000 (16:07 +0000)
committerStephen L Johnson <sjohnson@monsters.org>
Wed, 19 May 1999 16:07:39 +0000 (16:07 +0000)
src/lib/Spong/Client/logmon.pm [new file with mode: 0755]

diff --git a/src/lib/Spong/Client/logmon.pm b/src/lib/Spong/Client/logmon.pm
new file mode 100755 (executable)
index 0000000..e5ed118
--- /dev/null
@@ -0,0 +1,200 @@
+# 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;