| Category: | Sysadmin utlities |
| Author/Contact Info | Artem Litvinovich |
| Description: | Logregate README. Author: Artem Litvinovich The purpose of logregate is to combine several log files from a single system and arrange the log entries by their proximity to each other in time. For example, entries from apache's access.log, messages log, and secure log which were written between 7:40am and 7:45am will be displayed next to each other. Logregate make it much easier to determine the source of a problem that occurred around a particular time window. |
#!/usr/bin/perl -w # Logregate # Artem Litvinovich, 2001-08-02 # an aggregate log analyzer # aims to provide output from various log sources in a time syncronize +d manner # logregate utility by Artem Litvinovich use strict; use Config::General; use Getopt::Std; use Time::Local; use POSIX; my %logtable = (); my @sections = (); my ($interval) = (""); &main(); exit 1; sub main { my (%months_3ltr,%months_full) = (); @months_3ltr{ qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec) +} = @months_full{ qw(January February March April May June July August + September October November December) } = (0..11); my %reserved = ( 'default_timestamp' => [ qw( ^($M=\w+)\b($D=\d+)\b($h=\d+ +):($m=\d+):($s=\d+)\b($data=.*)$ )], # timestamp works on syslog file +s 'default_interval' => ["1m"], 'default_maxlines' => ["-1"], 'default_nostamp' => ["log"], 'default_fromtime' => ["00,00,00,12,02,1982"], 'default_totime' => ["59,59,23,30,11,2020"] ); # get command line options my %options = (); getopt('c:',\%options); my $conFile = exists($options{c}) ? $options{c} : "./logregate.con +f"; die "Unable to find config file ($conFile).\nUsage: logregate [-c +config_file]\n" unless(-f $conFile); # load config file my $conf = new Config::General($conFile); my %config = $conf->getall; # get grouping interval $interval = exists($config{default_interval}) ? interval2sec($conf +ig{default_interval}) : interval2sec($reserved{default_interval}); foreach my $section (keys %config) { next if(exists $reserved{$section}); # check with path, then without, store if found, next if doubt my ($logfile) = exists($config{$section}{path}) ? (-f $config{$sec +tion}{path}."/".$section ? ($config{$section}{path}."/".$section) : n +ext) : (-f $section ? ($section) : next); # conf must have a way to get the timestamp, use default if regex +omitted my ($stampRegex,@elements) = exists($config{$section}{timestamp}) +? stamp2regex($config{$section}{timestamp}) : (exists($config{default_timestamp}) ? stamp2regex($config{defa +ult_timestamp}) : $reserved{default_timestamp}); # get maxlines read from logfile my $maxlines = exists($config{$section}{maxlines}) ? $config{$sect +ion}{maxlines} : (exists($config{default_maxlines}) ? $config{default_maxlines} + : $reserved{default_maxlines}); # get nostamp action my $nostamp = exists($config{$section}{nostamp}) ? $config{$sectio +n}{nostamp} : (exists($config{default_nostamp}) ? $config{default_nostamp} : + $reserved{default_nostamp}); # get from time limit my $fromTime = exists($config{$section}{fromtime}) ? $config{$sect +ion}{fromtime} : (exists($config{default_fromtime}) ? $config{default_fromtime} + : $reserved{default_fromtime}); $fromTime = timelocal(split(",",$fromTime)); # get to time limit my $toTime = exists($config{$section}{totime}) ? $config{$section} +{totime} : (exists($config{default_totime}) ? $config{default_totime} : $ +reserved{default_totime}); $toTime = timelocal(split(",",$toTime)); open LOGDATA, $logfile; my %logline = ('Y'=>'2001'); my $epochSeconds = 0; # main log-reading loop while(my $line = <LOGDATA>) { chomp($line); $maxlines--; # if maxlines was set >0, loop until it is 0. if( $line =~ m/$stampRegex/ ) { # extract timestamp # record each timestamp element (+data) into logline hash for(my $ecount=0;$ecount<scalar(@elements);$ecount++) { $logline{$elements[$ecount]} = eval("\$".($ecount+1)); } # record entire line if data not specified $logline{data} = $line unless($logline{data}); # when getting month, try to get numerical value $logline{M} = exists($months_3ltr{$logline{M}}) ? $months_3lt +r{$logline{M}} : (exists($months_full{$logline{M}}) ? $months_full{$logline +{M}} : ($logline{M} =~ /^\d*$/ ? $logline{M} : next)); # convert timstamp into a single number (hash key) $epochSeconds = timelocal($logline{s},$logline{m},$logline{h}, +$logline{D},$logline{M},$logline{Y}); } else { # did not find timstamp with regex from conf, examine + alternatives for ($nostamp) { /ignore/ && do { last; }; /log/ && do { $logline{data} = "[NO TIME-STAMP] ".$line; }; /incby\s+(\d+)/ && do { $epochSeconds+=$1; }; /decby\s+(\d+)/ && do { $epochSeconds-=$1; }; } } # add data to overall table push @{$logtable{$epochSeconds}{$section}}, $logline{data} if( +$epochSeconds>=$fromTime && $epochSeconds<=$toTime); last unless($maxlines); } close LOGDATA; push @sections, $section; } &displayHTML(); } sub display { my $mark = -1; foreach my $entrytime (sort keys %logtable) { if($entrytime-$mark > $interval) { print "time: ".localtime($entrytime)."\n"; $mark = $entrytime; } foreach my $section (keys %{$logtable{$entrytime}}) { print "section: $section\n"; foreach (@{$logtable{$entrytime}{$section}}) { print "entry: $_\n"; } } } } sub displayHTML { my $mark = -1; my $colWidth = "300"; my $minColorDiv = "00"; my $maxColorDiv = "5"; my $baseColor = "9933"; my $colorStep = "51"; my $colorOpr = "+"; my $colorMod = $minColorDiv; print "<HTML><BODY>\n"; print "Interval is $interval seconds.<br>\n"; # display deviation spectrum print "<TABLE><TR><td colspan=".($maxColorDiv+1).">Interval separa +tion color spectrum in seconds:</td></TR>\n"; print "<TR>\n"; for ($minColorDiv..$maxColorDiv) { print "<td bgcolor=".($baseColor.($_?dec2hex($colorStep*$_):"00")) +.">". sqr($_)*$interval. "</td>\n"; } print "</TR><br>\n<TR>\n"; for ($minColorDiv..$maxColorDiv) { print "<td bgcolor=".($baseColor.($_?dec2hex($colorStep*$_):"00")) +.">".sqr($maxColorDiv+$minColorDiv-$_)*$interval."</td>\n"; } print "</TR></TABLE><br>\n"; # main agregate log table print "<TABLE border=1 width=".$colWidth*scalar(@sections).">\n"; foreach my $entrytime (sort keys %logtable) { if($entrytime-$mark > $interval) { # if interval reached, reprint +heading my $intDiff = POSIX::floor(sqrt(($entrytime-$mark)/$interval)- +1); if($colorMod >= $minColorDiv+$colorStep*$maxColorDiv) { $colorOpr = "-"; } elsif ($colorMod <= $colorStep*$minColorDiv) { $colorOpr = "+"; } $colorMod += ($colorOpr.$colorStep * $intDiff); if($colorMod>$minColorDiv+$colorStep*$maxColorDiv) { $colorMod = $minColorDiv+$colorStep*$maxColorDiv; } elsif($colorMod<$colorStep*$minColorDiv) { $colorMod = $colorStep*$minColorDiv; } my $color = $baseColor . ($colorMod ? dec2hex($colorMod) : "00 +"); print "<TR><td colspan=".scalar(@sections)." bgcolor=#$color>< +b>time: ".localtime($entrytime)."</b></td></TR>\n"; $mark = $entrytime; print "<TR>"; foreach (sort @sections) { print "<td><b>$_</b></td>\n"; } print "</TR>"; } print "<TR>"; foreach my $section (sort @sections) { print "<td width=$colWidth>"; foreach (exists($logtable{$entrytime}{$section}) ? @{$logtable +{$entrytime}{$section}} : ()) { print localtime($entrytime)." $_<br>\n"; } print "</td>\n"; } print "</TR>"; } print "</TABLE>\n"; print "</BODY></HTML>\n"; } sub stamp2regex { # s - sec, m - min, h - hour, D - day, W - week, M - month, Y - ye +ar # Ex: # "^($M=\w+)\b($D=\d+)\b($h=\d+):($m=\d+):($s=\d+)\b($data=.*)$" f +or syslog # Aug 2 16:57:59 artemz1 su(pam_unix)[18581]: session closed for +user root my ($str) = @_; my @orderedElements = (); # drop encapsulating quotes $str =~ s/^"(.*)"$/$1/; # compile the time_stamp elements in order while($str =~ m/\(\$(.+?)=.+?\)/g) { push @orderedElements, $1; } # convert into a real regex $str =~ s/\(\$.+?=(.+?)\)/\($1\)/g; return ($str,@orderedElements); } sub dec2hex($) { my($dec) = @_; return sprintf("%lx ", $dec ); } sub sqr($) { my($num) = @_; return $num*$num; } sub interval2sec($) { # convert interval to seconds # s - sec, m - min, h - hour, D - day, W - week, M - month, Y - ye +ar my ($interval) = @_; $interval =~ s/^(\d+)(\w)$/$1/; for ($2) { /s/ and last; $interval *= 60; /m/ and last; $interval *= 60; /h/ and last; $interval *= 24; /D/ and last; $interval *= 7; /W/ and last; $interval *= 4; /M/ and last; $interval *= 12; /Y/ and last; $interval = $1; # wierd denomination, assuming seconds. } return $interval; } ------------- Sample conf file: # list of logs # time interval default # time interval for each # s - sec, m - min, h - hour, D - day, M - month, Y - year # default_timestamp "^($M=\w+)\s+($D=\d+)\s+($h=\d+):($m=\d+):($s=\d+)\s ++($data=.*)$" default_interval 5m default_nostamp log default_fromtime 00,00,10,6,7,2001 default_totime 00,00,15,6,7,2001 <./u/messages> </./u/messages> <./u/maillog> </./u/maillog> <./u/cron> timestamp "^.*?\(($M=\d+).($D=\d+).($h=\d+):($m=\d+):($s=\d+).($data=. +*)$" </./u/cron> <./uniserv/messages> </./uniserv/messages> <./uniserv/maillog> </./uniserv/maillog> <./uniserv/cron> timestamp "^.*?\(($M=\d+).($D=\d+).($h=\d+):($m=\d+):($s=\d+).($data=. +*)$" </./uniserv/cron> <./uniserv/secure> </./uniserv/secure> |
|
|
|---|