.netCoders Contact Us
Search:

Tracing

Tracing refers to the monitoring of a running application for performance or diagnostic reasons. The .NET framework contains the System.Diagnostics namespace for just such a task. In this article, we cover instrumenting your application (adding trace statements), configuring listeners, and turning tracing on and off with trace switches.
Instrumentation
Instrumentation refers to adding tracing ability to your application, by adding trace code for example. By sprinkling trace messages throughout your application, you can identify bottlenecks and diagnose problems. Instrumentation can be done using two objects from the System.Diagnostics namespace: Trace and Debug. These objects support the same methods, listed below, with the difference being that by default, Debug statements are not compiled into the Release build, whereas Trace statements are. [Note: This can be changed with conditional attributes.]

The six methods supported by these two objects are listed below:

  Method Description
Assert Writes the specified text (or the Call Stack) if the specified condition is false
Fail Emits the specified error message
Write Writes the specified text
WriteIf Writes the specified text it the specified condition is true
WriteLine Writes the specified text and a carriage return
WriteLineIf Writes the specified text and a carriage return if the specified condition is true

Following are some examples of tracing code using the methods above.

Trace.Write("Submitting Order");
Trace.WriteLine("Processing Credit Card");
Trace.WriteIf(bCreditCardFailed == true, "Credit Card Failed");
Debug.Assert(bCreditCardProcessed == true, "Credit Card Not Processed");

Trace Listeners
The Trace and Debug objects contain a Listeners collection. These Listeners collect the output from the trace statements. There are three types of predefined listeners:

Class Description
DefaultTraceListener Writes trace statements to the OutputDebugString and the Debugger.Log method. In Visual Studio, these messages show up in the Output window. This listener is automatically included in the Listeners collection by default.
TextWriterTraceListener Writes messages to an instance of a Stream class, including output to the console or a file.
EventLogTraceListener Writes messages to the Event Log.

You can also write your own custom listener by inheriting from the TraceListener base class and overriding the methods above. The following code shows how to add some example listeners:

//Output to Console
Trace.Listeners.Add(new TextWriterTraceListener(Console.Out));

//Output to Event Log
Debug.Listeners.Add(new EventLogTraceListener());

//Output to File
Trace.Listeners.Add(new TextWriterTraceListener(@"c:\trace.log"));
There are a couple points about the Listeners collection that you should note. The first is that the Debug and Trace classes share the Listeners collection. If you register a listener with the Trace class, and call a method on the Debug class, the Listener will receive the message. The second point to note is that if you register the same Listener twice, it will receive the messages twice. The Listeners collection does not check to see if the Listener has already been registered. So, combining these two points, if you register the Listener with both the Trace and Debug classes, it will in effect be registered twice, and will receive messages from either the Debug class or the Trace class twice.

Trace Switches
Trace switches are status flags which you can use to filter tracing behavior. There are two kinds of trace switches: a BooleanSwitch, which is on/off, and a TraceSwitch, which has an enumeration of levels. For BooleanSwitches, check the Enabled property to determine if the switch is enabled and for TraceSwitches, use the Level property (or alternatively check each of the boolean properties TraceError, TraceWarning, TraceInfo, and TraceVerbose). Both of these switches inherit from the Switch class.

The TraceSwitch class has 5 levels, from the TraceLevel enumeration, as summarized in the table below. When you set the switch for a particular level, it includes all levels including and less than the indicated level. For instance, if you set a TraceSwitch's Level property to TraceLevel.Warning, both Warning and Error messages will be included.

Level Value
Off 0
Error 1
Warning 2
Info 3
Verbose 4

Trace switches can be configured through your application's .config file. When you instantiate a switch, the application automatically checks the configuration file for a matching named switch. This is useful because you do not need to recompile your application to enable logging. If there's a match, the configuration parameters are set.

Here is an example app.exe.config file that sets two switches, a Boolean Switch indicating whether to write out DatabaseMessages, and a TraceLevelSwitch with a level of Info (3):

<configuration>
    <system.diagnostics>
        <switches>
            <add name="DatabaseMessagesSwitch" value="1" />
            <add name="TraceLevelSwitch" value="3" />
        </switches>
    </system.diagnostics>
</configuration>

The matching code to instantiate the switches, and check them in your code, would look like this:

//Instantiate Switches
BooleanSwitch objDBSwitch = new BooleanSwitch("DatabaseMessagesSwitch", "Database Messages In Entire Application");<br>
TraceSwitch objGeneralSwitch = new TraceSwitch("TraceLevelSwitch", "Trace Level for Entire Application");

//Show Levels
Console.WriteLine("Boolean Switch Enabled: " + objDBSwitch.Enabled);
Console.WriteLine("Trace Level:" + objGeneralSwitch.Level);

//Use Switches
Trace.WriteLineIf(objDBSwitch.Enabled, "Creating Connection to Database");
Trace.WriteLineIf(objGeneralSwitch.TraceInfo, "Order Submitted");
So, to recap, the steps for using trace switches in your application are as follows:
  1. Create a BooleanSwitch or TraceSwitch
  2. Set the switch's configuration
  3. Test the switch's value in WriteIf statements

Example
The following console application example pulls together the pieces we have covered so far. We set up our configuration file with our switches, create our switches in the code, verify their settings, add a listener that writes out to the console, and then write some trace statements.

The full source code of the console application is as follows:

using System;
using System.Diagnostics;

public class TracingApp
{
    public static void Main()
    {
        Console.WriteLine("Starting");

        //Instantiate Switches
        BooleanSwitch objDBSwitch = new BooleanSwitch("DatabaseMessagesSwitch", "Database Messages In Entire Application");
        TraceSwitch objGeneralSwitch = new TraceSwitch("TraceLevelSwitch", "Trace Level for Entire Application");

        //Output to Console
        Trace.Listeners.Add(new TextWriterTraceListener(Console.Out));

        //Show Levels
        Console.WriteLine("Boolean Switch Enabled: " + objDBSwitch.Enabled);
        Console.WriteLine("Trace Level:" + objGeneralSwitch.Level);

        //Use Switches
        Trace.WriteLineIf(objDBSwitch.Enabled, "TRACE: Creating Connection to Database");
        Trace.WriteLineIf(objGeneralSwitch.TraceInfo, "TRACE: Order Submitted");
        Trace.Flush();
        
        Console.WriteLine("Finished");
    }
}

From a command line, compile this application:

csc /t:exe /d:TRACE example.cs
Type enter.exe at the command line to run your application. You should see the output of the trace messages on the console. If you change the configuration file so that the switch values are both 0 (off), and rerun the application, you won't see any trace messages.