#!/usr/bin/perl -sw
$versionVjlog = "1.08";
# Created by Steve Voisey (srv) 01 Feb 2011.
# email: [steve.voisey@ericsson.com]
#
# name: vjlog.pl

# [n2bb.log] format of message headers:
# field 0    field 1      field 2   field 3   field 4    field5  field 6   field 7
# date       gmt          localDate localTime gmtOffset  level   component text
# 2007/06/20 22:00:59.164 GMT(06/21 00:00:59  +0200)     DEBUG   SRM       StatMonitor.gatherStats(): Method Entered
# 2009/06/18 02:20:36.019                                                    DEBUG  SRM        SClient.getString(): Successfully retrieved

# jboss format of message headers:
# Fields
#00 01  02   03           04    05    06
#01 Feb 2011 06:42:21,702  INFO [CMS] [pool-45-thread-1] SANManager.updateDriveProperties() - [Storage] Drive Content is 35.86328299089116% used, path /content
#01 Feb 2011 07:12:21,881  INFO [CMS] [pool-45-thread-1] SANManager.updateDriveProperties() - [Storage] Drive C
#01 Feb 2011 06:12:22,362 ERROR [CMS] [main] Agent.start() - Unable to start SNMP monitoring
#java.net.ConnectException: Connection refused
#                          FLEVEL FCOMP FSUBCOMP           FMETHOD    FTEXT
#01 Feb 2011 06:42:21,702  INFO   [CMS] [pool-45-thread-1] method() - text message.
use Time::Local;
use File::Basename;

use constant FJBOSSDAY       => 0;
use constant FJBOSSMONTH     => 1;
use constant FJBOSSYEAR      => 2;
use constant FJBOSSTIME      => 3;
use constant FJBOSSLEVEL     => 4;
use constant FJBOSSCOMP      => 5;
use constant FJBOSSSUBCOMP   => 6;
use constant FJBOSSMETHOD    => 7;
use constant FJBOSSTEXT      => 8;
use constant FJBOSSNUMBER    => 9;

use constant FDATE      => 0;
use constant FTIME      => 1;
use constant FLEVEL     => 2;
use constant FCOMP      => 3;
use constant FSUBCOMP   => 4;
use constant FMETHOD    => 5;
use constant FTEXT      => 6;
use constant FNUMBER    => 7;

if (defined($help)) { displayHelpText(); exit; }

# foreach (@ARGV) { print "Arg $_\n"; } # Come back to this later.

$validLevels  = "INFO|DEBUG|WARN|ERROR|FATAL";
#$validComponents = "ASSET|SRM|SESSIONGW|OPENAPP|CUSTOMER|TERMINAL";


unless (defined($file))      { $file = "/opt/tandbergtv/cms/log/jboss.log"; }
unless (defined($dir))       {  $dir = "/opt/tandbergtv/cms/log"; }

# If just a start time is defined, default to todays date.
if (defined($time)) {
    unless (defined($date))      { $date = "today"; }
}
# Otherwise default time will be start of the day.
unless (defined($time))      { $time = 0; }
# Deafault start date will be way back, so all entries are displayed by default.
unless (defined($date))      { $date = "1900/01/01"; }
# Deafault end date will be end of the current day, so all entries are displayed by default.
unless (defined($etime))     { $etime = "23:59"; }
unless (defined($edate))     { $edate = "today"; }
unless (defined($level))     { $level = "ALL"; }
unless (defined($component)) { $component = "ALL"; }
unless (defined($sub))       { $sub = "ALL"; }
unless (defined($method))    { $method = "ALL"; }
unless (defined($ignore))    { $ignore = "NONE"; }
unless (defined($inc))       { $inc = "NONE"; }
unless (defined($incs))      { $incs = "NONE"; }
unless (defined($skip))      { $skip = 0; }
unless (defined($unique))    { $unique = 0; }
unless (defined($ulen))      { $ulen = 0; }
unless (defined($help))      { $help = 0; }
unless (defined($sum))       { $sum = 0; }

