Sunday, March 20, 2011

C# - Debugging C-Sharp Applications - Part 3 - Tracing


1. Introduction


To Read the other topic below check the C# Label.
Part 1: Debugging Basic
Part 2: Debugging Windows
Part 3: Tracing [You are Here]

In this third and last part of the debugging, I will show you how do you get debugging information in the form trace. This will be helpful when the application deployed in the customer place. It is not possible to do the debugging, as we usually do not keep the source code in the deployed environment.

Dot net framework provided a facility to do the tracing on the client end and you can the information from them in the form of a flat text file or as an "Event Log" file. In this article, we will change the existing sample used in the previous two parts to enable the logging in the flat text file as well as in the windows event logger.

Below is the Screenshot of changed application. I will explain the code change when the article progresses.





2. Trace source, Trace switch, and Trace Listener


To understand this easily have a look at the below diagram:




The "Trace Source" is the place from where the information is pushed out. Usually, this is your application, which pumps the trace data for recording purpose. The framework has a class for this trace source and its member function will actually push the trace data.

"Trace Listeners" are recording media, which takes the pumped trace information and stores it. The listener can be a flat text file, an XML file, Console output window or a windows event logger. These listeners are actually attached to the Trace source. Also, note that multiple listeners can be attached to a single trace source.

"Trace Switch" is the intermediate common filter between all the listeners attached to single Trace source. So trace switch can perform the trace filter. A trace source can push a variety of trace data say Information messages, Critical messages, Error Message etc., So here you can set the switch to skip all the trace type except Critical messages.

There is one more filtering available in the listener itself. In our demo, we are going to use the filter in the trace switch level to reduce the length of this article. But, I will give a brief idea on how do you put second level filter also. This filter is specific for the individual listeners.

OK. Let us start to enables these traces in our sample that we used in the previous two parts of this debugging article.

3. Add required controls


Add two radio button and check boxes. In the above screenshot, it looks like a toggle button. To set the required properties for the controls open the attached sample and set the properties that show in bold in the property window.

OK. Let me start explaining the code that enables the tracing for our sample.  If you do not want the explanation you can directly go to the attached sample and start exploring the code.

Search Tag: //Trace 0

Before you move to the next section, have a brief look at the search result, which will give a high-level idea of what we are going to do.

4. Default Trace Listener


The default tracing takes the debugger output window as the trace listener. First, let me show you the trace output on the debugger output window. Then we will move to the text Log file and Window event logger.

1) First provide the required name space for using the Tracing.

//Trace 007: Include diagnosis Namespace
using System.Diagnostics;

2) Introduce member variables for Trace Source and Trace Switch

//Trace 008 : Declare the Trace Source, Trace Switch
TraceSource TrcSource;
SourceSwitch TrcSwitch;

3) Create the Trace Source and Trace Switch objects. Set no filtering in the Switch object using the property Level. Then attach the switch to the Trace Source. That’s all. What have we done? As already stated the default listener is debugger output window. The switch does not to any filtering is attached to the Trace Source which has the only listener as Output window. So all the Trace Message pumped will be displayed on the output window. Below is the code:

//Trace 009: Create the Trace Source,Switch,
TrcSource = new TraceSource("Trace Source");
TrcSwitch = new SourceSwitch("Trace Switch");

//Trace 010: Set the Switch Levels
TrcSwitch.Level = SourceLevels.All;

//Trace 011: Set the above-created switch to Trace Source
TrcSource.Switch = TrcSwitch;

4) Let us start pumping a debug message. The TraceInformation function will pump the messages to all listener attached to the source (Now it is only the output window). The sample pumps the Information message to output window when a prime number is detected. Below is the code:

//Trace 012: Write the Trace to the Output Window
String info = string.Format("Prime Number Detected: {0}", i);
TrcSource.TraceInformation(info);
TrcSource.Flush();

5) Now run the sample and provide 24 as the input, right click on the output window and clear all the existing texts. Then click Get prime sum button. Look at the output window to see the results. The screen shot is given below:



5. Text File Listener



When the toggle button File Log is clicked the text trace logging will be enabled. The text log enabled form is shown below:


When you click the same button again the log will be disabled. So we will create the text log listener and attach it to the Trace source when the check box is in the checked state (Here, toggled on) and remove the text log listener when the check box is in the un-checked state (Here, toggled off).

When the check box is checked, create a "TextWriterTraceListener" object by specifying the name (Useful when you are removing it) and the filename with path in which the pumped messages are written. Using the "TraceOutputOptions Property" set the time and thread are as required. Refer MSDN for more output options. Once the listener is ready add it to the trace source. At this stage, we have two listeners for the trace source. So the message pumped in the search tag Trace 012 will now go to C:\Trace.log in addition to the debugger output window.

if (chkLogFileTrace.CheckState  == CheckState.Checked)
{
    //Trace 013: Create text file Listener and set the output options
    TextWriterTraceListener txtListen;
    txtListen = new TextWriterTraceListener(@"C:\Trace.log", "Text Trace Listener");
    txtListen.TraceOutputOptions = TraceOptions.Timestamp | TraceOptions.ThreadId ;

    //Trace 014: Add the Listener to the TraceSource
    TrcSource.Listeners.Add(txtListen);
}

And remove the listener when the check box is in the unchecked state. Note that we referred the Listener name that we gave in the constructor when we are creating it. Below is the code:

else
{
    //Trace 015: When the Toggle is off, remove the listener
    TrcSource.Listeners.Remove("Text Trace Listener");
}

Run the Sample now to check you are getting the pumped message in both the listener when the File Log is toggled on.

6. Event log Listener


Event logger is the windows tool that hides the underlying file and shows you the log information in a UI. However, you can copy the content to a new file. Right, click on your “My computer” icon and select Manage. In the left pane expand the Event Viewer and click on the System node.  The right side pane now shows all the events logged. Double-clicking an event display the detailed information about the event. Below is the screen shot:




Now we will make our sample to write the debugging content here.

The code logic is similar to the one explained in the previous section. In place of creating TextWriterTraceListener we will create the EventLogListener. Below is the code:

if (chkEventLog.CheckState == CheckState.Checked)
{
    //Trace 016: Create the event log listener and add it to the Listener collection
    //of Trace Source.
    EventLogTraceListener evtListener;
    evtListener = new EventLogTraceListener("Event Log Listener");
    TrcSource.Listeners.Add(evtListener);
}
else
{
    //Trace 017: Remove the listener
    TrcSource.Listeners.Remove("Event Log Listener");
}

Test the sample by enabling the Event Log toggle now. Look at the application event log, as the log will not enter into system event log.

7. Set the Filters at the Source Switch


Do you remember? In the constructor, we asked the Trace Switch to allow all the information to the listeners attached to the Trace Source. Now by the radio button, we will do the filtering of the log data. The code is straight forward and below is the code:

private void RadError_CheckedChanged(object sender, EventArgs e)
{
    //Trace 018: Filter the Trace
    TrcSwitch.Level = SourceLevels.Error;
}

private void RadInfo_CheckedChanged(object sender, EventArgs e)
{
    //Trace 019: Filter the Trace
    TrcSwitch.Level = SourceLevels.Information;
}


Below is the screen shot was taken from the MSDN about the trace levels and what it filters.



All right. At present, we are writing only the information messages. How do I check the filters are working fine? Well. Add the below piece of code:

//Trace 016: Log the Trace Errors
TrcSource.TraceEvent(TraceEventType.Error, 3, "Invalid Number Specified");
TrcSource.Flush();

Note that this time we are not making a call to the Trace Source function TraceInformation. We are making a call to the TraceEvent function and setting the TraceEventType as Error. Once again refer the MSDN for other enumeration values. To test provide the text in the input and click the Get Prime Sum button.

To perform the Secondary level ( that is level2 ) filtering at the individual listener make use the filter property of the individual listener. The property takes same enumeration values shown above.

See you in the next article. Bye.

Note: The sample is created using the VS2005 IDE. If you have the latest one say yes to the conversion dialog.

Source Code : Download


No comments:

Post a Comment

Leave your comment(s) here.

Like this site? Tell it to your Firend :)