Hey, Scripting Guy! Question

Hey, Scripting Guy! I am trying to chase down a problem I have on my computer. Every so often, it just quits responding. I think it may be trying to talk to Elvis or commune with E.T. Perhaps if I point my finger and say, "E.T. gone home" in a raspy drone-like voice it would cease and desist all illogical activities and begin to listen to me. Anyway, I think there must be some information logged in one of the event logs that may give me a clue as to why my computer is behaving like a three-year-old child that has gotten into mom's box of chocolates. The problem is that I do not know which event log to examine. I wish I could look at all the logs at the same time, from the same time frame. But I guess that is a bit too much to ask, even from a Scripting Guy. Or is it? Can you help me?

- AC

SpacerHey, Scripting Guy! Answer

Hi AC,

Have you tried singing to your computer? Maybe a few verses of "Love Me Tender" would cause your computer to settle down and behave a little nicer. We do not think that chanting "You ain't nothin' but a hound dog" would help, however, and may cause data loss if you are not careful. If you are embarrassed to sing soft sweet songs to your computer (particularly if you work in cubical city), maybe you would rather track down the cause of your frustrations. As you suggested, AC, one of these ways might be to examine the event logs. It might be that there is a scheduled task or a service that is starting up in the background that is causing what you interpret as attempts to commune with dearly departed music legends or fictitious creatures from outer space. Certainly we should rule out the mundane before attempting to understand the more exotic causes of your frustration.

This week is Event Log Week. We have quite a few good scripts that work with event logs in the Script Center Script Repository. The Scripting Guide has some good information about querying event logs, managing event logs, and writing to event logs from a VBScript perspective. These same types of information are covered from a Windows PowerShell perspective in chapter 3 of the Windows PowerShell Scripting Guide. Over the years, there have been a few “Hey, Scripting Guy!” articles on topics such as finding the oldest event in an event log, the newest event in an event log, backing up the event log to a text file, retrieving audit failures from the event log, or retrieving all failures from the security event log. There is also the Log Parser 2.2, and we have a number of examples on the Script Center of how to use Log Parser. The scripts this week are written in Windows PowerShell. If you need help converting VBScript to Windows PowerShell, you can refer to this conversion guide.

We decided to write a script called CheckEventLogs.ps1 for you today. This script accepts a single parameter that governs how many minutes worth of event logs it will retrieve. As you requested, AC, this script will examine all of the classic event logs on your computer, and pull out all events that occurred during the time frame specified. The complete CheckEventLogs.ps1 script is shown here.

Param($numberOfMinutes=60)
Function Get-Dot($x)
{
 "." * $x
} #end Get-Dot

$x = 1
Get-EventLog -List | 
ForEach-Object  -begin `
 { Write-Host -Foregroundcolor yellow "Inspecting event logs $(Get-Dot -x $x )" }  `
-process { 
  Write-Host -ForeGroundColor green "Checking log $($_.logDisplayName) $(Get-Dot -x $x)"
   Get-EventLog -logname $_.log |
   Where-Object { $_.TimeWritten -ge (Get-Date).AddMinutes(-$numberOfMinutes) }
 $x ++
}

The first thing that we need to do is to create a parameter that will allow us to modify the way the script runs. To do this, we use the Param statement and create a variable named $numberofminutes to allow us to change the number of minutes worth of events that we will retrieve from the event log. By default we set this value to be equal to 60 minutes; therefore, when the script is run, only events that occurred within the previous hour will be retrieved. This is seen here:

Param($numberOfMinutes=60)

The Get-Dot function is used to indicate progress in reading to the end of an event log. This is used primarily for fun, but it does give a visual indicator of the progress of the script. To create the Get–Dot function, we begin with the Function keyword, specify the name, and assign an input variable. We open a set of curly brackets and place the period inside a pair of quotation marks ("."). We multiply the period by the number that is stored in the $x variable. By using this multiplication technique, we are able to create a series of the same string value that is the length represented by the variable:

Function Get-Dot($x)
{
"." * $x
} #end Get-Dot