$ignoreErr[0] = "log4j:ERROR \[BaseClassLoader";
$ignoreErr[1] = "log4j:ERROR \"org\.jboss\.logging\.appender\.FileAppender";
$ignoreErr[2] = "MODApplicationdbDriveri\.DBWritePlay.*Caught exception.* ORA-14400: inserted partition key does not map to any partition";

if ($ignore eq "std") {
        $ignore = join('|', @ignoreErr);
    print "IGNORE: $ignore\n";
}

my $loopCount  = 0;
my $match      = 0;
my $noMatch    = 0;
my $included   = 0;
my $ignoreMatch = 0;
my $displayed  = 0;
my $entries    = 0;
my $matchEntries    = 0;
my $matchErrorCount = 0;
my $errorCount = 0;
my $matchWarnCount = 0;
my $warnCount = 0;
my $line       = "";
my $msgHeader = 0;
my $startSeconds    = 0;
my $endSeconds    = 0;
my @message = ();
my @logFileList = ();
my @tmpLogFileList = ();
my $divide = "=" x 80 ."\n";
my $tmp    = "";
my @tmp = ();

my %historyFTEXT;
my $fileHandle = "FxILE";
if ($file =~ /argv/i) { $fileHandle = "ARGV"; }

#Why did I do this?
#if ($fileDir =~ /ld/i) { $fileDir = "/opt/tandbergtv/cms/log/"; $file = $fileDir . $fileName . $fileExt; }

# Allow for input date like '21/feb/2011' or '21 feb 2011' as jlog uses this order and 
# three letters for months.

$date  = convertDateFormat($date); 
if ( $date eq "0" ) { print "To see help options use: ./vjlog.pl -help\n"; exit; }

$edate = convertDateFormat($edate);
if ( $edate eq "0" ) { print "To see help options use: ./vjlog.pl -help\n"; exit; }
    
# Just incase the user has entered the time incorrectly, and its a quick fix.
# Convert from (jboss format) time [HH:MM:SS,hhh] to [HH:MM:SS.hhh]
$time  = convertTimeFormat($time);
$etime = convertTimeFormat($etime);

unless (validateInputParams ($file, $date, $time, $etime, $level, $component, $ignore, $inc, $incs, $skip,
        $validLevels)) {
    print "To see help options use: ./vjlog.pl -help\n";
    exit 1;
}

$startSeconds = convertToSeconds( $date, $time );
$endSeconds   = convertToSeconds( $edate, $etime );



# Debug aid, create a log file of all filtered lines that will NOT be displayed.
if ($skip) { unless (open (SKIP, ">skip.log")) { die "cannot open skip.log for writing: $!"; } }

#unless (($file =~ /stdin/i ) || ($file =~ /argv/i )) {
#    if ($fileExt =~ /\.gz/i ) {
#        unless (open ($fileHandle, "gunzip -c $file |")) { die "cannot open $file for writing: $!"; }
#    } else {
#        unless (open ($fileHandle, "<$file")) { die "cannot open $file for writing: $!"; }
#    }
#}

# Load the file into an array at start, or process each line?
# Using the file handle will allow processing piped output from tail -f

if ( $file eq "stdin" ){
    foreach $line (<>) {
        $loopCount++;
        $msgHeader = checkMessageHeader($line);
        if ($msgHeader) { $entries++; }
            # srv - 05sep12
            # If it's the start of the log, and we start mid way through an entry,
            # ignore until we reach the first header.
            unless($msgHeader) {
                if($entries == 0) { next; }
            }
        if ((@message > 0) && ($msgHeader)) {
            processMessage(\@message);
            @message = ();
            push (@message, $line);
            next;
        }
        push (@message, $line);
        next;
    }
    # Dropped out, so must have finished file, so one last time!
    processMessage(\@message);
}

unless ( $file eq "stdin" ){
    @tmpLogFileList = split(/,/ , $file);
    foreach $entry (@tmpLogFileList) { 
        if( $entry =~ /\*/ ) { 
            # How to glob properly!  Messes up with <$entry>
            @tmp = glob($entry);
            @logFileList = (@logFileList, @tmp);
            @tmp = ();            
        } else {
            push( @logFileList, $entry );
        }
    }
    ### Not sure why this is here??? srv 05sep12 - @logFileList
}

