Prizes & Awards
My Profile
Active Members
TodayLast 7 Days
more...
|
New Feature: Community Sites:
Create your own .NET community website and start earning from Google AdSense !
It's Free !
|
Trace Debugging in .Net
|
Trace Debugging Back in the bad old days of computer programming, before source-level debuggers and integrated development environments, about the only way to "see" inside your executing program was to add output messages in key places using Basic's PRINT statement or C's printf function. That worked reasonably well for console mode applications, but service and GUI applications required a little more work to create a log file on disk. Some programmers wrote code to pass debugging output to other systems via a serial or other communications port. Regardless of how we did it, we grumbled at the need to add these statements in conditional compilation blocks (if the language supported it), and to re-compile the program for debugging in order to enable tracing. Along came source level debuggers and all kinds of fancy development technology, and the art of program tracing fell out of vogue. In the early 1990's, it was common to meet a programmer who'd never used tracing. It wasn't a lost art, but its use became much less common; source level debuggers and IDEs made it so much easier to break into a program and see what was happening. There are disadvantages to depending on a source debugger, though. The program has to be compiled with debug information, and it has to be running on a developer's machine in order to diagnose the problems. As Web applications became more prevalent, it became increasingly necessary to have some way to diagnose an application while it was running in the production environment. Re-enter tracing. .NET Support for Tracing The .NET Framework has an integrated tracing mechanism that allows you to add trace output statements to your program and then selectively enable those statements at runtime by setting values in the application's configuration file. The Framework allows you to define your own classes that collect the trace information and send it to whatever output device you choose. Two very similar classes make use of trace output: · The Debug class is enabled when you compile with the DEBUG conditional enabled. In Visual Studio .NET, the DEBUG conditional is enabled by default when you compile a debug build. · The TRACE conditional is enabled by default in both debug and release builds, and enables the use of tracing output through the Trace class. In theory, you would use Debug output for messages that you only want to view during debugging--that is, during program development and never in production. You would use Trace output statements for messages that you might want to view after the program has been placed in production. In practice, I've found it much more convenient to ignore Debug and use only Trace output, reserving the Verbose trace level (described below) for debug-level messages. Although the following discussion describes only the Trace class, the discussion applies equally to the Debug class unless otherwise noted. The process of adding trace statements to your program is called instrumentation. The Trace class defines six different output methods: · Assert: Outputs the specified text (or the call stack if no text is specified) if the condition supplied in the first argument evaluates to false. · Fail: Outputs the specified text (or the call stack if no text is specified). This method is typically used inside an error-handling code block. Write Outputs the specified text. · WriteIf: Outputs the specified text if the condition supplied in the first argument evaluates to true. WriteLine Outputs the specified text and a carriage return. · WriteLineIf Outputs the specified text and a carriage return, only if the condition supplied in the first argument evaluates to true. The Assert and Fail methods not only output messages, but also pop up dialog boxes that display an error message and allow you to Abort the program, Retry the operation, or Ignore the error. You probably do not want to use Assert and Fail in programs that are supposed to run unattended. Where Does The Trace Output Go? The Trace class routes messages to listeners: classes that are designed to accept Trace messages and send them to the appropriate output device. The Trace.Listeners collection contains a list of the listeners that are registered with the system. Calling any of the Trace output methods will send a message to all of the registered listeners. A class called DebugTraceListener is automatically added to the Listeners collection, and routes messages to the OutputDebugString API function. If you want trace messages to be routed somewhere else (the console perhaps, or a log file), you have to create an instance of a TraceListener class and add it to the Listeners collection. The .NET Framework defines a class, TextWriterTraceListener that will direct trace output to a pre-defined TextWriter or Stream, or to a named file. Another class, EventLogTraceListener will direct output to the system event log. If you want trace output directed to some other device, you will have to create your own class that inherits from TraceListener. Trace Debugging The .NET Framework defines a class, TextWriterTraceListener that will direct trace output to a pre-defined TextWriter or Stream, or to a named file. Another class, EventLogTraceListener will direct output to the system event log. If you want trace output directed to some other device, you will have to create your own class that inherits from TraceListener. Using Trace Using the Trace class is very simple. The class is located in the System.Diagnostics namespace, so you need to include a reference (using in C#, or Imports) to that namespace in your program. Inside your main program, create an instance of a TraceListener class, and add Trace output statements to your code. The program below provides a very simple example that outputs tracing information to a file called "traceout.txt". using System; using System.Diagnostics; namespace tracecon_cs { class Class1 { [STAThread] static void Main(string[] args) { // create the listener and add it to the Listeners collection TextWriterTraceListener tracer = new TextWriterTraceListener("traceout.txt"); try { Trace.Listeners.Add(tracer); Trace.WriteLine("Starting program"); Trace.Indent(); FirstOperation(false); SecondOperation(true); Trace.Unindent(); Trace.WriteLine("Program complete"); } finally { // Free unmanaged resources tracer.Dispose(); } } static void FirstOperation(bool isGood) { Trace.WriteLine("First operation"); Trace.Indent(); try { Trace.WriteLineIf(isGood, "isGood is true!"); Trace.WriteLine("Detail goes here."); } finally { Trace.Unindent(); } } static void SecondOperation(bool isGood) { Trace.WriteLine("Second operation"); Trace.Indent(); try { Trace.WriteLineIf(isGood, "isGood is true!"); Trace.WriteLine("Detail goes here."); } finally { Trace.Unindent(); } } } } The program starts by creating a trace listener and adding it to the Listeners collection. It then outputs a message and indents the output. The FirstOperation and SecondOperation methods each output a message and also indent the output. They use the WriteLineIf method to output a conditional message and then un-indent the output before continuing. The indentation methods in the Trace class are very handy ways to organize your output. The default indentation level is four spaces, but you can change that by setting the IndentSize property. You can also set the indentation level directly by setting IndentLevel. I used a try..finally block to ensure that the indentation level gets reset correctly at the end of each method, even if an exception occurs somewhere in the method. The small performance overhead is well worth the price when it comes time to reading trace output. A sample of the program's output is shown here: Trace output from the sample program Starting program First operation Detail goes here Second operation isGood is true! Detail goes here Program complete Controlling Trace Output with Trace Switches One of the nicest things about .NET Trace is the ability to enable or disable tracing by modifying the application configuration file. Whereas it's always been possible to write applications that selectively enable tracing, that infrastructure had to be built custom for each program. In contrast, the .NET Framework has such support built in. It's much more convenient than the most common method used in the past, which was to recompile the program with trace disabled or set to a different level. The mechanism used to accomplish this in .NET is the trace switch. There are two parts to using trace switches to control trace output: he configuration file and the code that uses the configuration file information to control trace output. I'll consider each separately. Using Trace Switches in Code The .NET Framework defined two different kinds of trace switches: BooleanSwitch, which has a simple on or off value; and TraceSwitch, which can take on values of Off, Error, Error, Warning, and Verbose. If you want to conditionally enable tracing, you must create a TraceSwitch or BooleanSwitch in your code and test its value. The switch's constructor code reads the relevant information from the configuration file and sets the switch's value appropriately. From there, your code can test the value of the switch to determine whether or not to output a trace message. The code below creates a BooleanSwitch that enables or disables trace output, and then tests the value of that switch. // Create switch that controls message tracing BooleanSwitch MessageTrace = new BooleanSwitch("MessageTrace", "Messaging Module"); // output message only if MessageTrace is enabled Trace.WriteLineIf(MessageTrace.Enabled, "Got a message"); // output message only if MessageTrace is enabled if (MessageTrace.Enabled) Trace.WriteLine("Got a message"); The constructor call initializes the MessageTrace switch value from the corresponding section in the configuration file which is described below. Note that there are two ways to use the value of the switch in controlling trace output. The first way uses the Trace.WriteLineIf method to test the value and output a message if the value is true. The other way is with a standard conditional statement. There are good reasons for using both methods, which will be described below. The TraceSwitch class gives you much more flexibility, because you can set the level of information that you want. In normal program operation, for example, you might want only error and warning messages to be output to the trace log. If you're trying to diagnose a problem, you would also include information and possibly even verbose messages. Or you might want to disable all trace output for any one of a variety of reasons. The Level property holds a value that indicates the current trace level setting. This property can take on one of the values of the TraceLevel enumeration: Off, Error, Warning, Info, and Verbose. Note that these settings are arranged in increasing order of verbosity. A higher Level setting automatically enables all lower settings. That is, if the Level property is set to TraceLevel.Info, then the TraceInfo, TraceWarning, and TraceError properties are automatically set to true, and TraceVerbose is set to false. The code below shows how to use a TraceSwitch to selectively enable certain trace messages.
// Create switch that controls program flow tracing TraceSwitch ProgramFlowTrace = new TraceSwitch("ProgramTrace", "Program flow"); // Output verbose message only if verbose enabled Trace.WriteLineIf(ProgramFlowTrace.TraceVerbose, "Program complete"); // Output verbose message only if verbose enabled if (ProgramFlowTrace.TraceVerbose) Trace.WriteLine("Starting program"); Again, the constructor initializes the TraceSwitch's value from the program configuration file. If you put the above code in a program and tried to run it, you will find that no trace information is output. The reason is that the trace switches were not defined in the application configuration file. If the BooleanSwitch constructor cannot locate the switch configuration, the Enabled switch is set to false. Similarly, if no configuration exists for a TraceSwitch, the Level property is set to TraceLevel.Off, and the TraceError, TraceWarning, TraceInfo, and TraceVerbose properties are all set to false.
|
Responses
|
No responses found. Be the first to respond and make money from revenue sharing program.
|
|