Hey, Scripting Guy! Question

Hey, Scripting Guy! When I received my Microsoft Certified Systems Engineer (MCSE) certification several years ago, I remember the instructor jumping up and down and telling us that we were not doing our job if we do not check the event logs on a daily basis. When I got my first job as a network administrator, I did a pretty good job checking, but lately we have so many servers and there is so much data in the event logs that I just do not have time to do all that. I feel guilty, like I am not doing my job, but dude, I can't help it. I know this is not a scripting question, but you seem to have been around quite a bit, and so I thought I would write you anyway.

- GB

SpacerHey, Scripting Guy! Answer

Hi GB,

We have always said that checking event logs is kind of like flossing one’s teeth. We all know we should check the event logs, but often it is one of those things that falls by the wayside along the path of good intentions. It is impractical to open the Event Viewer utility, connect to the server, and manually review the previous day's events, unless you only have a few servers. Even if you do have only a few servers, you may prefer to use the techniques we will talk about this week, which are faster and help to ensure you do not miss something.

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.

The Event Viewer utility, seen in the following image, underwent a major overhaul for Windows Vista. In its new incarnation, it is much easier to use, and has some tremendous features that make searching and filtering for events a dream.

In addition to the three traditional event logs (Application, Security, and System), Windows Vista also introduces what are sometimes called the Crimson logs. These are a new type of log that are used by various applications to provide diagnostic information. In Windows PowerShell 2.0, there is a new cmdlet that provides access to those logs, but we will not talk about that cmdlet until Windows PowerShell 2.0 is shipped.

Image of the Event Viewer utility

 

We can retrieve exactly the same information that is displayed in the event viewer by using the Get-EventLog Windows PowerShell cmdlet. The only thing you need to supply to the Get-EventLog cmdlet is the name of the event log. The following command will list every event contained in the application log of the local computer. (You can also see a VBScript version of this command.) The results of the command are seen in the following image.

Get-EventLog -name application
Image of the results of the command

 

Of course, such a command is less than illuminating from a network management perspective, even if it does kind of look cool as the results scroll off the screen. If you want to, you can set a black background with green font and print out the event log a hundred times or so. If you are interested, the code below will print out the entire application log 100 times.

1..100 | % { Get-EventLog application }

The convention 1..100 creates an array with the numbers 1 through 100 in it. Because it is an array, you can actually index into it. To see the data contained in the 55th element, you could do something like this:

PS C:\> (1..100)[55]56

Because the array begins with 0 and we have the number 1 in element 0, element 55 contains the number 56. This can make things a bit confusing at times. The results of the array are pipelined to the ForEach-Object cmdlet (the % sign is an alias for ForEach-Object). Inside the curly brackets, we execute the Get-EventLog cmdlet to retrieve the items from the application log.

Let's get back to work. If we want to filter items from the event log, we need to use the Where-Object cmdlet.

In Windows PowerShell 2.0, there are some additional parameters that allow us to filter by date, event type, or even message. Because Windows PowerShell 2.0 is not out yet, the methodology we use works on both Windows PowerShell 1.0 and Windows PowerShell 2.0. Keep in mind that when Windows PowerShell 2.0 comes out, there will be more efficient ways of doing things.

In order to know what we can use in our filters, we can select a single event log entry, and pipeline it to the Format-List cmdlet. This will allow us to see the property names as well as a piece of sample data. This is shown here (for a VBScript version of this command, refer refer to this "Hey, Scripting Guy!" article):

PS C:\> Get-EventLog -LogName APPLICATION -Newest 1 | Format-List -Property *


EventID            : 1704
MachineName        : vista.nwtraders.com
Data               : {}
Index              : 4827
Category           : (0)
CategoryNumber     : 0
EntryType          : Information
Message            : Security policy in the Group policy objects has been appli
                     ed successfully.
Source             : SceCli
ReplacementStrings : {}
InstanceId         : 1073743528
TimeGenerated      : 4/2/2009 12:30:05 PM
TimeWritten        : 4/2/2009 12:30:05 PM
UserName           :
Site               :
Container          :

In looking through our Application log, we notice there are several events from VSS with the EventID of 8193. This is a Volume Shadow Copy Service error. If I want to see all of these errors, I could use the following command (also see a VBScript version of this command):

Get-EventLog -LogName APPLICATION | Where-Object { $_.EventID -eq 8193 }

This command uses the Get-EventLog cmdlet to retrieve all the events from the Application log. It pipelines this to the Where-Object cmdlet. The Where-Object cmdlet looks at each of the event log entries as they come across. Each event log entry is represented by the $_ character. We look at the EventID property to see if it is equal to the number 8193. If it is, it is printed out to the screen. This is seen here:

Image of the 8193 EventIDs printed to the screen

 

Wow, that looks like an awful lot of events. How many are there? We could scroll up and down the screen counting one, two, three, four, but that would be imprecise and time consuming. We could use the old trick, like we used to do in the VBScript days, of creating a counter variable that we increment on each loop, but that would require too much work. The best way to get the count of the number of event log entries is to use the Measure-Object cmdlet. All you need to do is add another pipe to the end of the previous command, and type Measure-Object. This is seen here:

PS C:\> Get-EventLog -LogName APPLICATION | Where-Object { $_.EventID -eq 8193 }
 | Measure-Object


Count    : 136
Average  :
Sum      :
Maximum  :
Minimum  :
Property :

If you would like to know exactly how many entries are in the event log, you can access the count property of the object:

PS C:\> (Get-EventLog -LogName APPLICATION).count
4827

A faster way to get the number of entries in the event log is to use the -List switch from the Get-EventLog cmdlet. This is seen herehere:

PS C:\> Get-EventLog -List

  Max(K) Retain OverflowAction        Entries Log
  ------ ------ --------------        ------- ---
  20,480      0 OverwriteAsNeeded       4,827 Application
     512      7 OverwriteOlder              0 DFS Replication
     512      7 OverwriteOlder              1 ForScripting
  20,480      0 OverwriteAsNeeded           0 HardwareEvents
     512      7 OverwriteOlder              0 Internet Explorer
  20,480      0 OverwriteAsNeeded           0 Key Management Service
  20,480      0 OverwriteAsNeeded      15,035 Security
  20,480      0 OverwriteAsNeeded      40,727 System
  15,360      0 OverwriteAsNeeded       6,542 Windows PowerShell

We can then find the percentage of the VSS errors in our Application log by using the following command:

PS C:\> (135 / 4827) * 100
2.79676817899316

Not surprisingly, it looks like junior high school math. If we feel that we do not need this level of precision, we depart quite rapidly from junior high school math. We can use .NET Framework format specifiers. The format pattern goes inside curly brackets. The initial 0, the one before the colon, represents the index of the number to be formatted. The N2 means we are formatting a number and want two digits. The -f is the format operator:

PS C:\> "{0:N2}" -f ((135 / 4827) * 100)
2.80

If that is too complicated, you can also format the number by using an overload of the tostring method. This is something I came up with a few weeks ago and is the way I intend to start formatting my numbers (assuming I don't forget). Compare the two methods, and you can make your own decision:

PS C:\> ((135 / 4827) * 100).tostring("N2")
2.80

Well, GB, I hope this will help you get back into the habit of checking your event logs. As you can see, it is easy and can actually be quite fun. I can also do things I would never dream of doing before when using the Event Viewer utility (like telling you that 2.8 percent of the event log entries in my Application log are related to VSS). Pretty cool, huh? Join us tomorrow as Event Log Week continues. Until then, peace.

 

Ed Wilson and Craig Liebendorfer, Scripting Guys