Diagnostics

.Diagnostics
Performance Monitoring | Custom Performance Counters | Event Logs

"Applications running in production need to be able to monitor their behavior, detect errors, and log diagnostic information. Performance Counters can be created to sample an application's performance. Custom Event Logs can be created to log warnings and errors."

The IDE is a tremendous help in finding problems during development. However once an application is in production it must gather and record diagnostic information itself. In the System.Diagnostics namespace .NET has facilities to monitor an application, detect runtime errors, and log diagnostic information. Tracing is also an effective diagnostic technique which is covered in the Debugging section along with the Debug Class, Profiling, Stack Dumps, and the Performance Wizard.

Performance Monitoring

.Performance Monitor

The Windows operating system contains a performance-monitoring infrastructure which tracks runtime behavior for processes and applications. Windows provides a graphical interface to this infrastructure with a program call The Performance Monitor (perfmon.exe). The monitoring infrastructure stores the runtime information into performance counters which are grouped into categories such as "System" and "Processor". As well as viewing the performance counters in the GUI, a programmer can also access the performance counters in code.

Be aware that some performance counters require administrative privileges. All performance counters measure "instances" of processing. Some performance counters will always have instances, such as the CPU. Others however many not have an instance, such as garbage collection counters. So if it is possible that an instance might not exist, it is important that the code check for a valid instance before attempting to read a performance counter. The PerformanceCounter class is used to obtain information from the monitoring infrastructure. Below is an example using the PerformanceCounter class to obtain the number of bytes per second of network traffic. I used perfmon.exe to obtain the required string name which identifies the network hardware.

.Network Interface Performance Counter
Network Interface Performance Counter

using System;
using System.Diagnostics;
using System.Threading;

namespace PerformanceCounterExample1
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Network Interface Performance Counter\n");
            using (PerformanceCounter pc = new PerformanceCounter("Network Interface", "Bytes Received/sec", "Realtek RTL8168C[P]_8111C[P] Family PCI-E Gigabit Ethernet NIC [NDIS 6.20]"))
            {
                for (int i = 0; i < 15; i++)
                {
                    Console.WriteLine("{0,2}. Bytes/Sec: {1,10:F2}", i+1, pc.NextValue());
                    Thread.Sleep(1000); // Poll every second
                }
            }
            Console.WriteLine();
        }
    }
}

Top




Custom Performance Counters

Creating custom performance counters can target the monitoring of specific areas of an application. Once the performance counters are created then they can read with another application or with the Windows performance monitor. Creating performance counters require administrative privileges, so they are usually created as part of an application setup. Additionally custom counters can not be created on remote computers. Custom counters can be created three different ways:

  1. Use Server Explorer to create one or more counters at design time. See How to: Create Custom Performance Counters for more details.

  2. Programmatically, use the CounterCreationData class to create one or more counters, and then add them to a collection of type CounterCreationDataCollection and pass the collection as a parameter of a special form of the Create method.

  3. Programmatically, use the Create method on the PerformanceCounterCategory class to create a single counter in a new category.

When programmatically creating performance counters, the type of the performance counter will need to be specified. The available types are contained in the PerformanceCounterType Enumeration. Performance counter types can represent raw data or they can represent calculated values that are based on one or more counter samples. Performance counter types can be classified as:

Performance Counter Type Classification
  1. Average - measure a value over time and display the average of the last two measurements. Associated with each average counter is a base counter that tracks the number of samples involved.
  2. Difference - subtract the last measurement from the previous one and display the difference, if it is positive; if negative, they display a zero.
  3. Instantaneous - display the most recent measurement.
  4. Percentage - display calculated values as a percentage.
  5. Rate - sample an increasing count of events over time and divide the change in count values by the change in time to display a rate of activity.
Performance Counter Type Enumeration

Performance counter types specify the formula used to calculate the NextValue method for a PerformanceCounter instance. Some of the type values from the enumeration include:

  1. AverageBase - Stores the denominator that is used in the calculation of time or count averages
  2. AverageCount64 - An average counter that shows how many items are processed, on average, during an operation.
  3. AverageTimer32 - An average counter that measures the time it takes, on average, to complete a process or operation.
  4. CounterDelta64 - A difference counter that shows the change in the measured attribute between the two most recent sample intervals
  5. CounterTimer - A percentage counter that shows the average time that a component is active as a percentage of the total sample time.
  6. ElapsedTime - A difference timer that shows the total time between when the component or process started and the time when this value is calculated.
  7. NumberOfItems64 - An instantaneous counter that shows the most recently observed value.
  8. RateOfCountsPerSecond64 - A difference counter that shows the average number of operations completed during each second of the sample interval.
  9. SampleBase - A base counter that stores the number of sampling interrupts taken and is used as a denominator in the sampling fraction
  10. SampleCounter - An average counter that shows the average number of operations completed in one second.
