Playing .NET Doctor: Diagnose Application Hiccups with .NET Classes

This month's .NET Nuts & Bolts covers the different .NET Framework options for diagnosing issues within your applications. It touches on a couple of the classes available in the System.Diagnostics namespace and the functionality they provide for debugging and tracking an application's execution path.

Classic Debugging

For those who have been programming long enough, you probably recall a time prior to advanced development tools such as Visual Studio when diagnosing application issues was more complicated. It was a time when you could not step through your code, nor determine exactly where the application was in its execution. One of the techniques often used then was embedding a series of print statements within the code to display the output at any given point in time and track the application logic flow. These statements had to be removed prior to application deployment, but one or two invariably would remain and cause embarrassment when displayed. Microsoft has included the Debug and Trace classes in the. NET Framework to help with this style of debugging and to avoid some of the pitfalls.

Modern Day Debugging

The System.Diagnostics.Debug class provides a set of methods and properties for debugging code. The methods in the Debug class offer a number of very useful options. One of them is that it displays the messages only when the code is compiled as a debug build. (Note: the debug build option is compiler specific. In C#, it is /d:DEBUG. In Visual Basic .NET, it is /d:DEBUG=True. I prefer just to let Visual Studio control it for me by changing the mode through Configuration Manager.) The result is that the debug statements can remain in the code at all times and can be easily excluded from production code simply by compiling without the debug option.

System.Diagnostics.Debug Sample Code

The following are some of the more common methods available in the Debug class:

  • Assert—Displays a dialog message if the condition being checked is false
  • Write/WriteLine—Writes information with or without a new line
  • WriteIf/WriteLineIf—Conditionally writes information with or without a new line
  • Indent/Unindent—Indents or unindents the debugging output

The following sample code demonstrates all the methods listed above. The Assert statement results in a dialog box (Figure 1), while the others result in output being displayed in the Debug section of the Output window (Figure 2):

System.Diagnostics.Debug.Assert(false, "Message",
"Detail message to show in the debug window.");
Random rand = new Random(100);
System.Diagnostics.Debug.WriteLine("First random numbeCr: "
   + rand.Next().ToString());
System.Diagnostics.Debug.WriteLineIf(rand.Next()%2 == 0,
   "Even random number generated.");

Figure 1: Assert Output

Figure 2: Debug Output

Using a Configuration File

Another handy System.Diagnostics.Debug option is the ability to control the Debug class through the application's configuration file (web.config or app.config). A <system.diagnostics> element allows control of the behavior. The following example sets the size of indentions when printing and controls whether or not the Assert method generates a dialog:

   <trace autoflush="true" indentsize="7" />
   <assert assertuienabled="false" />

When you add the items above to your configuration file and execute it, you see slightly different output. Instead of the dialog from Figure 2, the output displays larger indention than the default (see Figure 3).

Figure 3: Control Debug Through Configuration


The System.Diagnostics.Trace class provides a set of methods and properties for tracing the execution of code. These statements are often handy when you deal with complex logic and aren't sure where the code is at in execution. Trace statements are very similar to Debug statements. Trace exposes the same methods and functionality as the Debug class offers. The difference is that Trace is enabled by default for both debug and release builds. Thus, Trace statements can be used to trace the execution of code when it has been released to production. This can be handy for finding additional information on how your application is behaving in production.

System.Diagnostics.Trace Sample Code

Because the Debug and Trace classes have the same methods, I just changed the sample code above to use Trace instead of debug. The output will be the same, so I won't bother to include the figures again:

System.Diagnostics.Trace.Assert(false, "Message",
   "Detail message to show in the debug window.");
Random rand = new Random(100);
System.Diagnostics.Trace.WriteLine("First random number: " +
System.Diagnostics.Trace.WriteLineIf(rand.Next()%2 == 0,
   "Even random number generated.");


The previous examples are all well and good as long as you're running the code in Visual Studio so that you can see the output window. This may work when debugging on the local machine, but it won't be an option when the application is in production. Thus, you need to be able to control where the output from Debug and Trace statements is written. This is done through what is known as a listener. A listener does just as its name implies: It listens for debug or trace output and then behaves accordingly. You can use a TextWriterTraceListener class to write Debug or Trace output to a TextWriter or a Stream, which can result in output being written to the console, a file, and so forth. You also can use an EventLogTraceListener to send messages to the Windows Event Log. Finally, you have an abstract TraceListener class that you can implement to create custom listeners for monitoring debug and trace output.

Listener Sample Code

The following sample code extends the previously given example code to have the output written to the console window rather than the Output window of the debugger (see Figure 4). It also demonstrates how to send the output just to the console:

// Add a listener to send output to the console
System.Diagnostics.TextWriterTraceListener writer = new

// Remove the default listener and only send to the console

// Output debug messages
System.Diagnostics.Debug.Assert(false, "Message",
   "Detail message to show in the debug window.");
Random rand = new Random(100);
System.Diagnostics.Debug.WriteLine("First random number: "
   + rand.Next().ToString());
System.Diagnostics.Debug.WriteLineIf(rand.Next()%2 == 0,
   "Even random number generated.");

Figure 4: Console Output

Possible Enhancements

Beyond the basics of using the Debug and Trace classes covered here, consider the following enhancements:

  • Set up a listener through the configuration file—Rather than set up the listener via code, experiment with controlling it through the <listeners> element in the application configuration file.
  • Set up the EventLogTraceListener—Configure the listener to output to the event log.
  • Implement a custom Listener—Create additional listeners to log to alternative locations such as a database.

Future Columns

The topic of the next column is yet to be determined. If you have something in particular that you would like .NET Nuts & Bolts to explain, contact me at

About the Author

Mark Strawmyer

Mark Strawmyer is a Senior Architect of .NET applications for large and mid-size organizations. He specializes in architecture, design and development of Microsoft-based solutions. Mark was honored to be named a Microsoft MVP for application development with C# for the fifth year in a row. You can reach Mark at


  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Protecting business operations means shifting the priorities around availability from disaster recovery to business continuity. Enterprises are shifting their focus from recovery from a disaster to preventing the disaster in the first place. With this change in mindset, disaster recovery is no longer the first line of defense; the organizations with a smarter business continuity practice are less impacted when disasters strike. This SmartSelect will provide insight to help guide your enterprise toward better …

  • In support of their business continuity and disaster recovery plans, many midsized companies endeavor to avoid putting all their eggs in one basket. Understanding the critical role of last-mile connectivity and always available Internet access for their enterprises, savvy firms utilize redundant connections from multiple service providers. Despite the good intentions, their Internet connectivity risk may still be in a single basket. That is because internet service providers (ISPs) and competitive local …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds