#!/bin/sh
#
# $Header: /home/vikas/netmgt/nocol/src/utility/RCS/logstats,v 1.3 1994/05/18 10:09:57 vikas Exp $
#
#	@(#)logstats.sh 1.2 02/13/94 rjk
#
###
#
# Shell & awk script to digest NOCOL log files and generate a report.
#
# Given a severity level (warning, err, critical), this program generates
# a report as to the number of times that a site (variable) went into that
# severity level, and the time that it was in that level (or levels higher).
# If your desired level is warning, then make sure that your input log file
# was also written at or above the 'warning' level (warn or info). By the
# same token, if your desired level is 'critical', then specifying a log
# file written at the 'info' level will just take longer to process.
#
# Usage: logstats [-wec] logfile
#
# Written by: Bob Kupiec (kupiec@jvnc.net)
# Modifications:  Vikas Aggarwal (vikas@navya.com)
#
# Revisions:
# 12/20/93   1.0  kupiec	initial version
# 02/05/94   1.1  kupiec	cleaned up code, allow for sites down longer
#				than 1 day
# 02/13/94   1.2  kupiec	added debugging, fixed a sort bug
# 05/10/94   1.3  vikas		rewrite to handle variable names and track
#				severity changes (not just up/down).
#
# Caveats:  This program does not take account for leap years,
#           daylight savings time, or sites down more than two years :-)
#
###
TMPFILE=/tmp/logstats.tmp.$$
USAGE="Usage: $0 [-wec] logfile"

trap "echo Exiting on signal; rm -f $TMPFILE; exit 1"  1 2 3 9 15
case $# in
	1)	level="-c" ;;
	2)	level=$1 ; shift ;;
	*)	echo $USAGE ;
		exit 1 ;;
esac

file=$1 ;

case $level in
	-w)	level="Warning" ;;
	-e)	level="Error" ;;
	-c)	level="Critical" ;;
	*)	echo $USAGE ;
		exit 0 ;;
esac

if [ ! -f "$file" ]
then
    echo "File $file not found."
    exit 0
fi

# The following assumes the log is in chronological order
echo "From: `head -1 $file | cut -c 1-24`"
echo "To:   `tail -1 $file | cut -c 1-24`"
echo

echo "Severity level: $level"

# Control variables (used mainly for debugging)
#
# extended: set to 1 if you want to see extended data output
# debug: set from level 1 (min) to level 9 (max), 0 is off

# Sort by site, sender, variable, timestamp. The first line is the desired
# reporting level.
(echo $level; cat $file) | sort +7 -8 +5 -6 +10 -11 +4 -5 +1 -2M +2 -3n| awk '\
BEGIN	{
	extended = 1 ; debug = 0
	FS = " " ; gotdown = 0
	LEVEL = 1 ;	# default level if none given is Critical

	# To convert the string severities into numbers...Match only 4
	# characters (see substr() for level below)
	intlevel["Info"] = 4;	# Info
	intlevel["Warn"] = 3;	# Warning
	intlevel["Erro"] = 2;	# Error
	intlevel["Crit"] = 1;	# Critical

	# index to number of days in the months of the year
	offset["Jan"] = 0;
	offset["Feb"] = 31;
	offset["Mar"] = 59;
	offset["Apr"] = 90;
	offset["May"] = 120;
	offset["Jun"] = 151;
	offset["Jul"] = 181;
	offset["Aug"] = 212;
	offset["Sep"] = 243;
	offset["Oct"] = 273;
	offset["Nov"] = 304;
	offset["Dec"] = 334;

	if (debug > 0)
	printf "+Debugging on.\n+Flags are: extended=%d debug=%d\n",\
	    extended, debug

}	# end: BEGIN