unless ( $file eq "stdin" ){
    foreach $entry (@logFileList) {  
        unless( $entry =~ /^\// ) { $entry = $dir . "/" . $entry; }
        ($tmp, $tmp, $fileExt) = fileparse($entry, qr/\.[^.]*/);
        if ($fileExt =~ /\.gz/i ) {
            unless (open ($fileHandle, "gunzip -c $entry |")) { die "cannot open $entry $!"; }
        } else {
            unless (open ($fileHandle, "<$entry")) { die "cannot open $entry $!"; }
        }   
        foreach $line (<$fileHandle>) {
            $loopCount++;
            $msgHeader = checkMessageHeader($line);
            if ($msgHeader) { $entries++; }
            # srv - 05sep12
            # If it's the start of the log, and we start mid way through an entry,
            # ignore until we reach the first header.
            unless($msgHeader) {
                if($entries == 0) { next; }
            }
            if ((@message > 0) && ($msgHeader)) {
                processMessage(\@message);
                @message = ();
                push (@message, $line);
                next;
            }
            push (@message, $line);
            next;
        }
        # Dropped out, so must have finished file, so one last time!
        close $fileHandle;
        processMessage(\@message);
    }   
}

#@fileArray = <$fileHandle>;
#close $fileHandle;
#foreach $line (@fileArray) {

if ($skip) { close SKIP; }

# Just to check everything looks ok display some stats.
# If $noMatch + $displayed do NOT match the total number of lines, we have messed up somewhere!

if ($unique) {
    my $totalUniqueEntries = 0;
    my $countUniqueEntries = 0;
    print "\n$divide\t\tSummary unique entries [$level]\n$divide";
    foreach $key (sort(keys(%historyFTEXT))) {
        print "$historyFTEXT{$key}\t$key\n";
        $totalUniqueEntries = $totalUniqueEntries + $historyFTEXT{$key};
        $countUniqueEntries++;
    }
    print "$divide\n Unique|Total [$countUniqueEntries|$totalUniqueEntries]\n";
}

print "\n files processed:\n\n";
foreach $entry (@logFileList) {
    print "\t$entry\n";
}

print "\n lines [$loopCount] entries [$entries] error match|total [$matchErrorCount|$errorCount] " .
      "warn match|total [$matchWarnCount|$warnCount]\n" .
      "\n match|unmatch|total [$matchEntries|$noMatch|" .($matchEntries + $noMatch) . "] \n";
print "\n";
exit 1;

#======================================================================================
#======================================================================================

########################################################################################################
#
########################################################################################################

sub convertToSeconds {
    my $date = $_[0];
    my $time = $_[1];
    # Perl & unix store date/time in epoch format, thats the number of seconds since
    # the midnight before January 1, 1970.
    # Calculate the value of the start of the current day in seconds offset format.
    # We can ignore all the seconds, minutes, hours etc.
    # ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time());

    # Convert time to seconds.
    # Note: can not currently support 100's of a second, so ignore if present.
    unless ( $time eq "0" ) {
        @tmpTime = split (':', $time);
        if (defined($tmpTime[2])) {
            # @tmpSec = split (".", $tmpTime[2]);
            $timeSeconds = ($tmpTime[0] * 60 * 60) + ($tmpTime[1] * 60) + $tmpTime[2];
        } else { 
            if (defined($tmpTime[1])) {
                $timeSeconds = ($tmpTime[0] * 60 * 60) + ($tmpTime[1] * 60);
            } else { 
                $timeSeconds = ($tmpTime[0] * 60 * 60);
            }
        }
    } else {
        $timeSeconds = 0;
    }

    # Convert date to seconds.


    # Convert the input date parameter into seconds offset format.
    if ($date eq "today") {
        ($tmp,$tmp,$tmp,$tmpmday,$tmpmon,$tmpyear,$tmp,$tmp,$tmp) = localtime(time());
        $dateSeconds = timelocal(0,0,0,$tmpmday,$tmpmon,$tmpyear);
    } else {
        @tmpDate = split ('/', $date, 3);
        $dateSeconds = timelocal(0,0,0,$tmpDate[2],($tmpDate[1]-1),($tmpDate[0]-1900));
   }
   # Finished
   return ( $dateSeconds + $timeSeconds );
}

########################################################################################################
#
########################################################################################################
sub processMessage {
    my $msgArrayRef = $_[0];
    my $element = "";
    my $match = 0;
    my @fields = ();
    
    #@fields = split (/\s+/, @$msgArrayRef[0], FNUMBER);
    @fields = getFields(@$msgArrayRef[0]);

    $match = applyHeaderFilters(@$msgArrayRef[0],
                                    $startSeconds,
                                    $endSeconds,
                                    $level,
                                    $component,
                                    $sub,
                                    $method,
                                    $ignore);
    if ($match) {
        if ($inc eq "NONE") { $match = 1; } else { $match = 0; }
        unless ($inc eq "NONE") {
            foreach $element (@$msgArrayRef) {
                if ($element =~ /$inc/i ){
                    $match = 1;
                    $included++;
                }
            }
        }
    }

    if ($match) {
        if ($incs eq "NONE") { $match = 1; } else { $match = 0; }
        unless ($incs eq "NONE") {
            foreach $element (@$msgArrayRef) {
                if ($element =~ /$inc/ ){
                    $match = 1;
                    $included++;
                }
            }
        }
    }

    if (($unique) && ($match)) {
        $match = checkUnique($fields[FTEXT]);
    }

    if (($match) && ($fields[FLEVEL] =~ /ERROR/)) { $matchErrorCount++; }
    if ($fields[FLEVEL] =~ /ERROR/) { $errorCount++; }
    
    if (($match) && ($fields[FLEVEL] =~ /WARN/)) { $matchWarnCount++; }
    if ($fields[FLEVEL] =~ /WARN/) { $warnCount++; }
    
    if ($match) {
        $matchEntries++;
        if ($sum) {
            print @$msgArrayRef[0];
            #print "TEXT: $fields[FTEXT]\n";
        } else {
            foreach $element (@$msgArrayRef) { print $element; }
        }
    }
    unless ($match) { 
        if ($skip) { print SKIP $line; } 
        $noMatch++;
    }
}
########################################################################################################
#
########################################################################################################
sub getFields {
    my $line = $_[0];
    my @fields = ();
    my @tmp = ();
    my @logTime = ();
    my $other = "";
    my $remain = "";
    my $element = "";

    # jboss format of message headers:
    # Fields
    #00 01  02   03           04   05    06                 07                                   08
    #01 Feb 2011 06:42:21,702 INFO [CMS] [pool-45-thread-1] SANManager.updateDriveProperties() - [Storage] Drive Content is 35.86328299089116% used, path /content
    #
    # FJBOSSDAY       => 0;
    # FJBOSSMONTH     => 1;
    # FJBOSSYEAR      => 2;
    # FJBOSSTIME      => 3;    
    # FJBOSSLEVEL     => 4;
    # FJBOSSCOMP      => 5;
    # FJBOSSSUBCOMP   => 6;
    # FJBOSSMETHOD    => 7;
    # FJBOSSTEXT      => 8;
    # FJBOSSNUMBER    => 9;
    #
    # FDATE      => 0;
    # FTIME      => 1;
    # FLEVEL     => 2;
    # FCOMP      => 3;
    # FSUBCOMP   => 4;
    # FMETHOD    => 5;
    # FTEXT      => 6;
    # FNUMBER    => 7;
    #
    @tmp = split (/\s+/, $line, 6);
    
    # Use date/time in seconds offset format for comparisons.
    # Convert the logfiles current line date into seconds.
    $fields[FDATE] = timelocal(0,0,0,$tmp[FJBOSSDAY],getMonth($tmp[FJBOSSMONTH]),($tmp[FJBOSSYEAR]-1900));
    
    # Convert the logfiles current line time into seconds.
    @logTime = split (':', $tmp[FJBOSSTIME]);
    # Convert from (jboss format) time [HH:MM:SS,hhh] to [HH:MM:SS.hhh]
    $logTime[2]  =~ s/,/\./g;
    
    # some sites have the following format enabled:
    #      06 Aug 2012 15:02:00,206{GMT+0}  INFO [CMS] [event-bus]
    # so we need to strip off the {GMT+0} from the time
    if ( $logTime[2]  =~ /(.*){/ ) {
       ( $logTime[2] ) =  $logTime[2]  =~ /(.*){/ ;
    }
    
    $fields[FTIME] = ($logTime[0] * 60 * 60) + ($logTime[1] * 60) + $logTime[2];
    # Calculate logfiles current date/time as a seconds offset value.
    #$logSeconds = $logDateSec + $logTimeSec;
    
    $fields[FLEVEL] = $tmp[FJBOSSLEVEL];
    
    $remain = join(" ", $tmp[5]);
    #($fields[5], $fields[6], $fields[7], $fields[8]) = $remain =~ /^.*(\[.*\])\s+(\[.*\])(.*\))(.*$)/;
    ($fields[FCOMP], $fields[FSUBCOMP], $fields[FMETHOD], $fields[FTEXT]) = $remain =~ /^.*(\[.*\])\s+(\[.*\])(.*\))\s+-(.*$)/;

    # Not sure if there will always be a method defined, need to check.
    # In meantime, just in case.
    if ( $fields[FTEXT] eq "" ) { $fields[FTEXT] = $fields[FMETHOD]; }
    return @fields;
}
########################################################################################################
#
########################################################################################################
sub checkUnique {
    my $fieldText = $_[0];
    chomp($fieldText);

    # Strip out special characters to make our regular expression easier.
    # Looks like this is not necessary.
    #$fieldText =~ s/\.//g;
    #$fieldText =~ s/\(//g;
    #$fieldText =~ s/\)//g;
    #$fieldText =~ s/\*//g;

    # By default use the full text field, unless ulen has a value.
    unless ( $ulen == 0 ) {
        $fieldText = substr ($fieldText,0,$ulen);
    }
    if (defined($historyFTEXT{$fieldText})) {
        $historyFTEXT{$fieldText}++;
        return 0;
    }
    $historyFTEXT{$fieldText} = 1;
    return 1;
}

########################################################################################################
#
########################################################################################################

sub checkMessageHeader {
    my $line = $_[0];

    my @fields = ();

    @fields = split (/\s+/, $line, FJBOSSNUMBER);
    #@fields = getFields($line);
    # Is this line a valid message header? The text field can run over onto several lines so not
    # every line is a header!
    # Assume its a valid header if it has 6 fields, starts with a valid date field and contains a valid level.
    #
    # Note: Jboss specific check, can not convert fields until we know its a header.
    if ( (@fields >= FJBOSSNUMBER) &&
         ($fields[FJBOSSDAY] =~ /^\d\d/ ) &&
         ($fields[FJBOSSMONTH] =~ /^\w\w\w/ ) &&
         ($fields[FJBOSSYEAR] =~ /^\d\d\d\d/ ) &&
         ($fields[FJBOSSLEVEL] =~ /$validLevels/) &&
         ($fields[FJBOSSCOMP] =~ /CMS/))
         { return 1; }

    return 0;
}
########################################################################################################
#
########################################################################################################
sub applyHeaderFilters {
    my ($line)           = $_[0];
    my ($startSeconds)   = $_[1];
    my ($endSeconds)     = $_[2];
    my ($level)          = $_[3];
    my ($component)      = $_[4];
    my ($sub)            = $_[5];
    my ($method)         = $_[6];
    my ($ignore)         = $_[7];
 
    my (@fields);
    
    @fields = getFields($line);
    
    # Note: Now store logfile date and time as seconds offset values.
    # Is the current logfile date/time after our requested start date/time?
    unless (( $fields[FDATE]  + $fields[FTIME] ) >= $startSeconds) { return 0; }
    # Is the current logfile date/time before our requested end date/time?
    unless (( $fields[FDATE]  + $fields[FTIME] ) < $endSeconds)    { return 0; }

    # Check against level.
    unless ($level eq "ALL") { unless ( $fields[FLEVEL] =~ /$level/ ) { return 0; }}

    # Check against component.
    unless ($component eq "ALL") { unless ( $fields[FCOMP] =~ /$component/ ) { return 0; }}

    # Check against sub component.
    unless ($sub eq "ALL") { unless ( $fields[FSUBCOMP] =~ /$sub/ ) { return 0; }}
    
    # Check against method.
    unless ($method eq "ALL") { unless ( $fields[FMETHOD] =~ /$method/ ) { return 0; }}
    
    # Check against ignore.
    # Not sure if to just check against FTEXT, or include FSUBCOMP and FMETHOD.
    # Will include all three for now, more useful?
    
    #unless ($ignore eq "NONE") { if ( $fields[FTEXT] =~ /$ignore/ ) { return 0; }}
    # Lets include the method field in our ignore.
    #unless ($ignore eq "NONE") { 
    #    if (( $fields[FMETHOD] =~ /$ignore/ ) || ( $fields[FTEXT] =~ /$ignore/ )) { return 0; }}
        
    # Lets include the subcomponent and method field in our ignore.
    unless ($ignore eq "NONE") { 
        if (( $fields[FSUBCOMP] =~ /$ignore/ ) || ( $fields[FMETHOD] =~ /$ignore/ ) || ( $fields[FTEXT] =~ /$ignore/ )) 
            { return 0; }
    }

    
    # If we have made it this far, we have a match!
    return (1);
}

########################################################################################################
#
########################################################################################################

sub validateInputParams {
  #($file, $date, $time, $etime, $local, $level, $component, $ignore, $inc, $incs, $skip );
  my ($file)       = $_[0];
  my ($date)       = $_[1];
  my ($time)       = $_[2];
  my ($etime)      = $_[3];
  my ($level)      = $_[4];
  my ($component)  = $_[5];
  my ($ignore)     = $_[6];
  my ($inc)        = $_[7];
  my ($incs)       = $_[8];
  my ($skip)       = $_[9];
  my ($validLevels)= $_[10];

  unless (($file =~ /stdin/i)) {
     #unless (-r $file) { print "Invalid log file: $file\n"; return 0; }
  }

  unless (($date =~ m|^\d\d\d\d\/\d\d\/\d\d$|) || ($date eq "today")) { 
           print "Invalid date: [$date]\n"; return 0;}
  unless (($edate =~ m|^\d\d\d\d\/\d\d\/\d\d$|) || ($edate eq "today")) { 
           print "Invalid edate: [$edate]\n"; return 0;}

  unless (($time eq "0") || ($time =~ /^\d\d$/) || ($time =~ /^\d\d\:\d\d$/) || ($time =~ /^\d\d\:\d\d\:\d\d$/) || ($time =~ /^\d\d\:\d\d\:\d\d\.\d\d\d$/)) {
           print "Invalid start time: [$time]\n"; return 0;}
  unless (($etime eq "0") || ($etime =~ /^\d\d$/) || ($etime =~ /^\d\d\:\d\d$/) || ($etime =~ /^\d\d\:\d\d\:\d\d$/) || ($etime =~ /^\d\d\:\d\d\:\d\d\.\d\d\d$/)) {
           print "Invalid end time: [$etime]\n"; return 0;}

  #unless (($time eq "0") || ($time =~ /^\d\d$/) || ($time =~ /^\d\d\:\d\d$/) || ($time =~ /^\d\d\:\d\d\:\d\d$/)) {
  #         print "Invalid start time: $time\n"; return 0;}
  #unless (($etime eq "0") || ($etime =~ /^\d\d$/) || ($etime =~ /^\d\d\:\d\d$/) || ($etime =~ /^\d\d\:\d\d\:\d\d$/)) {
  #         print "Invalid end time: $etime\n"; return 0;}
  
  unless (($level eq "ALL") || ($level =~ /$validLevels/)) { print "Invalid level: $level\n"; return 0;}
  # Not sure what full list of valid components is
  unless (($skip == 0) || ($skip == 1)) { print "Invalid skip: $skip\n"; return 0; }

  return 1;

}
########################################################################################################
#
########################################################################################################
sub getMonth {
    my $monthTxt = $_[0];

    $monthTxt = lc($monthTxt);
    my %months = ();

    $months{jan} = 0;
    $months{feb} = 1;
    $months{mar} = 2;
    $months{apr} = 3;
    $months{may} = 4;
    $months{jun} = 5;
    $months{jul} = 6;
    $months{aug} = 7;
    $months{sep} = 8;
    $months{oct} = 9;
    $months{nov} = 10;
    $months{dec} = 11;

    if (defined($months{$monthTxt})) {
        return ($months{$monthTxt});
    } else {
         return (999);
    }
}

########################################################################################################
#
########################################################################################################

sub convertDateFormat {
    my $date = $_[0];
    
    if (($date =~ m|^\d\d\d\d\/\d\d\/\d\d$|) || ($date eq "today")) { 
         # looks good, just return it!
         return $date;
    }
    
    # Allow a space seperator between components, just convert to a '/'.
    $date  =~ s/\s+/\//g;
    # Allow for input date like '21/feb/2011' as jlog uses this order and three letters for months
    unless ($date =~ m|^\d\d\/\w\w\w\/\d\d\d\d$|) {
        print "ERROR: Invalid date format $date\n";
        return 0;
    }    
    @tmpDate = split ('/', $date, 3);
    # Need to add one, to match logic in validate input parameters.
    $tmpDate[1] = ( getMonth($tmpDate[1]) + 1);
    $tmpDate[1] = sprintf "%02d" , $tmpDate[1];
    if ( $tmpDate[1] > 12 ) {
        print "ERROR: Invalid date: $date\n"; return 0;
    }
    # Swap day and year to match YYYY/MM/DD
    $tmp = $tmpDate[0]; $tmpDate[0] = $tmpDate[2]; $tmpDate[2] = $tmp;
    $date = join("/", @tmpDate);
    return $date;
}

########################################################################################################
#
########################################################################################################

sub convertTimeFormat {
    my $time = $_[0];
    
    # Very simple at present.
    # Just incase the user has entered the time incorrectly, if not, no change.
    # Convert from (jboss format) time [HH:MM:SS,hhh] to [HH:MM:SS.hhh]
    $time  =~ s/,/\./g;
    return $time;
}

########################################################################################################
#
########################################################################################################

sub displayHelpText {

$helpText1 = <<ENDHELPPAGE1;

vjlog.pl Version: $versionVjlog

This script assists in viewing the log file [jboss.log]

Input Parameters:

       -file      : File name to view
       
                    Can be a single file path
                          -file=jboss.log                           
                    or a comma separated list of file paths
                          -file=jboss.1.log,jboss.2.log,jboss.3.log                    
                    Wildcards are supported
                           -file=jboss*log
                           
                    Default [ /opt/tandbergtv/cms/log/jboss.log ]
                    If \$file=stdin read from <STDIN> instead of from a file.
                    
       -time      : Start time to view 
                    [HH|HH:MM|HH:MM:SS|HH:MM:SS.hhh] eg. [ 10:38:22.999 ]
                    Default [00:00]
                    
       -etime     : End time to view   
                    [HH|HH:MM|HH:MM:SS|HH:MM:SS.hhh] eg. [ 11:38 ]
                    Default [Current time]
                    
       -date      : Start date to view 
                    [YYYY/MM/DD] eg. [ 2011/06/19 ] 
                    Default - ( time undefined ) 1900/01/01
                              ( time defined   ) today
                    Entering [ today ] will start with the current date.
                    Also supports format DD/MMM/YYY eg. [ 19/feb/2011 ]
                    
       -edate      : End date to view 
                    [YYYY/MM/DD] eg. [ 2011/06/19 ] 
                    Default - [ current date ]
                    Also supports format DD/MMM/YYY eg. [ 19/feb/2011 ]
                    
       -level     : Message Level to view
                    [ \"INFO|DEBUG|ERROR|WARN\" ]
                    Default [ALL]
                    
       -component : Component to view (FCOMP). Typically always [CMS].
                    Default [ALL]
                    
       -sub       : Sub component to view
                    Default [ALL]
                    
       -method    : Method to view
                    Default [ALL]
                    
       -ignore    : Text idenfifying messages to ignore
                    A VALID perl regular expression. 
                    Matching text in the message header 
                    fields  [FSUBCOMP|FMETHOD|FTEXT] 
                    will supress matching entries eg. [ \"Storage\" ]
                    note: [-ignore=std] Ignores a list of common errors 
                    defined in [\@ignoreErr]
                    
       -inc       : Text idenfifying messages to include ( case insensitive ).
                    A VALID perl regular expression. 
                    ANY matching lines and all previous text 
                    lines back to the related header will be displayed,
                    subject to all other filters. 
                    Match is case INSENSITIVE. ALL other lines will be ignore.
                    
       -incs      : Text idenfifying messages to include ( case sensitive ).
                    Same function as the option \"-inc\" except the match 
                    is case SENSITIVE.
                    
       -unique    : Supress previously displayed header text fields 
                    [0|1]
                    Default [0]
                    1 - supress previous entries
                    Useful to find the number and type of different errors 
                    since a specific time.
                    At the end of the listing prints a summary showing table

       -ulen      : Length of text field FTEXT to use to check text uniqueness.
                    Value [0] will use the full text field.       
                    [integer]
                    Default [0]
                    
       -sum       : [0|1] 
                    Default [0] 
                    1 - Only display message header.
                    0 - Display full output.

===============================================================================
                       Example Message Header
===============================================================================
                         FLEVEL FCOMP      FSUBCOMP           FMETHOD    FTEXT
-date       -time        -level -component -sub               -method
01 Feb 2011 06:42:21,702 INFO   [CMS]      [pool-45-thread-1] method() - Remaining 
                                                                         message text.
===============================================================================

Note:

  When entering parameters, especially regular expressions, containing 
  meaningful shell characters like \"|\", eg. [ DEBUG|ERROR ], the string 
  must be enclosed in quotes eg. [ \"DEBUG|ERROR\" ].
  Any special regular expression characters must be escaped \"\\\" if 
  used as literals.
  
  eg to search for: [ ^text\$ ] use [ \"\\^text\\\$\" ]

  Currently only partial input parameter validation is being performed so 
  watch your typing!

Examples:

To view all ERROR and DEBUG messages after 11:27, today enter 
the following command:

  # ./vjlog.pl  -level=\"ERROR|DEBUG\" -time=11:27

To view ERROR messages logged after 13:20 on the 20th June, but 
supress any \"SANManager\" errors.

  # ./vjlog.pl  -level=ERROR -date=2011/06/20 -time=13:20 -ignore=SANManager

To view full file:

  # ./vjlog.pl

ENDHELPPAGE1

print "$helpText1\n";
return 1;
}
#======================================================================================
########################################################################
#  Created by steve.voisey@ericsson.com 01 Feb 2011.
#
#  Assist in viewing/analysing the cms log file [jboss.log]
#
# Location: /opt/tandbergtv/cms/log/jboss.log
#
# See helptext for functional description.
#
# Outstanding Issues
# ==================
#  Still a few global variables knocking about that need to be tidied!
#
#  Matches in non-header text performed with -inc/-incs may result in an 
#  invalid (excessive) error count.
#
# History
#
# date        author   id   description
# ----------- -------- ---  ----------------------------------------------------------
# 01/feb/2011 srv      1.00 Created
# 09/nov/2011 srv      1.03 Include level in summary header
# 30/mar/2012 srv      1.04 Check properly for log starting with partial entry and 
#                           not a valid message header, and skip until 1st header.
# 10/jun/2012 srv      1.05 Add warn total.
# 17/jun/2012 srv      1.06 Add support for comma separated list of log files.
# 06/aug/2012 srv      1.07 Support time formats containing {GMT+0}
# 05/sep/2012 srv      1.08 Realy check for starting with partial entries, original 
#                           fix seems to have been lost??? 
#                           Add level FATAL
###################################################################################
