#!/usr/pkg/bin/perl
#
## $Header: /home/vikas/src/nocol/utility/RCS/logstats,v 2.1 1999/10/26 03:42:30 vikas Exp $
#
# NOCOL log file reporter
#
# Typical usage:
#   logstats.pl  -error  -ignore=10 -file ../logs/warning -v Thruput my-gw
#
# Command line args:
#	-warn	or -crit or -err, only consider events worse than this level
#	-ignore=10	ignore downtimes of less than 10 seconds
#	-f <logfile>	input logfile (else stdin)
#	-v <variable>	only for variable regex listed
#	-r		run report for past 1 week time period only
#	<site> <site> <site>  sitenames to extract report for.
#
# Copyright Netplex Technologies Inc. 1997  info@netplex-tech.com
#
$debug = 0;

while ($_ = $ARGV[0], /^-/) {
    shift;
    last if /^--$/;
    /^-ignore=(\d+)/i && do { $mindowntime = $1 ; next; };
    /^-([cewi])/i && do { $maxlevel = $1; next; };
    /^-f/ && do {$logfile = $ARGV[0]; shift; next; };
    /^-v/ && do {$var_regex = $ARGV[0]; shift; next; };
    /^-d/ && do {++$debug ; next; };
    /^-r/ && do {$runtime = time;
		 $etime = localtime($runtime);
		 $stime = localtime($runtime - 604800); };
    /^-/ && (print "Ignoring unknown option $ARGV[0]\n");
}
# we are given a list of sites that user is interested in
if ($#ARGV > -1) {
    @sites = @ARGV;
    $site_regex = $ARGV[0]; shift;
    while ($ARGV[0]) {$site_regex .= "|$ARGV[0]"; shift; }
    ($debug > 0) && (print STDERR "+debug SITES are @sites, ($site_regex)\n") ;
}
##
## Init variables
# To convert the string severities into numbers...Match only 4 chars
$info = 4; $warn = 3; $err = 2; $crit = 1;
%intlevel = ('I', $info, 'W', $warn, 'E', $err, 'C', $crit);
@intlevel = ("", "Critical", "Error", "Warning", "Info" );
# offset in days of months in the year.
%month_offset = (
    'Jan', 0,    'Feb', 31,   'Mar', 59,  'Apr', 90,
    'May', 120,  'Jun', 151,  'Jul', 181, 'Aug', 212,
    'Sep', 243,  'Oct', 273,  'Nov', 304, 'Dec', 334
		 );
$linenum = 0; $badlines =0;
##
##  END initializing all statics

$maxlevel =~ tr /a-z/A-Z/d;			# all to uppercase
$maxlevel = "CRITICAL" unless $maxlevel;	#default value
$imaxlevel = $intlevel{substr($maxlevel, 0, 1)};	#integer value
if ($imaxlevel >= $info || $imaxlevel < $crit) {
    print "Invalid user level, resetting to CRIT\n";
    $imaxlevel = $crit;
}
$maxlevel = $intlevel[$imaxlevel];

if (defined $logfile && $logfile) {
    open (LOGFILE, "<$logfile") || die("Couldnt open $logfile, $|");
}
else {open (LOGFILE, "cat |"); }	# open stdin

while (<LOGFILE>) {
    ++$linenum;
    if ( /^(\w+\s+\w+\s+\d+\s+\d+:\d+:\d+\s+\d+)\s+\[([^]]+)\]:\s+SITE\s+(\S+)\s+(?:(\S+)\s+)?VAR\s+(\S+)\s+(\d+).*LEVEL\s+(\S+)\s+LOGLEVEL\s+(\S+)\s+NOCOP\s+(\S+)/ ) {
    $e_date = $1; $e_monitor = $2; $e_site = $3; $e_addr = $4; $e_varname = $5;
    $e_varval = $6; $e_level = $7; $e_loglevel = $8; $e_nocop = $9;

    # if any sites give, do only the listed sites.
    if ($site_regex) { next unless $e_site =~ /$site_regex/oi; }
    if ($var_regex)  { next unless $e_varname =~ /$var_regex/oi; }

    $index = "$e_site:$e_monitor:$e_varname";
    # if ($debug > 5) { print "+(debug) index=$index\n" };
    $ilevel = $intlevel{substr($e_level, 0, 1)};
    if ($ilevel < $crit || $ilevel > $info) {  # invalid level
	print "+$linenum Illegal Level ($e_level=$ilevel): $_";
	++$badlines;
	next ;
    }
    ++$oklines;
    if (!defined($stime)) { $stime = $e_date; }  # starting date of all LOGS

    if ($ilevel <= $imaxlevel) {  # user desired worst level, i.e. is DOWN
	# if already down entry, leave alone, dont update. Else...
	if (!defined($DOWN{$index}) || $DOWN{$index} == 0) {
	    $DOWN{$index} = &cvt_time($e_date);	# store timestamp
	    ++$FLAPS{$index};	# how many times did this site go down
	    $TOTVARVALUE{$index} += $e_varval ;	# to calculate average
	    if ($debug > 1) { print "+(debug) DOWN{$index} = $DOWN{$index}\n";}
	}
    }
    else {	# site is up
	if ($DOWN{$index} != 0) {	# we have a previous down entry
	    $delta = &cvt_time($e_date) - $DOWN{$index};
	    if ($debug > 1) { print "+(debug) $index UP after $delta secs\n";}
	    if (defined $mindowntime && $delta <= $mindowntime) {
		$DOWN{$index} = 0;	#reset
		# --$FLAPS;		# decrement ?? or let it be...
		next;
	    }
	    $TOTALDOWN{$index} +=  $delta;
	    $MAXDOWN{$index} = &max($delta, $MAXDOWN{$index});
	    $TOTVARDOWN{$e_varname} += $delta;	#track stats per variable
	    $DOWN{$index} = 0;		# reset this variable
	}
	else { ++$SPAREUPS{$index} ; }	# we got an UP when site was not down
    }

    next;
  }	# end if(//)
  ++$badlines;
}	# end while(<LOGFILE>)
close(LOGFILE);