{
	processed++				# lines processed

	# first line is special, and has desired level to process at,,,
	if (processed == 1) {
	    if (debug)
		printf "+Level line= %s\n", $0

	    cmdlevel = $1
	    LEVEL = intlevel[substr(cmdlevel, 1, 1)]
	    if (LEVEL < 1 || LEVEL > 3) LEVEL = 1   # no Info level
	    if (debug) printf "+Monitor level is %d\n", LEVEL
	    next
	}

        site = $8				# parse input strings
        variable = $11
	value = $12
	severity = $16
	years = $5
	months = $2
	days = $3

	hours = substr ($4, 1, length($4) - 6)	# parse times
	mins  = substr ($4, 4, length($4) - 6)
	secs  = substr ($4, 7, length($4) - 2)

	idx = sprintf ("%s\t%s", site, variable)

	if (tidx != idx && gotdown) {	# new site+var combination
	    gotdown = 0		# reset.
	    noup[tidx]++	# per site counter
	    badup++		# total lines counter
	    if (debug)
		printf "+New site index- %s, resetting gotdown\n", idx
	}

	level = intlevel[substr(severity, 1, 4)]	# match atleast 4 char

	if (level == 0) {	# bad severity word in the log line
	    if ($14 == "LEVEL") {	# probably a field with spaces or blank
		spaced++		# only in versions before 3.0
		if (debug > 3)
		    printf "+Found pre-v3.0 line, ignored...\n"
	    }
	    else {
		unknown++			# unknown/unparseable line
		if (debug > 0)
		    printf "+WARNING, DANGER WILL ROBINSON!  I seem to have found and unparsable line!\n"
	    }

	    next
	}

	if (debug > 1 && count[idx] == 1)
	    printf "+Index= %s\n", idx

	if (debug > 1)
	    printf "+%-15s %-12s %6s %3.2d:%2.2d:%2.2d %2.2d/%s/%d\n", \
	    site, variable, severity, hours, mins, secs, days, months, years

	if (level <= LEVEL) {
	    stateup = 0;	# at or more severe than desired level (down)
	}
	else {
	    stateup = 1;	# site as good as up.
	}

	## Ignore UP lines that are consecutive or didnt have a down
	#  can happen as a site is stepping down through the severities.
	if (stateup && gotdown == 0) {
	    if (debug > 5)
		printf "+Found extraneous up line, ignoring...\n"
	    next
	}
	    

	## We do this if we find a down line. Note that an event might
	# escalate in severity so it is perfectly all right to get two
	# 'downs' in a row.
	if (stateup == 0) {

	    if (debug > 5)
		printf "+Found a down line\n"

	    if (gotdown == 0) {		# changing state to down from up
		tyears = years
		tmonths = months
		tdays = days
		thours = hours		# save to tmp var
		tmins = mins
		tsecs = secs

		gotdown = 1

		numdown[idx]++		# number of downs found
	    }

	    totalval[idx] += value	# add all 'values' for mean at end
	    valcount[idx]++
	    sitelist[idx]++		# keeps track of associative array keys
	}

	## We do this if find an up & we have already seen a down line
	else if (stateup && gotdown) {

	    if (debug > 5)
		printf "+Found a up line, processing...\n"

					# convert to seconds
	    tmdown  = offset[tmonths] * 24 * 60 * 60 \
		    + tdays * 24 * 60 * 60 \
		    + thours * 60 * 60 \
		    + tmins  * 60 \
		    + tsecs

	    tmup    = offset[months] * 24 * 60 * 60 \
		    + days * 24 * 60 * 60 \
	    	    + hours  * 60 * 60 \
	            + mins   * 60 \
	            + secs

	    if (tyears < years) {		# time spans over 1 year
		tmup += 365 * 24 * 60 * 60	# add 1 year
	    }

	    tmdelt = tmup - tmdown		# find time down

	    if (debug > 1) {
		if (tmdelt < 0 || tmup < 0 || tmdown < 0)
		    printf "+ERROR: negative time value\n"
		printf "+%s up:%d dn:%d delta:%d", site, tmup, tmdown, tmdelt
	    }


	    if (tmgreatest[idx] < tmdelt) {	# see if its the largest
		tmgreatest[idx] = tmdelt	#     time down
	    }

	    tmtot[idx] += tmdelt		# add to total time

	    if (debug > 1)
		printf " total:%d greatest:%d\n", tmtot[idx], \
		tmgreatest[idx]

	    gotdown = 0				# clear flag

	    # This counter keeps of tally of how many times
	    # the site actually flapped (down->up)
	    numup[idx]++			# increase counter.

	}

	tidx = idx			# set temp index variable
}

##
END     {

	if (debug)
	    printf "+Now doing END processing\n"

	# now convert time in seconds back to hh:mm:ss for all site-variable
	for (idx in sitelist) {
	    if (debug)
		printf "+[%s]: numup=%d, numdown=%d, noup=%d\n",\
			x, numup[idx], numdown[idx], noup[idx]

	    if (tmtot[idx] > 0) {
		hgr[idx] = int (tmgreatest[idx] / (60 * 60)) # longest time
		tmgreatest[idx] -= hgr[idx] * (60 * 60)
		mgr[idx] = int (tmgreatest[idx] / 60)
		tmgreatest[idx] -= mgr[idx] * 60

		sgr[idx] = int (tmgreatest[idx])

		tmmean[idx] = int(tmtot[idx] / numup[idx])  # find mean time

		hme[idx] = int (tmmean[idx] / (60 * 60))    # convert mean time
		tmmean[idx] -= hme[idx] * (60 * 60)
		mme[idx] = int (tmmean[idx] / 60)
		tmmean[idx] -= mme[idx] * 60
		sme[idx] = int (tmmean[idx])

		h[idx] = int (tmtot[idx] / (60 * 60))	# convert total time
		tmtot[idx] -= h[idx] * (60 * 60)
		m[idx] = int (tmtot[idx] / 60)
		tmtot[idx] -= m[idx] * 60
		s[idx] = int (tmtot[idx])
	    }
	}

	# print out summary report

	printf "\nProcess summary, number of records:\n\n"
	printf "\tMisaligned  : %d\n", spaced
	printf "\tUnparseable : %d\n", unknown
	printf "\tMissing Up  : %d\n", badup
	printf "\tTotal Lines : %d\n", processed

	if (extended) {
	    printf "\nNOTE:\n\t-# tms down- indicates number of state flaps\n"
	    printf "\t-# dn vals-  is number of down samples in calc of avg value\n\n"
	}

	# print long header, if displaying all ignored lines also
	if ( extended == 1) {
	    printf "%-15s %-12s   %-4s   %-7s%-4s  %-9s %-9s %-9s%-3s\n", \
	    	    ""   , ""   , "#",   "Avg","#", "Total", "Longest", "Mean", "#"

	    printf "%-15s %-12s   %-4s   %-7s%-4s  %-9s %-9s %-9s%-3s\n", \
	    	     ""  , ""  , "tms", "Down", "dn", "Time", "Time", "Time", "ukn"

	    printf "%-15s %-12s   %-4s   %-7s%-4s  %-9s %-9s %-9s%-3s\n", \
	     "Site", "Variable", "dn", "Value", "val", cmdlevel, cmdlevel, cmdlevel, "dn"
	printf "------------------------------------------------------------------------------------\n"
	}
	# print short header
	else {
	    printf "%-15s %-12s  %-4s    %-8s %-9s %-9s %-9s\n", \
	    ""    , ""        , "#",   "Avg",  "Total", "Longest", "Mean"

	    printf "%-15s %-12s  %-4s    %-8s %-9s %-9s %-9s\n", \
	    ""    , ""        , "tms", "Down",  "Time",  "Time",    "Time"

	    printf "%-15s %-12s  %-4s    %-8s %-9s %-9s %-9s\n", \
	    "Site", "Variable", "dn",  "Value", cmdlevel, cmdlevel, cmdlevel
	printf "----------------------------------------------------------------------------\n"
	}

        for (idx in sitelist) {
	    split(idx, sitevar, "\t")

	    # print valid data
	    if (numup[idx] > 0 && extended != 1) {	# short header
		printf "%-15s %-12s %4d  %8d   %3.2d:%2.2d:%2.2d %3.2d:%2.2d:%2.2d %3.2d:%2.2d:%2.2d\n", \
		sitevar[1], sitevar[2], \
		numup[idx],  int (totalval[idx] / valcount[idx]), \
		h[idx], m[idx], s[idx], \
		hgr[idx], mgr[idx], sgr[idx], \
		hme[idx], mme[idx], sme[idx]
	   }

	    # print valid data w/ ignored records
	    else if ( numup[idx] > 0 && extended == 1) {
	    printf "%-15s %-12s %4d  %8d %4d  %3.2d:%2.2d:%2.2d %3.2d:%2.2d:%2.2d %3.2d:%2.2d:%2.2d %3d\n", \
		sitevar[1], sitevar[2], \
		numup[idx], \
		int (totalval[idx] / valcount[idx]), valcount[idx],\
		h[idx], m[idx], s[idx], \
		hgr[idx], mgr[idx], sgr[idx], \
		hme[idx], mme[idx], sme[idx], \
		noup[idx]
	   }

	    # print records that just had a single down, no up.
	    else if (numup[idx] == 0 && extended == 1) {
	    printf "%-15s %-12s %4d  %8s %4s   --:--:--  --:--:--  --:--:-- %3d\n", \
	       sitevar[1], sitevar[2], \
	       numup[idx], "-", "-", \
	       noup[idx]
	    }
	}


}'  > $TMPFILE

sed -n '1,/^------------/p' $TMPFILE		# print all except sites
sed    '1,/^------------/d' $TMPFILE | sort +0 -1	# sort the sites

rm -f $TMPFILE
exit 0

