You need to use some of the more advanced performance counters to accurately track information about your application. This performance counter exists as two counters used together. The first counter is the main counter, which is divided by the second counter, called the base counter. Essentially, the first counter is the numerator and the second counter is the denominator; the custom counter reports the result of this division operation. The main counter is used in tandem with its base counter type to calculate, for example, the average amount of time it takes for an action (e.g., connecting to a server) to complete or the average number of actions that occur during a single process (e.g., database timeouts).
Create a complex performance counter, which is used in tandem with the base counter type to calculate, for example, the average amount of time it takes for an action to complete or the average number of actions that occur during a single process. Use the following method to create a complex custom counter:
public void CreateComplexCounter(string counterName, string counterHelp, PerformanceCounterType counterType, string baseCounterName, string baseCounterHelp, PerformanceCounterType baseCounterType, string categoryName, string categoryHelp, out PerformanceCounter appCounter, out PerformanceCounter appBaseCounter) { CounterCreationDataCollection counterCollection = new CounterCreationDataCollection( ); // Create the custom counter object and its base counter object // and add them to the collection of counters (they must be // added successively) CounterCreationData counter = new CounterCreationData(counterName, counterHelp, counterType); counterCollection.Add(counter); CounterCreationData BaseCounter = new CounterCreationData(baseCounterName, baseCounterHelp, baseCounterType); counterCollection.Add(BaseCounter); // Create category if (PerformanceCounterCategory.Exists(categoryName)) { PerformanceCounterCategory.Delete(categoryName); } PerformanceCounterCategory appCategory = PerformanceCounterCategory.Create(categoryName, categoryHelp, counterCollection); // Create the counter and initialize it PerformanceCounter newAppCounter = new PerformanceCounter(categoryName, counterName, false); PerformanceCounter newAppBaseCounter = new PerformanceCounter(categoryName, baseCounterName, false); newAppCounter.RawValue = 0; newAppBaseCounter.RawValue = 0; appCounter = newAppCounter; appBaseCounter = newAppBaseCounter; }
The CreateComplexCounter
method returns two
PerformanceCounter
objects as out
parameters; one is the counter, and
the other is the base counter. These two counters are used in tandem;
the base counter controls some aspect of the denominator in the
calculation relating these two counters. Since the value of the
appCounter
parameter, returned from this method,
depends on the value in the appBaseCounter
parameter, we are considering these types of counters as complex
counters.
The ASPNET
user account, by default, prevents
performance counters from being read. You can either increase the
permissions allowed by this account or use impersonation to enable
this functionality. However, this then becomes a deployment
requirement of your web application. Decreasing security for the
ASPNET
account may very well be frowned upon by IT
folks deploying your application.
This method operates similarly to
the CreateSimpleCounter
method described in Recipe 6.12. The one major difference is that two
CounterCreationData
objects are created and added
to the CounterCreationDataCollection
object. This
first CounterCreationData
object is the main
counter used in the calculation for this counter. The second is the
base counter, used in the denominator of the calculation for this
counter. These counters must be added, in order, to the
CounterCreationDataCollection
object. In addition,
the counter defined by the counterName
parameter must be added before the counter defined by the
baseCounterName
parameter.
The application can perform several actions on these
PerformanceCounter
objects. An application can
increment or decrement a PerformanceCounter
object
using one of these three methods:
long value = newAppCounter.Increment( ); long value = newAppCounter.Decrement( ); long value = newAppCounter.IncrementBy(i); long value = newAppBaseCounter.Increment( ); long value = newAppBaseCounter.Decrement( ); long value = newAppBaseCounter.IncrementBy(i); // Additionally, a negative number may be passed in to the IncrementBy method // to mimic a DecrementBy method (which is not included in this class) long value = newAppCounter.IncrementBy(-i); long value = newAppBaseCounter.IncrementBy(-i);
The first two methods accept no parameters, while the third accepts a
long
containing the number by which to increment
the counter. All three methods return a long
type
indicating the new value of the counter.
In addition to incrementing or decrementing these counters, you can also take samples of these counters at various points in the application. A sample is a snapshot of the counter and all of its values at a particular instance in time. A sample may be taken using the following lines of code:
CounterSample counterSampleValue = newAppCounter.NextSample( ); CounterSample counterSampleValue = newAppBaseCounter.NextSample( );
The NextSample
method accepts no parameters and
returns a CounterSample
object.
At another point in the application, a counter may be sampled again,
and the samples can be passed in to the static
Calculate
method on the
CounterSample
class. These actions may be
performed on a single line of code as follows:
float calculatedSample = CounterSample.Calculate(counterSampleValue, newAppCounter.NextSample( ));
Note that you need to pass only the newAppCounter
samples; the newAppBaseCounter
samples are handled
for you. The calculated sample calculatedSample
may be stored for future analysis. See Recipe 6.12 for a definition of the
Calculate
method.
The complex performance counters defined in the .NET Framework are defined here:
AverageCount64
Calculates the AverageTimer64
value change over
the AverageBase
value change. This counter uses
AverageBase
as its base counter type.
AverageTimer32
Calculates the AverageTimer32
value change over
the number of ticks per second, all over the
AverageBase
value change. This counter uses
AverageBase
as its base counter type.
CounterMultiTimer
Calculates the percentage of CounterMultiTimer
value change over the CounterMultiTimer
time
change divided by CounterMultiBase
. This counter
uses CounterMultiBase
as its base counter type.
CounterMultiTimerInverse
Calculates the inverse of the CounterMultiTimer
counter. This counter uses CounterMultiBase
as its
base counter type.
CounterMultiTimer100Ns
Calculates the percentage of CounterMultiTime100Ns
value change over the CounterMultiTime100Ns
time
change divided by CounterMultiBase
. The value of
this counter is measured in 100ns time units. This counter uses
CounterMultiBase
as its base counter type.
CounterMultiTimer100NsInverse
Calculates the inverse of the
CounterMultiTimer100Ns
counter. This counter uses
CounterMultiBase
as its base counter type.
RawFraction
Calculates a percentage of the RawFraction
counter
value over the RawBase
counter value. This counter
uses RawBase
as its base counter type.
SampleCounter
Calculates the SampleCounter
value change over
their corresponding SampleBase
value change per
second. This counter uses SampleBase
as its base
counter type.
SampleFraction
Calculates the percentage of SampleCounter
value
change over the SampleBase
value change. This
counter uses SampleBase
as its base counter type.
See Recipe 6.12; see the “PerformanceCounter Class,” “PerformanceCounterType Enumeration,” “PermformanceCounterCategory Class,” “ASP.NET Impersonation,” and “Monitoring Performance Thresholds” topics in the MSDN documentation.