The initial value of the $x variable is set to be equal to 1. This is a straight-forward value assignment and is shown here:

$x = 1

Next we call the Get-EventLog cmdlet with the –List parameter. When this is used, it produces a listing of all the logs. By utilizing this technique, we avoid having to specify from which event logs we wish to retrieve information. Each of the items produced by the Get-EventLog cmdlet is an instance of the system.diagnostics.event log .NET Framework class, which is then pipelined to the next command. This is shown here:

Get-EventLog -List | 

We use the ForEach-Object cmdlet to enable us to work through the collection of system.diagnostics.eventlog classes. Because we wish to produce a heading for our report, we specify the –begin parameter of the ForEach-Object cmdlet. The –begin parameter is called one time for the entire series of items, which is passed to the ForEach-Object cmdlet. We decide to use the Write-Host cmdlet, which gives us the option to specify a different color for our text. This will allow our header to stand out visually from the other information that is given by this command. To specify a different color, we have the option of specifying either a foreground color or a background color. The background color would change the background upon which the text was to be displayed. In general I do not use this parameter. The –foregroundcolor parameter, however, is much more useful in that it changes the color of the text which is displayed. We just display a status message that states that we’re inspecting the event logs. Then we call the Get-Dot function. Because the call to the function takes place within a set of double quotation marks, we need to ensure that the function is evaluated before the text. To do this we use a subexpression that consists of a dollar sign, an opening smooth parenthesis, the name of the function, and the closing parentheses. The ForEach-Object command is not complete and therefore we must use a back-tick to indicate that the line continues. This is all seen here:

ForEach-Object  -begin `
{ Write-Host -Foregroundcolor yellow "Inspecting event logs $(Get-Dot -x $x )" }  `

Now we get to the portion of the script that will actually process the event logs. To do this we use the -process parameter of the ForEach–Object cmdlet. This parameter is the default parameter for the ForEach-Object and most of the time does not actually appear in a script. The –process parameter is seen here:

-process { 

We then want to produce a status message to the screen. To do this we again use the Write-Host cmdlet with the –foregroundcolor parameter. This time we specify the color as green to make it visually distinctive from the beginning header statement. The display name of the log is different at times than the actual name of the log. We use the logDisplayName property, which we get from the eventlog object, to inform the user of our progress. We do this because some of the logs may be very large, and a significant amount of time could be spent searching each log. We call the Get-Dot function to help track our progress. This is seen here:

  Write-Host -ForeGroundColor green "Checking log $($_.logDisplayName) $(Get-Dot -x $x)"

When the script is run, the status messages appear on the screen as seen here:

Image of the status messages that appear when the script is run

 

We use the Get-EventLog cmdlet to read the event information from the event logs. The name of the log that we are to examine is contained in the log property of the object. The $_ variable refers to the current item on the pipeline. Because this item is an EventLog object, we are able to directly access its properties. We pipeline the collection of EventLog objects to the Where-Object. This is seen here:

   Get-EventLog -logname $_.log |

To filter out the EventLog objects that occurred during the last number of minutes, we need to use the Where-Object cmdlet. Inside the script block of the Where-Object cmdlet, we look at the TimeWritten property of the object. If the value of the TimeWritten property is greater than or equal to the time value—computed by subtracting the current time stamp we obtained from the Get-Date cmdlet from minutes stored in the $numberofMinutes variable—we write the event to the screen.This is seen here:

   Where-Object { $_.TimeWritten -ge (Get-Date).AddMinutes(-$numberOfMinutes) }

When we are finished processing the current log, we increment the value of $x by 1, and go to the next event log. This is seen here.

 $x ++
}

Well, AC, this has been a simple little script that can hopefully save you a great deal of embarrassment and frustration with your computer. Try this script first. If you don't find anything in your event logs, you can always grow your sideburns long and work on your Elvis impression. This also brings us to the end of Event Log Week. Join us tomorrow for Quick-Hits Friday. Until tomorrow, don’t be cruel. Peace!

 

Ed Wilson and Craig Liebendorfer, Scripting Guys