## save the last time in the log file
if (!defined($etime)) { $etime = $e_date; } # end date for all LOGS
$tot_delta = &cvt_time($etime); $tot_delta -=  &cvt_time($stime);
if ($tot_delta == 0) {$tot_delta = 1; }

##### Start REPORT: ####
## Header
printf ("\n%50s\n%50s\n", "NOCOL SUMMARY REPORT", "====================");
printf ("%75s\n%75s\n", "From: $stime", "To: $etime");
print "Severity level: $maxlevel\n";
print "Total Lines processed = $oklines, Unparseable= $badlines\n";
print "All times in d+hh:mm\n";
if ($#sites > -1) {print "Report for sites:  @sites\n"; }
if ($var_regex) {print "Filter on variable name: $var_regex\n"; }
if ($mindowntime) {print "Skipping downtimes less than $mindowntime secs\n"; }

## Per site report
print "\nPer Site Reports\n================\n\n";
format STDOUT_TOP =
    Site         Variable       +--------- Downtime ---------+   Total    Avg.
                                Total     Max      Avg.    %age  Downs   Value
------------------------------------------------------------------------------
.
format STDOUT =
@<<<<<<<<<<<<<<  @<<<<<<<<<<<  @>>>>>>  @>>>>>>  @>>>>>>  @>>>  @>>>>>  @>>>>>
$sitE,           $vaR,     &secs2hrs($TOTALDOWN{$s}), &secs2hrs($MAXDOWN{$s}), $avG, $percenT, $FLAPS{$s},  int($TOTVARVALUE{$s}/$FLAPS{$s})
.

for $s (sort(keys %DOWN))
{
    next if (defined $mindowntime && $TOTALDOWN{$s} <= $mindowntime);
    local ($sitE, $monitoR, $vaR) = split(':', $s);
    $NETDOWN += $TOTALDOWN{$s};
    ++$nsites;
    if ($FLAPS{$s} == 0) { $FLAPS{$s} = 1; }	# avoid divide by zero
    local ($avG, $percenT) = (&secs2hrs($TOTALDOWN{$s}/$FLAPS{$s}),
			      ($TOTALDOWN{$s}*100)/$tot_delta ) ;
    write;	# use the format above.
}
if ($nsites > 0) {
    printf ("\n\nTotal downtime (all sites)=%s\nAverage downtime= %s\n",
	    &secs2hrs($NETDOWN), &secs2hrs($NETDOWN/$nsites));
}

## Reports  based on variables
print "\nPer Variable Report\n===================\n\n";
print "      Variable      TotalDown\n    --------------------------\n";
local ($ncount) = 0;
for $s (sort {$TOTVARDOWN{$b} <=> $TOTVARDOWN{$a}} (keys %TOTVARDOWN))
{
    last if (++$ncount == 5);		# only top 5
    printf "%18s   %s\n", $s, &secs2hrs($TOTVARDOWN{$s});
}

## 10 Worst sites
print "\n10 Worst Site Events\n====================\n\n";
printf "%15s  %10s %s\n",  "Site     ", "Variable  ", "TotalDown";
print "    ------------------------------------\n";
local ($ncount)= 0;
for $s (sort {$TOTALDOWN{$b} <=> $TOTALDOWN{$a}} (keys %TOTALDOWN))
{
    last if (++$ncount == 10);		# only top 10
    printf "%15s %0.0s %-10s %s\n", split(/:/, $s), &secs2hrs($TOTALDOWN{$s});
}


exit 0;

##
## Sub-routines
##
# Convert a date string 'Wed Mar 18 12:48:19 1997'  into secs since 1970
sub cvt_time {
    local($datestr) = @_;
    local($timestamp) = 0;
    if ($datestr =~ /^\S+\s+(\w+)\s+(\d+)\s+(\d\d):(\d\d):(\d\d)\s+(\d\d\d\d)/)
    {
	$timestamp = (($6 - 1970) * 365) + $month_offset{$1} + $2;
	$timestamp *= (24 * 3600);
	$timestamp += ($3 * 3600) + ($4 * 60) + $5;
    }
    return ($timestamp);
}

sub max {
    local ($a, $b) = @_;
    if ($a > $b) { return $a;}
    return $b;
}

sub secs2hrs {
    local ($secs) = @_;
    local ($d, $h, $m, $s);
    $d = int($secs / (24*3600));  $secs %= (24 * 3600);
    $h = int($secs / 3600);	$secs %= 3600;
    $m = int($secs / 60);	$secs %= 60;
    $s = $secs ;

    if ($d > 0) { return sprintf("%d+%2.2d:%2.2d", $d, $h, $m) ;}
    else { return sprintf("%2.2d:%2.2d", $h, $m); }
}
