Introduction
Performance counters provide an excellent method to monitor key performance indicators for an application. Before being able to write to a performance counter, two tasks need to be completed.
- First, the performance counter category and the contained performance counters must be created on the machine where the application is going to use them. This can only be done by a user with administrative rights.
- Second, the application needs to create writable instances of the performance counters.
By using custom attributes (this was inspired by Duncan Edwards Jones' article about performance counters) and a helper class, we can easily perform both tasks from a single definition for each performance counter.
Background
We recently introduced new search technology to our Web site and wanted to make sure that we get a good feeling for the performance of that search technology in the production environment. We also wanted to define thresholds for some performance indicators for the system and network monitoring tool so we can automatically raise alerts if performance drops below the threshold. Performance counters were the ideal solutions for us since they are lightweight, fast, and could easily be plugged into our monitoring tool.
For ease of use and maintenance reasons, we only wanted to define performance counters once in the code and then have the ability to use that definition to create the performance counters and categories on the machine as well as the instances in the application. Due to the application being a Web site, there was no way for the application to create the performance counters if they did not exist because it does not have administrative rights. At the same time we wanted to avoid having to create additional tools for performance counter and category creation.
Using the Custom Attributes
To associate a class with a performance counter category, simply decorate the class with the PerformanceCounterCategoryAttribute
.
[PerformanceCounterCategory("Random Test", PerformanceCounterCategoryType.MultiInstance)]
class Test
{
...
To associate a PerformanceCounter
class member with a performance counter, simply decorate it with the PerformanceCounterAttribute
.
[PerformanceCounter("Random Rate", "", PerformanceCounterType.RateOfCountsPerSecond32)]
private static PerformanceCounter pcRandomRate;
[PerformanceCounter("Random Average", "", PerformanceCounterType.AverageCount64)]
private PerformanceCounter pcRandomAverage;
private PerformanceCounter pcRandomAverageBase;
[PerformanceCounter("Random Value", "",
PerformanceCounterType.NumberOfItems64, "Standard")]
private PerformanceCounter pcRandomValue;
[PerformanceCounter("Random Value", "",
PerformanceCounterType.NumberOfItems64, "Inverted")]
private PerformanceCounter pcRandomInvertedValue;
private bool usePerformanceCounters;
Note that counter types that need a base counter only have an attribute on the actual performance counter. The helper class will automatically create the base counter when needed. The helper class also assumes that the variable name for the base counter is the performance counter variable name with "Base" appended to it. The same assumption is made for the counter and base counter names.
If the performance counter category is created to support multi-instance counters and a counter does not have an instance name, the helper class uses _default
as the instance name.
Declare a boolean variable that can be used to safeguard updating of performance counters in the application code.
Creating Writable Performance Counter Instances
To instantiate the performance counter for a given class, use the helper class PerformanceCounterFactory
inside the constructor:
public Test()
{
pcRandomRate = pcRandomAverage = pcRandomAverageBase = pcRandomValue
= pcRandomInvertedValue
= null;
try
{
usePerformanceCounters =
PerformanceCounterFactory.CreateCounters(typeof(Test), this);
}
catch (Exception)
{
usePerformanceCounters = false;
}
}
Updating Performance Counters
The performance counters can then be updated as usual from within the application code. Make sure the performance counter access is safeguarded with a boolean variable that indicates that the performance counter creation was successful.
if (usePerformanceCounters)
{
pcRandomValue.RawValue = r;
pcRandomInvertedValue.RawValue = randMax - r;
pcRandomRate.Increment();
pcRandomAverage.IncrementBy(r);
pcRandomAverageBase.Increment();
}
Installing Performance Counter Categories
The code above assumes that the performance counters and the category they belong to already exist. From the class definition and attributes, we can also create the category and performance counters by using the helper class in conjunction with an Installer
and a PerformanceCounterInstaller
. The following code needs to be added to the installer's constructor:
PerformanceCounterFactory category = new PerformanceCounterFactory(typeof(Test));
counterInstaller.CategoryName = category.Name;
counterInstaller.CategoryType = category.CategoryType;
if (category.Help != null)
counterInstaller.CategoryHelp = category.Help;
counterInstaller.Counters.AddRange(category.Counters);
If the performance counters for a performance counter category spread across multiple classes, use PerformanceCounterFactory.AddCounters(typeof(AdditionalTestClass));
to add them to the helper class before passing them to the PerformanceCounterInstaller
.
Installing and uninstalling of performance counter categories can simply be done by running installutil
with the given assembly.
To install a performance counter category and counters without using an installer, simply use the PerformanceCounterFactory
constructors and the Create()
method.
Points of Interest
Installers can be useful even if you do not intend to wrap them in an MSI.
History
- 1.0 Initial version
- 2.0 Migrated the code to .NET 2.0 and added support for performance counter instances