Performance Counter Categories

The Windows performance counters are grouped into categories which include physical components (processors, memory, etc) and system objects (processes, threads, etc). The most frequently used categories for Windows performance counters are: Cache, Memory, Objects, PhysicalDisk, Process, Processor, Server, System, and Thread. These categories are created from the Performance Counter Category class. You can put a custom counter into one of the predefined Windows categories (use PerformanceCounterCategory.Exists to verify existence first), or you can create a new category (use PerformanceCounterCategory.Create).

After a performance counter is created, it should not be immediately used. A latency time is required to enable the counters, so the performance counter creations should be a separate process than the performance counter usage. The new performance counters can be viewed with the Windows performance monitor utility (perfmon.exe) or with the Server Explorer, as shown below .

.Performance Categories in Server Explorer
Performance Categories in Server Explorer


Collection for CounterCreationData

When more than one performance counter is created for a category, the counters should be stored in a collection using the predefined CounterCreationDataCollection Class. This class provides a strongly typed collection of CounterCreationData objects. The CounterCreationData objects define the individual performance counters and contain the counter type, name, and help string for the custom counter.

Note: When creating new performance counters, you must close the performance monitor and reopen it without deleting the counters. Otherwise the performance monitor does recognize the new counters.

Performance Counter Data

The initial, or uncalculated, value of the performance counter (PerformanceCounter Class) is stored in the RawValue property. The ReadOnly property can be set to put a particular instance of a performance counter into read-only mode. The performance counter has thread-safe methods used to change the value of the counter:

  1. Increment - Increments the associated performance counter by one through an efficient atomic operation.
  2. IncrementBy - Increments or decrements the value of the associated performance counter by a specified amount through an efficient atomic operation.
  3. Decrement - Decrements the associated performance counter by one through an efficient atomic operation.

Two methods are used to obtain a data sample:

  1. NextValue - Obtains a counter sample and returns the calculated value for it.
  2. NextSample - Obtains a counter sample, and returns the raw, or uncalculated, value for it.

The following example program creates two customer performance counters by:

  1. Create a collection of type CounterCreationDataCollection.
  2. Create the counters you want to create as objects of type CounterCreationData and set their necessary properties.
  3. Add the CounterCreationData objects to the collection by calling the collection's Add method.
  4. Call the Create method on the PerformanceCounterCategory class and pass the collection to it.

and then uses the performance counters by changing the RawValue property and obtaines samples of data in the calculated (NextValue) and uncalculated
(NextSample) forms.

.Custom Performance Counters
Custom Performance Counters

using System;
using System.Diagnostics;

public class Program
{

    public static void Main()
    {
        CollectSamples();
    }

    public static void CollectSamples()
    {
        const String categoryName = "KevinsPerformanceCounters";
        const String counterName1 = "ElapsedTimeSample";
        const String counterName2 = "NumberofItemsSample";

        /***************************************************************************
         * The first time the code is run, the performance counters are created.   *
         *                                                                         *
         *  If the category does not exist, create the category and exit.          *
         *  Performance counters should not be created and immediately used.       *
         *  There is a latency time to enable the counters, they should be created *
         *  prior to executing the application that uses the counters.             *
         ***************************************************************************/
        //if (PerformanceCounterCategory.Exists(categoryName))
        //    PerformanceCounterCategory.Delete(categoryName);
        //return;

        if (!PerformanceCounterCategory.Exists(categoryName))
        {
            // 1. Create a collection of type CounterCreationDataCollection.
            CounterCreationDataCollection ccdCollection = new CounterCreationDataCollection();

            // 2.Create the counters CounterCreationData and set their necessary properties.
            CounterCreationData cdCounter1 = new CounterCreationData();
            cdCounter1.CounterName = counterName1;
            cdCounter1.CounterType = PerformanceCounterType.ElapsedTime;

            CounterCreationData cdCounter2 = new CounterCreationData();
            cdCounter2.CounterName = counterName2;
            cdCounter2.CounterType = PerformanceCounterType.NumberOfItems32;

            // 3. Add the CounterCreationData objects to the collection via Add method.
            ccdCollection.Add(cdCounter1);
            ccdCollection.Add(cdCounter2);
            
            //4. Create the category and pass the collection to it.
            PerformanceCounterCategory.Create(categoryName,
                    "Kevin's Performance Counters",
                PerformanceCounterCategoryType.SingleInstance, ccdCollection);
            // Return, rerun the application to make use of the new counters.
            return;

        }
        else
        {
            Console.WriteLine("Category exists - {0}", categoryName);
        }

        /***************************************************************************
         * The second time the code is run, the performance counters are used.     *
         ***************************************************************************/
        // Create the performance counter.
        PerformanceCounter PC1 = new PerformanceCounter(categoryName,
                                                       counterName1,
                                                       false);
        PerformanceCounter PC2 = new PerformanceCounter(categoryName,
                                                       counterName2,
                                                       false);

        // Initialize the counter.
        PC1.RawValue = Stopwatch.GetTimestamp();

        DateTime Start = DateTime.Now;

        // Loop for the samples.
        for (int j = 0; j < 100; j++)
        {
            // Output the values.
            if ((j % 10) == 9)
            {
                Console.WriteLine("NextValue() {0} = {1}", counterName1, PC1.NextValue().ToString());
                Console.WriteLine("NextValue() {0} = {1}", counterName2, PC2.NextValue().ToString());
                OutputSample(PC1.NextSample(), counterName1);
                OutputSample(PC2.NextSample(), counterName2);
            }

            // Reset the counter on every 20th iteration.
            if (j % 20 == 0)
            {
                PC1.RawValue = Stopwatch.GetTimestamp();
                PC2.RawValue = j;
                Start = DateTime.Now;
            }
            System.Threading.Thread.Sleep(50);
        }

        Console.WriteLine("Elapsed time = " + DateTime.Now.Subtract(Start).ToString());
    }

