Within Windows there are many system level processes that publish performance counters. For example there are performance counters that allow you to see CPU time, memory usage, and disk usage of the system. Since performance counters are a standard system function they are an ideal method for tracking the performance your applications. Luckily, Microsoft has provided several classes within the .NET Framework to publish custom performance counters.
This blog post is also available in PDF form in a TechRepublic download, which includes all of the example code in a Visual Studio Project file.
These custom counters can be used to publish performance information directly from your applications. For instance, if you have a file parsing service that parses files and inserts records into a database, you can publish a custom performance counter to expose the number of records inserted. This information can then be used to determine the speed at which records are being inserted, which can help in making decisions on scaling options and hardware requirements.
Performance counters can also be used to determine whether or not a process has completely locked up. If the data represented by a performance counter doesn't change within a specified time frame then you can determine that there has been an issue and publish an alert. A very good example of this is in the case of monitoring MSMQ messages. Generally you expect the number of messages within a MSMQ queue to change fairly rapidly - if at some point the number of messages stops changing then there is a good chance something is wrong.
Publishing your counter
Performance counters are organized into categories which contain multiple counters that usually monitor different aspects of the same system function. Within each counter there can also be multiple instances. For example, if we want to monitor the number of messages in a MSMQ we would go to the category "MSMQ Queue", select the counter "Messages in Queue", and then select the instance that we want to monitor (in this case each queue is an instance).After you have determined how you will organize your custom counters you will have to create them. The code shown in Figure A creates a counter named "SampleCounter" in the category "SampleCategory".
Create a counter
Note that only one counter is added in this code. If more than one counter needs to be created simply copy the code that creates the sampleCounter object and add it to the counterData array. Any counters added to the counterData array will be created when you call the Create method on the PerformanceCounterCategory object.
Also notice the "SingleInstance" portion of the Create() call. This disables multiple instances for the specified counter. If you need multiple instances you can simply change "SingleInstance" to "MultiInstance" when you call the Create method.Once the Create() method is called the counter is ready for you to write data to it. Figure B shows how to correctly instantiate the performance counter object and how to check to see if the category currently exists.
Instantiate the performance counter
In this code we instantiate the PerformanceCounter object so that its scope is not just the scope of the code that will be writing to it. This is done for a couple of reasons.
The first is that the code uses a Timer to write data to the performance counter. Because of this the PerformanceCounter object must be in scope when the Timer's Tick event is fired.
Second, instantiating the PerformanceCounter before writing to it every time can have a major performance impact on the overall application's performance (more on this later).Now that we have instantiated the PerformanceCounter we are able to write to it. As mentioned above, this is done in the Timer's Tick method which is shown in Figure C.
Timer's Tick method
This code simply sets the PerformanceCounter.RawValue property to the current second and then modifies the counter's interval to make the counter's value a little more dynamic. The RawValue property is the current value of the PerformanceCounter, and that is the value that will be displayed when you read the PerformanceCounter.
In production applications you will more than likely want to store some base values to be published via the custom performance counter. For instance, in the file parsing system mentioned above you would want to store the total number of records parsed, the total number of records parsed within the last minute, and possibly the average time to insert a record into the database. All of these numbers would be published from different performance counters within your application's performance counter category.
Reading performance counters
The easiest way to read a performance counter is to use the PerfMon snap-in for Microsoft Management Console. To run PerfMon and pull up your custom counter follow these steps:
- Click the Start menu and click on "Run"
- Type "perfmon" and hit enter
- The PerfMon snap-in should appear, and more than likely has a few default counters already added.
- Right click in the graph area and click "Add Counters"
- Under "Performance object:" choose "SampleCategory"
- Under "Select counters from list:" make sure "SampleCounter" is selected
- Click "Add"
- Click "Close"
The SampleCounter should now be displayed in the graph. If you are not currently sending data to the counter the line will be flat but will begin to change once you start sending data.
A more complex, but often much more useful way of reading performance counters is to do so programmatically. This allows you to poll the counter whenever you need to and store the counter's data for examination. I have discussed this previously: "A sample app to monitor performance counters and send alerts."
The cost of instantiation
Because of the way performance counters work, it is likely that you will be writing a large amount of data to them — for example each time a record gets added to the database you may want to add to a total record count and publish that via a performance counter. Since this is the case, whenever possible you should instantiate the PerformanceCounter object(s) once, and only once, instead of instantiating them each time you write data to them. The reason for this is that instantiating a PerformanceCounter object is a resource intensive task and if you instantiate a new PerformanceCounter object each time you need to write to it you could adversely affect application performance.Take, for example, the two snippets of code shown in Figure D and Figure E.
While both of these snippets perform the exact same function, the code in Figure E runs over 5000 times faster than the code in Figure D. This is because of the simple fact that the code in Figure E instantiates the PerformanceCounter object once, and the code in Figure D instantiates the PerformanceCounter object once for each iteration of the loop.
The TechRepublic Download
I would suggest that you take advantage of the TechRepublic Download that is associated with this document. The download includes a PDF version as well as a Visual Studio project file that contains all of the code for the functionality discussed.