--- parse_log.cgi 2003/10/05 19:00:15 1.2 +++ parse_log.cgi 2003/10/07 20:24:45 1.8 @@ -8,51 +8,77 @@ use strict; use POSIX qw(strftime); -use CGI qw/:standard *table/; -use CGI::Carp qw(fatalsToBrowser); +use CGI; +#use CGI::Carp qw(fatalsToBrowser); use Data::Sorting qw(:arrays); use Time::ParseDate; +use Time::Available; +use Cache::FileCache; use Data::Dumper; my $date_fmt = "%Y-%m-%d"; -my $date_time_fmt = "%Y-%m-%d %H:%M:%S"; +#my $date_time_fmt = "%Y-%m-%d %H:%M:%S"; +my $date_time_fmt = "%a %Y-%m-%d %H:%M:%S"; my $from_date = "now - 6 months"; my $to_date = "now"; # working days definition (1-7; mon=1) -my $wday_start = 1; -my $wday_end = 5; +my $dayMask = Time::Available::DAY_WEEKDAY; # working hours -my $whours_start = "7:00"; -my $whours_end = "17:00"; +my $from_time_interval = "7:00"; +my $to_time_interval = "17:00"; -my $debug=1; +my $debug=0; $debug++ if (grep(/-v/,@ARGV)); $debug++ if (grep(/-d/,@ARGV)); +my %days = ( + Time::Available::DAY_MONDAY=>'Mo', + Time::Available::DAY_TUESDAY=>'Tu', + Time::Available::DAY_WEDNESDAY=>'We', + Time::Available::DAY_THURSDAY=>'Th', + Time::Available::DAY_FRIDAY=>'Fr', + Time::Available::DAY_SATURDAY=>'Sa', + Time::Available::DAY_SUNDAY=>'Su' +); + my $q = new CGI; my $print_orphans = $q->param('print_orphans') || 0; my $rep_reset = $q->param('rep_reset') || 0; my @sg_selected = $q->param('sg_filter'); -my @sort; +# init misc sort parametars +my @sort_rules; my $order; my %sort_param; my ($usort,$dsort); if ($q->param('usort')) { $sort_param{'usort'} = $q->param('usort'); $q->delete('usort'); - @sort = ( -compare => 'numeric', $sort_param{'usort'} ); + @sort_rules = ( -compare => 'numeric', scalar $sort_param{'usort'} ); } if ($q->param('dsort')) { $sort_param{'dsort'} = $q->param('dsort'); $q->delete('dsort'); - @sort = ( -compare => 'numeric', -order=>'reverse', $sort_param{'dsort'} ); + @sort_rules = ( -compare => 'numeric', -order=>'reverse', scalar $sort_param{'dsort'} ); +} + +# make interval +my $working_days; +if ($q->param('use_time_limit')) { + $dayMask=0; + foreach my $dm ($q->param('day_interval')) { + $dayMask |= $dm; + } + $working_days=new Time::Available(start=>$q->param('from_time_interval'),end=>$q->param('to_time_interval'),dayMask=>$dayMask); } +# init cache and setup expriration +my $cache = new Cache::FileCache({ default_expires_in => '10 min' }); + # # This option (activated via command switch -r) will reset failure duration # if repeated failure on same group/service happend. @@ -64,11 +90,15 @@ # pretty format date sub d { my $utime = shift || return "?"; - return strftime($date_time_fmt,localtime($utime)); + if ($debug) { + return strftime($date_time_fmt." [%s]",localtime($utime)); + } else { + return strftime($date_time_fmt,localtime($utime)); + } } # pretty format duration sub dur { - my $dur = shift || return "?"; + my $dur = shift || return "0"; my $out = ""; my $s = $dur; @@ -93,118 +123,148 @@ # my %fail; -my %downtime; # total downtime -my %sg_filter; # filter for service/group -my %sg_count; # count number of downtimes - -my $log_file="/home/dpavlin/mon-log/sap.log"; - -my @data; - -open(LOG, $log_file) || die "$log_file: $!"; - -while() { - chomp; - if (/^(failure|up)\s+(\S+)\s+(\S+)\s+(\d+)\s+\(([^)]+)\)\s+(.+)$/) { - my ($status,$group,$service,$utime,$date,$desc) = ($1,$2,$3,$4,$5,$6); - my $id = "$group/$service"; - if ($status eq "up" && defined($fail{$id})) { - if (grep(m;$group/$service;,@sg_selected)) { - push @data, { - 'sg'=>"$group/$service", - 'from_time'=>$fail{$id}, - 'to_time'=>$utime, - 'dur_time'=>($utime - $fail{$id}), - 'from'=>d($fail{$id}), - 'to'=>d($utime), - 'dur'=>dur($utime - $fail{$id}), - 'desc'=>$desc }; - $downtime{"$group/$service"} += ($utime - $fail{$id}), - $sg_count{"$group/$service"}++; - } - $sg_filter{"$group/$service"}++; - delete $fail{$id}; - } elsif ($status eq "up") { - if ($print_orphans && grep(m;$group/$service;,@sg_selected)) { - push @data, { - 'sg'=>"$group/$service", - 'to_time'=>$utime, - 'from'=>'unknown', - 'to'=>d($utime), - 'dur'=>'unknown', - 'desc'=>$desc }; - $sg_count{"$group/$service"}++; - } - delete $fail{$id}; - $sg_filter{"$group/$service"}++; - } elsif (defined($fail{$id})) { - if ($rep_reset && grep(m;$group/$service;,@sg_selected)) { - push @data, { - 'sg'=>"$group/$service", - 'from_time'=>$fail{$id}, - 'to_time'=>$utime, - 'dur_time'=>($utime - $fail{$id}), - 'from'=>d($fail{$id}), - 'to'=>d($utime), - 'dur'=>dur($utime - $fail{$id}), - 'desc'=>'[failure again]'}; - $downtime{"$group/$service"} += ($utime - $fail{$id}), +my $sg_filter; # filter for service/group + +my $log_file="/var/log/mon/sap.log"; + +my $data; + +# generate unique key for this data and options +my $cache_key="monlog".join("|",@sg_selected)."|".$print_orphans."|".$rep_reset; + +# debug disables cache +if (! $debug) { + $data = $cache->get( $cache_key ); + $sg_filter = $cache->get("sg_filter $cache_key"); +} + +if (!$data || !$sg_filter) { + + open(LOG, $log_file) || die "$log_file: $!"; + + while() { + chomp; + if (/^(failure|up)\s+(\S+)\s+(\S+)\s+(\d+)\s+\(([^)]+)\)\s+(.+)$/) { + my ($status,$group,$service,$utime,$date,$desc) = ($1,$2,$3,$4,$5,$6); + my $id = "$group/$service"; + if ($status eq "up" && defined($fail{$id})) { + if (grep(m;$group/$service;,@sg_selected)) { + push @$data, { + 'sg'=>"$group/$service", + 'from'=>$fail{$id}, + 'to'=>$utime, + 'dur'=>($utime-$fail{$id}), + 'desc'=>$desc }; + } + $sg_filter->{"$group/$service"}++; + delete $fail{$id}; + } elsif ($status eq "up") { + if ($print_orphans && grep(m;$group/$service;,@sg_selected)) { + push @$data, { + 'sg'=>"$group/$service", + 'from'=>-1, + 'to'=>$utime, + 'dur'=>0, + 'desc'=>$desc }; + } + delete $fail{$id}; + $sg_filter->{"$group/$service"}++; + } elsif (defined($fail{$id})) { + if ($rep_reset && grep(m;$group/$service;,@sg_selected)) { + push @$data, { + 'sg'=>"$group/$service", + 'from'=>$fail{$id}, + 'to'=>$utime, + 'dur'=>($utime-$fail{$id}), + 'desc'=>'[failure again]'}; + $fail{$id} = $utime; + } + $sg_filter->{"$group/$service"}++; + } else { $fail{$id} = $utime; - $sg_count{"$group/$service"}++; } - $sg_filter{"$group/$service"}++; - } else { - $fail{$id} = $utime; } } + close(LOG); + + $cache->set($cache_key, $data); + $cache->set("sg_filter $cache_key", $sg_filter); + } -close(LOG); # generate output # -print header,start_html("mon availiability report"); +print $q->header,$q->start_html("mon availiability report"); # make some filters # -print start_form, - start_table({-border=>0,-cellspacing=>0,-cellpadding=>0}), - Tr(td( - em("Show just service/group:"),br, - checkbox_group(-name=>'sg_filter', - -values=>[keys %sg_filter], - -default=>[keys %sg_filter], +print $q->start_form,' + + +
+ Show just service/group:
+ ',$q->checkbox_group(-name=>'sg_filter', + -values=>[keys %$sg_filter], + -default=>[keys %$sg_filter], -linebreak=>'true', - ), - ),td( - em("Other options:"),br, + ),' +
+ Other options:
', $q->checkbox(-name=>'rep_reset',-checked=>0, - -label=>"show repeated failures on same service as individual failures"), - br, + -label=>"show repeated failures on same service as individual failures"),'
', $q->checkbox(-name=>'print_orphans',-checked=>0, - -label=>"show records which are not complete in this interval"), - br, + -label=>"show records which are not complete in this interval"),'
', $q->checkbox(-name=>'use_date_limit',-checked=>1, -label=>"use date limit from:"), - $q->textfield(-name=>'from_date',-size=>20,-default=>$from_date), - " to: ", - $q->textfield(-name=>'to_date',-size=>20,-default=>$to_date), - small('Using Time::ParseDate'), - br, - $q->submit(-name=>'show',-value=>'Show report'), - )),end_table; + $q->textfield(-name=>'from_date',-size=>20,-default=>$from_date),' to: ', + $q->textfield(-name=>'to_date',-size=>20,-default=>$to_date),' + Using Time::ParseDate +
+ ',$q->checkbox(-name=>'use_time_limit',-checked=>1, -value=>'on', + -label=>"use time limit for each day:"), + $q->textfield(-name=>'from_time_interval',-size=>8,-default=>$from_time_interval),' to: ', + $q->textfield(-name=>'to_time_interval',-size=>8,-default=>$to_time_interval), + '
Days: ', + $q->checkbox_group(-name=>'day_interval', + -values=>[ sort { $a <=> $b } keys %days ], + -labels=>\%days, + -defaults=>[ + Time::Available::DAY_MONDAY, + Time::Available::DAY_TUESDAY, + Time::Available::DAY_WEDNESDAY, + Time::Available::DAY_THURSDAY, + Time::Available::DAY_FRIDAY, + ] + ), + $q->submit(-name=>'show',-value=>'Show report'),' +
+ ',$q->end_form; + +# bail out of no data +if ($data && scalar @$data < 1) { + print $q->end_html; + exit; +} # dump report # +my %dir_html_entity = ( +# 'u' => '⇑', +# 'd' => '⇓' + 'u' => '▲', + 'd' => '▼', +); + sub sort_link { my $q = shift || return; my $col = shift || return; my $dir = lc(shift) || return; if ($sort_param{$dir.'sort'} && $sort_param{$dir.'sort'} eq $col) { - return '&'.$dir.'Arr;'; + return $dir_html_entity{$dir}; } else { - return '&'.$dir.'Arr;'; + return ''.$dir_html_entity{$dir}.''; } } @@ -221,52 +281,71 @@ # sort data # -my @sorted = sorted_array(@data, @sort); -#my @sorted = @data; - -print "-- sort: ",Dumper(@sort)," (data: ".@data." sorted: ".@sorted.") --\n",br if ($debug); +my @sorted = sorted_array( @$data, @sort_rules ); -print start_table({-border=>1,-cellspacing=>0,-cellpadding=>2,-width=>'100%'}); +print "-- sort: ",Dumper(@sort_rules)," (data: ".@$data." sorted: ".@sorted.") --\n
-- dayMask: $dayMask --\n
-- cache_key: $cache_key --\n
" if ($debug); -print Tr( - th("group/service"), - th({-bgcolor=>'#f0f0f0'}, - &sort_link($q,'from_time','u').' from '. - &sort_link($q,'from_time','d'), - br,$from_html - ), - th( - &sort_link($q,'to_time','u').' to '. - &sort_link($q,'to_time','d'), - br,$to_html - ), - th({-bgcolor=>'#e0e0e0'}, - &sort_link($q,'dur_time','u').' duration '. - &sort_link($q,'dur_time','d') - ), - th("description") - ) if (scalar @sorted > 0); +print ' + + + '; + +my $downtime; # total downtime +my $downinterval; # total downtime in time interval +my $sg_count; # count number of downtimes foreach my $row (@sorted) { - next if ($q->param('use_date_limit') && ($row->{from_time} < $from_time || $row->{to_time} > $to_time)); - print Tr( - td({-align=>'left',-valign=>'center'},$row->{sg}), - td({-align=>'right',-bgcolor=>'#f0f0f0'},$row->{from}), - td({-align=>'right'},$row->{to}), - td({-align=>'center',-bgcolor=>'#e0e0e0'},$row->{dur}), - td({-align=>'left'},$row->{desc}), - ),"\n"; + next if ($q->param('use_date_limit') && ($row->{from} < $from_time || $row->{to} > $to_time)); + my ($from,$dur,$int) = ('unknown','unknown','unknown'); + + if ($row->{from} != -1 ) { + $from = d($row->{from}); + $dur = $row->{to} - $row->{from}; + $downtime->{$row->{sg}} += $dur; + if ($q->param('use_time_limit')) { + $int = $working_days->interval($row->{from},$row->{to}); + $dur = dur($int)."
∑ ".dur($dur).""; + $downinterval->{$row->{sg}} += $int; + } else { + $dur = dur($dur); + } + } + $sg_count->{$row->{sg}}++; + + print ' + + + + + + '; } # dump totals # -foreach my $sg (keys %downtime) { -print Tr(td({-colspan=>3,-align=>'right'},"total for $sg:"), - td({-bgcolor=>'#e0e0e0',-align=>'right'},dur($downtime{$sg})), - td(small("in ".$sg_count{$sg}." failures"))),"\n"; +my $dur; +foreach my $sg (keys %$downtime) { + if ($downinterval->{$sg}) { + $dur=dur($downinterval->{$sg})."
∑ ".dur($downtime->{$sg}).""; + } else { + $dur=dur($downtime->{$sg}); + } + print ' + + + '; } -print end_table, - end_form; +print "
group/service', + &sort_link($q,'from','u'),' from ',&sort_link($q,'from','d'),''; +print '
',$from_html if ($from_html); +print '
', + &sort_link($q,'to','u'),' to ',&sort_link($q,'to','d'),''; +print '
',$to_html if ($to_html); +print '
', + &sort_link($q,'dur','u'),' duration ',&sort_link($q,'dur','d'),' + description
',$row->{sg},'',$from,'',d($row->{to}),'',$dur,'',$row->{desc},'
total for ',$sg,'',$dur,'in ',$sg_count->{$sg},' failures
", + $q->end_html; +