    private static void OutputSample(CounterSample s, string counterName)
    {
        Console.WriteLine("--------------------------------------------------");
        Console.WriteLine("Sample {0}", counterName);
        Console.WriteLine("   CounterType      = " + s.CounterType);
        Console.WriteLine("   RawValue         = " + s.RawValue);
        Console.WriteLine("   BaseValue        = " + s.BaseValue);
        Console.WriteLine("   CounterFrequency = " + s.CounterFrequency);
        Console.WriteLine("   CounterTimeStamp = " + s.CounterTimeStamp);
        Console.WriteLine("   TimeStamp        = " + s.TimeStamp);
        Console.WriteLine("--------------------------------------------------");
    }
}

Top



Event Logs

.Event Viewer for Performance Counter Deletion
Event Viewer Logging Performance Counter Deletion


Windows Event Logs

Windows Event Logs is a centralized logging mechanism which has been use on the Windows NT platform since its release in 1993. The logging mechanism was rewritten in the NT 6.0 kernel (Vista Server 2008) using an XML log-format and a log type to allow applications to more precisely log events. Event logs are organized into categories, with the predefined categories being:

  1. Application - Events are classified as error, warning, or information, depending on the severity of the event. An error is a significant problem, such as loss of data. A warning is an event that isn't necessarily significant, but might indicate a possible future problem. An information event describes the successful operation of a program, driver, or service.
  2. Security - These events are called audits and are described as successful or failed depending on the event, such as whether a user trying to log on to Windows was successful.
  3. Setup - Computers that are configured as domain controllers will have additional logs displayed here
  4. System - System events are logged by Windows and Windows system services, and are classified as error, warning, or information.
  5. Forwarded Events - These events are forwarded to this log by other computers.

Events contains id numbers specifying particular type of events, user, date and time, computer, keywords, operational code, and severity levels. There is also a wevutil.exe Windows Command Line Utility that allows the event logs to be queried, exported, archived and cleared.

.Windows Event Command Line Utility
Windows Event Command Line Utility


Creating Application Event Logs

Creating a custom event for an application can be accomplished with the EventLog class by calling the WriteEntry() method. Custom application events are usually written using "Application" category and a source name which uniquely identifies the application. The source name must be registered before using it. The program below registers a source name and creates an event under that name:

.Custom Event
Custom Event

using System;
using System.Diagnostics;
using System.Threading;

class Program
{
    public static void Main()
    {
        const string sourceName = "Kevin.Events";

        // Create the source, if it does not already exist.
        if (!EventLog.SourceExists(sourceName))
        {
            // An event log source should not be created and immediately used.
            // There is a latency time to enable the source, it should be created
            // prior to executing the application that uses the source.
            // Execute this sample a second time to use the new source.
            EventLog.CreateEventSource(sourceName, "Application");
            Console.WriteLine("CreatingEventSource");
            Console.WriteLine("Exiting, execute the application a second time to use the source.");
            // The source is created.  Exit the application to allow it to be registered.
            return;
        }

        // Create an EventLog instance and assign its source.
        EventLog.WriteEntry(sourceName, "Program started using admin password.", EventLogEntryType.Information,1234);
        Console.WriteLine("Message written to event log.");
    }
}

Top



Reference Articles

Top