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:
Trace.Listeners.Add(new
TextWriterTraceListener(Console.Out));
Debug.Listeners.Add(new
EventLogTraceListener());
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:
BooleanSwitch
objDBSwitch
=
new
BooleanSwitch("DatabaseMessagesSwitch",
"Database Messages In Entire Application");<br>
TraceSwitch
objGeneralSwitch
=
new
TraceSwitch("TraceLevelSwitch",
"Trace Level for Entire Application");
Console.WriteLine("Boolean
Switch Enabled: "
+
objDBSwitch.Enabled);
Console.WriteLine("Trace
Level:"
+
objGeneralSwitch.Level);
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:
-
Create a BooleanSwitch or TraceSwitch
-
Set the switch's configuration
-
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");
BooleanSwitch
objDBSwitch
=
new
BooleanSwitch("DatabaseMessagesSwitch",
"Database Messages In Entire Application");
TraceSwitch
objGeneralSwitch
=
new
TraceSwitch("TraceLevelSwitch",
"Trace Level for Entire Application");
Trace.Listeners.Add(new
TextWriterTraceListener(Console.Out));
Console.WriteLine("Boolean Switch Enabled: "
+
objDBSwitch.Enabled);
Console.WriteLine("Trace Level:"
+
objGeneralSwitch.Level);
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.
