bldsystemtools/commonbldutils/EventLogReader.pl
changeset 0 83f4b4db085c
child 2 99082257a271
equal deleted inserted replaced
-1:000000000000 0:83f4b4db085c
       
     1 #!perl -w
       
     2 # This script reads data from specified Windows Event Logs and writes the information to a file
       
     3 # Output is ScanLog-compatible. First this script establishes a time range by reading the specified Build Log.
       
     4 # For info. on Win32::EventLog, see "Win32 Perl Programming" Page 171 et seq.
       
     5 use FindBin;
       
     6 use Sys::Hostname;
       
     7 use Win32;
       
     8 use Win32::EventLog;
       
     9 use Getopt::Long;
       
    10 use strict;
       
    11 # For Date calculations
       
    12 {
       
    13 no warnings;
       
    14 use lib "$FindBin::Bin/../buildsystemtools/lib"; # For running in source
       
    15 }
       
    16 use Date::Manip;
       
    17 
       
    18 # Set TimeZone because Date:Manip needs it set and then tell it to IGNORE the TimeZone
       
    19 &Date_Init("TZ=GMT","ConvTZ=IGNORE");
       
    20 
       
    21 # Check if HiRes Timer is available
       
    22 my $gHiResTimer = 0; #Flag - TRUE if HiRes Timer module available
       
    23 if (eval "require Time::HiRes;") {
       
    24   $gHiResTimer = 1;
       
    25 } else {
       
    26   print "Cannot load HiResTimer Module\n";
       
    27 }
       
    28 
       
    29 # Capture the name of this script for Help display etc.
       
    30 $0 =~ m/([^\\]+?)$/;
       
    31 my $gThisFile = $1;
       
    32 # Process command line
       
    33 
       
    34 my ($gComputer, $gBuildLogstart, $gBuildLogend, $gOutLogFile, @gEventSourcesUser) = ProcessCommandLine();
       
    35 $gComputer = (defined $gComputer)? uc $gComputer: hostname();
       
    36 
       
    37 # Open logfile, if specified
       
    38 my $gOutLogHandle = \*OUTLOGFILE;
       
    39 if (defined $gOutLogFile)
       
    40 {
       
    41     open ($gOutLogHandle, ">$gOutLogFile") or die "Failed to open $gOutLogFile";
       
    42     my $iTime = gmtime(time);
       
    43     PrintStageStart(0,"Windows Event Log Extracts",$gThisFile);
       
    44     print "Logfile: $gOutLogFile  Opened: $iTime\n";
       
    45     print $gOutLogHandle "Output file: $gOutLogFile  Opened: $iTime\n";
       
    46 }
       
    47 else
       
    48 {
       
    49     $gOutLogHandle = \*STDOUT;
       
    50     PrintStageStart(0,"Windows Event Log Extracts",$gThisFile);     # Start logging Generic Info. as pseudo Build Stage
       
    51     print "Logging to STDOUT\n";
       
    52 }
       
    53 print $gOutLogHandle "Data extracted from Windows Event Logs for Computer: $gComputer\n";
       
    54 
       
    55 # Establish time range. Get build start  time from specified Build Log file.
       
    56 unless (defined $gBuildLogstart)
       
    57 {
       
    58     my $iMsg = "No Build Log specified";
       
    59     print $gOutLogHandle "ERROR: $iMsg\n";
       
    60     PrintStageEnd(0);     # Stop logging Generic Info.
       
    61     Usage("$iMsg");
       
    62 }
       
    63 
       
    64 unless (defined $gBuildLogend)
       
    65 {
       
    66     my $iMsg = "No Build Log specified";
       
    67     print $gOutLogHandle "ERROR: $iMsg\n";
       
    68     PrintStageEnd(0);     # Stop logging Generic Info.
       
    69     Usage("$iMsg");
       
    70 }
       
    71 
       
    72 
       
    73 my ($gStartSecs, $gStopSecs) = GetTimeRange($gBuildLogstart, $gBuildLogend);
       
    74 
       
    75 unless (defined $gStartSecs)
       
    76 {
       
    77     my $iMsg = "Invalid Build Log: $gBuildLogstart";
       
    78     print $gOutLogHandle "ERROR: $iMsg\n";
       
    79     PrintStageEnd(0);          # Stop logging Generic Info.
       
    80     Usage("$iMsg");
       
    81 }
       
    82 
       
    83 # Establish time range. Get build  end time from specified Build Log file.
       
    84 # Determine which Event Logs are to be read. Default is "All three". Specifying one only is really a debug convenience.
       
    85 my @gEventSourcesDefault = ('Application','Security','System');
       
    86 my @gEventSources;
       
    87 if (@gEventSourcesUser)  # Event Log(s) specified by user.
       
    88 {
       
    89     foreach my $iSrcU (@gEventSourcesUser)
       
    90     {
       
    91         my $iOKFlag = 0;
       
    92         foreach my $iSrcD (@gEventSourcesDefault)
       
    93         {
       
    94             if (lc $iSrcU eq lc $iSrcD)
       
    95             {
       
    96                 push (@gEventSources, $iSrcD);
       
    97                 $iOKFlag = 1;
       
    98                 last;
       
    99             }
       
   100         }
       
   101         unless ($iOKFlag)
       
   102         {
       
   103             my $iMsg = "Invalid Event Log Filename: $iSrcU";
       
   104             print $gOutLogHandle "ERROR: $iMsg\n";
       
   105             PrintStageEnd(0);     # Stop logging Generic Info.
       
   106             Usage("$iMsg");
       
   107         }
       
   108     }   # End foreach my $iSrcU (@gEventSourcesUser)
       
   109 
       
   110 }
       
   111 else    # Default to "All logs"
       
   112 {
       
   113     @gEventSources = @gEventSourcesDefault;
       
   114 }
       
   115 
       
   116 PrintStageEnd(0);     # Stop logging Generic Info.
       
   117 
       
   118 # Finally read the required Event Log(s)
       
   119 $Win32::EventLog::GetMessageText = 1;   # Ensure that we get the message content from each Event Log entry.
       
   120 for (my $iIndx = 0; $iIndx < @gEventSources; )
       
   121 {
       
   122     my $iEventSource=$gEventSources[$iIndx];
       
   123     ++$iIndx;
       
   124     print "Reading Event Log: $iEventSource\n";
       
   125     ReadEventLog ($iIndx,$iEventSource);
       
   126 }
       
   127 
       
   128 close OUTLOGFILE;
       
   129 exit (0);
       
   130 
       
   131 # ReadEventLog
       
   132 #
       
   133 # Read from one Event Log and output to supplied logfile handle.
       
   134 #
       
   135 # Input: Stage Number, Event Log Name
       
   136 #
       
   137 # Output: ScanLog-compatible data to log file
       
   138 #
       
   139 sub ReadEventLog
       
   140 {
       
   141     my $iStage = shift;         # Stage number.
       
   142     my $iEventSource = shift;   # Name of Event Log file.
       
   143     # First argument to "new Win32::EventLog()" may be "Application", "Security" or "System";
       
   144     my $iTotalEvents;
       
   145     my $iSeparator = "------------------------------------------------------------\n";
       
   146     PrintStageStart($iStage,"Windows $iEventSource Event Log Extracts",$gThisFile);
       
   147 
       
   148     my $iEventObject = new Win32::EventLog($iEventSource, $gComputer);
       
   149 
       
   150     unless ($iEventObject)
       
   151     {
       
   152         print $gOutLogHandle  "ERROR: Failed to open Event Log: $iEventSource\n";
       
   153         PrintStageEnd($iStage);
       
   154         return;
       
   155     }
       
   156 
       
   157     unless ($iEventObject->GetNumber($iTotalEvents))
       
   158     {
       
   159         print $gOutLogHandle  "ERROR: Cannot read Event Log: $iEventSource\n";
       
   160         PrintStageEnd($iStage);
       
   161         return;
       
   162     }
       
   163 
       
   164     unless ($iTotalEvents)    # Check number of events in log.
       
   165     {
       
   166         print $gOutLogHandle "No event recorded in $iEventSource Log.\n";
       
   167     }
       
   168     else
       
   169     {
       
   170         # Reading Flags: EVENTLOG_FORWARDS_READ, EVENTLOG_BACKWARDS_READ, EVENTLOG_SEQUENTIAL_READ, EVENTLOG_SEEK_READ
       
   171         my $iFlag = EVENTLOG_BACKWARDS_READ | EVENTLOG_SEQUENTIAL_READ;
       
   172         my $iRecNum = 0;     # Ignored unless $iFlag == EVENTLOG_SEEK_READ
       
   173         my $iStopFlag = 0;
       
   174         my $count = 0;
       
   175         while (!$iStopFlag)
       
   176         {
       
   177             my %iHash;
       
   178             unless ($iEventObject->Read($iFlag, $iRecNum, \%iHash))
       
   179             {
       
   180                 $iStopFlag = 1;
       
   181             }
       
   182             else    # Successful "read"
       
   183             {
       
   184             my $iEventTime = $iHash{TimeGenerated};
       
   185             if ($iEventTime > $gStopSecs)
       
   186                 { next; }
       
   187             if ($iEventTime < $gStartSecs)
       
   188                 { last; }
       
   189             ++$count;
       
   190             print $gOutLogHandle $iSeparator;
       
   191             # Supported Event Types: EVENTLOG_ERROR_TYPE, EVENTLOG_WARNING_TYPE, EVENTLOG_INFORMATION_TYPE, EVENTLOG_AUDIT_SUCCESS, EVENTLOG_AUDIT_FAILURE
       
   192             my $iTxt;
       
   193             if ($iHash{EventType} == EVENTLOG_ERROR_TYPE)
       
   194                 {$iTxt = 'Error'; }
       
   195             elsif ($iHash{EventType} == EVENTLOG_WARNING_TYPE)
       
   196                 {$iTxt = 'Warning'; }
       
   197             elsif ($iHash{EventType} == EVENTLOG_INFORMATION_TYPE)
       
   198                 {$iTxt = 'Information'; }
       
   199             elsif ($iHash{EventType} == EVENTLOG_AUDIT_SUCCESS)
       
   200                 {$iTxt = 'Audit Success'; }
       
   201             elsif ($iHash{EventType} == EVENTLOG_AUDIT_FAILURE)
       
   202                 {$iTxt = 'Audit Failure'; }
       
   203             else
       
   204                 {$iTxt = "*unknown* [$iHash{EventType}]"; }
       
   205             print $gOutLogHandle "EventType: $iTxt  Source: $iHash{Source}  RecNum: $iHash{RecordNumber}\n";
       
   206             my $iTimeStr = gmtime($iHash{TimeGenerated});
       
   207             print $gOutLogHandle "TimeGen:   $iHash{TimeGenerated} ($iTimeStr)\n";
       
   208             print $gOutLogHandle "Computer:  $iHash{Computer}\n";
       
   209             print $gOutLogHandle "User:      $iHash{User}\n";
       
   210             print $gOutLogHandle "EventID:   $iHash{EventID}\n";
       
   211             print $gOutLogHandle "Category:  $iHash{Category}\n";
       
   212             $iTxt = (defined $iHash{Message})? $iHash{Message}: '*none*';
       
   213             print $gOutLogHandle "Message:   $iTxt\n";
       
   214             $iTxt = ($iHash{Strings})? $iHash{Strings}: '*none*';
       
   215             print $gOutLogHandle "Strings:   $iTxt\n";
       
   216             }
       
   217         }   # End while (!$iStopFlag)
       
   218         print $gOutLogHandle $iSeparator;
       
   219         print $gOutLogHandle "Events in specified time range = $count    Events in file = $iTotalEvents\n";
       
   220     }   # End unless ($iTotalEvents)
       
   221     PrintStageEnd($iStage);
       
   222 }
       
   223 
       
   224 # PrintStageStart
       
   225 #
       
   226 # Print to log file the ScanLog-Compatible lines to start a stage
       
   227 #
       
   228 # Input: Stage, Component Name [,Command Name]
       
   229 #
       
   230 # Output: Start time etc.
       
   231 #
       
   232 sub PrintStageStart
       
   233 {
       
   234     my $iStage = shift;         # Stage number.
       
   235     my $iComponent = shift;     # e.g. Name of Event Log file.
       
   236     my $iCommand = shift;
       
   237     my $iTime = gmtime(time);
       
   238     print $gOutLogHandle "===-------------------------------------------------\n";
       
   239     print $gOutLogHandle "=== Stage=$iStage\n";
       
   240     print $gOutLogHandle "===-------------------------------------------------\n";
       
   241     print $gOutLogHandle "=== Stage=$iStage started $iTime\n";
       
   242     print $gOutLogHandle "=== Stage=$iStage == $iComponent\n";
       
   243     if (defined $iCommand)
       
   244     {
       
   245         print $gOutLogHandle "-- $iCommand\n";
       
   246     }
       
   247     print $gOutLogHandle "++ Started at $iTime\n";
       
   248 
       
   249     if ($gHiResTimer)
       
   250     {
       
   251         print $gOutLogHandle "+++ HiRes Start ".Time::HiRes::time()."\n";
       
   252     }
       
   253 }
       
   254 
       
   255 # PrintStageEnd
       
   256 #
       
   257 # Print to log file the ScanLog-Compatible lines to end a stage
       
   258 #
       
   259 # Input: Stage
       
   260 #
       
   261 # Output: End time etc.
       
   262 #
       
   263 sub PrintStageEnd
       
   264 {
       
   265     my $iStage = shift;         # Stage number.
       
   266     if ($gHiResTimer)
       
   267     {
       
   268         print $gOutLogHandle "+++ HiRes End ".Time::HiRes::time()."\n";
       
   269     }
       
   270     my $iTime = gmtime(time);
       
   271     print $gOutLogHandle "++ Finished at $iTime\n";
       
   272     print $gOutLogHandle "=== Stage=$iStage finished $iTime\n";
       
   273 }
       
   274 
       
   275 #sub GetTimeRange
       
   276 #
       
   277 # Establish start and end times for overall build
       
   278 # Typical start line: === Stage=1 started Mon Oct  4 15:55:31 2004
       
   279 # Typical end line:   === Stage=115 finished Tue Oct  5 01:47:37 2004
       
   280 #
       
   281 # Input: Name of Build Log File to read
       
   282 #
       
   283 # Output: Summary timing info to log file
       
   284 #
       
   285 # Return: Start/End times in seconds
       
   286 #
       
   287 sub GetTimeRange
       
   288 {
       
   289 
       
   290     my ($iBuildLogstart,$iBuildLogend) = @_;
       
   291 
       
   292     my ($iStartTime, $iStopTime);
       
   293     
       
   294 # $iStartTime Time read from $iBuildLogstart
       
   295     unless (open (INLOGFILE, "<$iBuildLogstart"))
       
   296     {
       
   297         print $gOutLogHandle "Failed to open input file: $iBuildLogstart\n";
       
   298         return undef, undef;
       
   299     }
       
   300     while(my $iLine = <INLOGFILE>)
       
   301     {
       
   302         chomp $iLine;
       
   303         unless (defined $iStartTime)
       
   304         {
       
   305             if ($iLine =~ m/^===\s+Stage=\S*\s+started\s+(.+)/)
       
   306                 { $iStartTime = $1; last; }
       
   307         }
       
   308     }
       
   309     close INLOGFILE;
       
   310 
       
   311 # $iStopTime Time read from $iBuildLogend
       
   312 
       
   313     unless (open (OUTFILE, "<$iBuildLogend"))
       
   314     {
       
   315         print $gOutLogHandle "Failed to open input file: $iBuildLogend\n";
       
   316         return undef, undef;
       
   317     }
       
   318     while(my $iLine = <OUTFILE>)
       
   319     {
       
   320         chomp $iLine;
       
   321         if (($iLine =~ m/^===\s(.*\s)?finished\s+(.+)/))
       
   322             { $iStopTime = $2; }
       
   323     }
       
   324     close OUTFILE;
       
   325 
       
   326     my $iDate = ParseDateString($iStartTime);
       
   327     my $iStartSecs = UnixDate($iDate,"%s");
       
   328 
       
   329     print $gOutLogHandle "Time range taken for Build start time from Build Log: $iBuildLogstart\n";
       
   330     print $gOutLogHandle "Earliest Event: $iStartTime\n\n";
       
   331 
       
   332     $iDate = ParseDateString($iStopTime);
       
   333     my $iStopSecs = UnixDate($iDate,"%s");
       
   334 
       
   335     print $gOutLogHandle "Time range taken for Build end time from Build Log: $iBuildLogend\n";
       
   336     print $gOutLogHandle "Latest Event: $iStopTime\n\n";
       
   337 
       
   338     my $iSecs = $iStopSecs - $iStartSecs;
       
   339     my $iMins = int ($iSecs/60);
       
   340     $iSecs %= 60;
       
   341     my $iHours = int($iMins/60);
       
   342     $iMins %= 60;
       
   343     printf $gOutLogHandle "--Time Range between the build START and FINISH:<< %d:%02d:%02d>>\n\n",$iHours,$iMins,$iSecs;
       
   344 
       
   345     return $iStartSecs, $iStopSecs;
       
   346 }
       
   347 
       
   348 
       
   349 # ProcessCommandLine
       
   350 #
       
   351 # Process Commandline. On error, call Usage()
       
   352 #
       
   353 # Input: None
       
   354 #
       
   355 # Return: Parameters as strings.
       
   356 #
       
   357 sub ProcessCommandLine
       
   358 {
       
   359 
       
   360   my ($iHelp, $iComputer, $iBuildLogstart, $iBuildLogend , $iOutFile, @iEventSources);
       
   361   GetOptions('h' => \$iHelp, 'c=s' => \$iComputer, 'l=s' => \$iBuildLogstart,'k=s'=> \$iBuildLogend, 'o=s' => \$iOutFile, 's=s' => \@iEventSources);
       
   362 
       
   363   if ($iHelp)
       
   364   {
       
   365     Usage();
       
   366   }
       
   367   else
       
   368   {
       
   369     return ($iComputer, $iBuildLogstart, $iBuildLogend,$iOutFile, @iEventSources);
       
   370   }
       
   371 }
       
   372 
       
   373 
       
   374 # Usage: Display Help and exits script.
       
   375 #
       
   376 # Input: Error message, if any
       
   377 #
       
   378 # Output: Usage information.
       
   379 #
       
   380 # Return: Never returns. Exits with non-zero errorlevel
       
   381 #
       
   382 sub Usage
       
   383 {
       
   384     if (@_)
       
   385     {
       
   386         print "\nERROR: @_\n";
       
   387     }
       
   388 
       
   389     print <<USAGE_EOF;
       
   390 
       
   391     $gThisFile:
       
   392       Reads the Windows Event Logs for the specified computer.
       
   393       The time range is established by reading the specified Build Log.
       
   394       ScanLog-compatible output is written to specified logfile.
       
   395 
       
   396     Usage: $gThisFile parameters [options]
       
   397 
       
   398     Parameters:
       
   399       -l  Build start Log file
       
   400       -k  Build End Log file
       
   401 
       
   402     Options:
       
   403       -h  Help
       
   404       -c  Computer name (defaults to local PC)
       
   405       -o  Logfile for output (defaults to STDOUT)
       
   406       -s  Event Log Source (defaults to "All")
       
   407           (Supported logs: "Application", "Security" or "System")
       
   408 
       
   409 
       
   410 USAGE_EOF
       
   411 
       
   412     exit 1;
       
   413 }
       
   414 
       
   415 __END__