Thursday, September 02, 2010
Search:
Article
48Hrs Help PAQ's
FAQ's
Download
Books
MS Alerts
Write to Us
Feedback
Link




a) Why use .NET My Services?
b) What are .NET My Services?

Get This Blog via Email:


Email This Feed Using Squeet




Debugging and Tracing in ASP.NET


By Utpal Chakraborty
January 04, 2003
Page is Viewed 16185 times


  
ASP.NET

Before web development was popular and most applications were stand alone programs debugging strategies depended a lot on how and where these were being developed. When developing in C/C++ printf(...) was a favorite debugging tool on both UNIX and DOS environments for programmers. That changed when Integrated Development Environments - IDEs - like Borland's (now Inprise) Turbo C++ and Microsoft's Visual Studio came along. These allowed stepping through code and watching local and global variables in a GUI as each line of code was executed. Suddenly, debugging was much more efficient. All that changed once again with the web-programming model. Debugging was once again much harder and reduced to print statements. Server side debugging before Visual Studio .NET was never up to the mark. Even with Visual Studio .NET server side debugging still does not have all the bells and whistles that the older IDEs had while developing stand-alone applications. On the brighter side the .NET framework comes with an enhanced library to aid in debugging and performance monitoring that makes debugging web applications easier than before.

In this article we will review these classes and see how to use them. Although, we specifically target web applications in this article most of all that we will discuss applies to other programming models (like standalone programs) too. For now, we will focus on debugging and tracing and discuss event logging and performance monitoring in a later article. The code examples in this article will use C# but the ideas are language independent and can be used in any language that can be used to program in .NET framework.

The System.Diagnostics namespace has almost all the classes we need for debugging and tracing. All of these classes can be subdivided into five logical groups. The first group of classes is the debugging helper classes that provide useful methods to write code that is easy to debug and maintain. The second group of classes provides tracing ability. Using these classes we are able to write trace data into many different tracing systems simultaneously. The third group of classes enables interaction with the Windows event log. Using these classes it is possible to read and write to the standard event logs (Application, Security, System) defined in Windows. It is also possible to create new logs. The fourth group of classes provides the ability to do performance monitoring. The fifth and last group provide a set of classes to obtain process information.

Debugging Helper Classes


These classes provide the basic debugging features. The following sections describe each of these classes in detail.

System.Diagonostics.ConditionalAttribute

Attaching metadata to code using attributes is a great feature in .NET. We can use this feature to write code that is conditionally compiled and invoked during program execution. Earlier, this was done in C/C++ using #ifdef statements. While this method can still be used in C# it leads to unclean code since the code gets sprinkled with preprocessor directives. The Java way of doing things is slightly better than C/C++. In Java, debugging code is put within an if-else block as shown below.

publicstaticboolean debug = true;

if(debug)
{
MyDebugMethod(); // debugging action here...
}
The Java compiler is usually smart enough not to compile the call to the debugging method and not include it in the byte code when the debug variable is set to false. However, all that has been accomplished is the replacement of a preprocessor directive of C/C++ with an if statement. Not a big change.

The System.Diagonostics.ConditionalAttribute attribute provides a much cleaner way of writing debugging code. Once methods that are exclusively used for debugging have been defined they are marked with the conditional attribute as shown below.

[Conditional("DEBUG")]
publicvoid MyDebugMethod ()
{
// debugging code here...
}

When the debug method needs to be called it can be called just like any other method. The call is compiled by the C# compiler only if the compiler directive DEBUG has been defined. This entirely localizes the debugging nature of the method instead of the code globally being aware it. Thus, the code that uses this debugging method needs to do no special handling (like preprocessor directives or if statement). Usually, in Visual Studio .NET DEBUG and TRACE are both defined when the debug build configuration is chosen. If it is necessary to define these explicitly including the following statement at the top of any file can always do it.

#define DEBUG

System.Diagonostics.BooleanSwitch

The use of conditional attributes is fine when we need to do debugging during application development. However, it does not allow us to turn debugging on or off during execution time. To have such dynamic control we need to read in some boolean value at run time and have our debugging methods called depending on the variable. While this can be custom coded very simply, .NET provides a class, System.Diagonostics.BooleanSwitch, specifically tailored for reading boolean variables from configuration files. In the case of ASP.NET the web.config file of the application should contain the definition and the value of the switch. An example where a switch named “debugSwitch” is defined is shown below. {In the case of stand-alone applications this switch should be defined in the applications configuration file.

<configuration>

...

<system.diagnostics>
<switches>
<add name="DebugSwitch" value="1" />
</switches>
</system.diagnostics>
</configuration>

Once the switch has been defined in the configuration it can be declared in the application as follows.

publicstatic BooleanSwitch debug = new BooleanSwitch("DebugSwitch", "Debug Switch");

It is a good idea to make these switches static so that they are read in only once. After they are declared debugging code can be written as shown below

if(debug.Enabled)
{
// debugging action here...
}

In this way debugging can be turned on and off without any recompilation. The only slight penalty is that a boolean check is always performed irrespective of whether debugging is on or off.

System.Diagostics.Debug

This class provides methods for writing debugging output and validating assertions. It has an Assert and Fail method with multiple signatures to suit different situations. The methods of this class are defined with the Conditional attribute discussed earlier. So the compiler variable DEBUG must be defined for these methods to be called. In Visual Studio .NET DEBUG is defined by default during debug builds. Thus, the use of the methods of this class does not affect performance or size of release builds.

When debugging output is written using the four different Write methods in this class they go to all the listeners that are registered with this class. All listeners that can trace debug output inherit from TraceListener, which is an abstract class. There are three concrete listeners that inherit TraceListener. The first listener, which is always registered by default, is the DefaultTraceListener. This prints the output to the debugger log during debugging. The second concrete listener is EventLogTraceListener. This directs its output to an event log. The third concrete listener is the TextWriterTraceListener. This prints its output to a text file.
When controlling the debugging trace with a BooleanSwitch we have two options. One is to use a if-else statement as shown below.
if(traceflag.Enabled)
{
Debug.Write("Some debugging ouput");
}

The other is to use the provided WriteIf methods of the Debug class.

Debug.WriteIf(debug.Enabled, "Some debugging ouput");

Given these two options it is always better to use the if-else structure for performance reasons. When the traceflag is disabled the call to Write is never performed. However, in the second construct the boolean check and the call is always performed. Thus, for all practical purposes the Write methods should be used exclusively.

Setting the appropriate properties of the Debug class can control the format of the debugging output. While all properties and listeners can be added in code it is best that they are done in the config file. This allows for changes without recompilation. The example config file shown above can be modified to add a text file log and fix some formatting options as shown below.

<configuration>

...

<system.diagnostics>
<switches>
<add name="debugSwitch" value="1" />
</switches>
<debug autoflush="true" indentsize="4">
<listeners>
<add name="textListener" type="System.Diagnostics.TextWriterTraceListener,System" initializeData="c:\Debug.log" />
</listeners>
</debug>
</system.diagnostics>
</configuration>

System.Diagnostics.StackTrace

Many times it is helpful to print out the call stack. This class provides the relevant functionality. Unlike Java where a stack trace can only be obtained for an exception by calling the printStackTrace method, the .NET framework provides the ability to construct a stack trace anywhere in the code by constructing a new instance of the StackTrace object. The call stack pertaining to an exception can also be generated using the appropriate constructor. Once the StackTrace object has been created the content can be printed out for observation.

A StackTrace object is essentially a collection of System.Diagnostics.StackFrame objects. Each StackFrame object contains information about one call. The StackTrace object exposes this collection using the FrameCount property and the GetFrame method.

The following code prints out the call stack using the StackTrace and StackFrame class.

StackTrace st = new System.Diagnostics.StackTrace(true);

for(int i = 0; i < st.FrameCount; i ++)
{
StackFrame sf = st.GetFrame(i);
Debug.WriteLine(" File: " + sf.GetFileName() +
" Line: " + sf.GetFileLineNumber() +
" Method: " + sf.GetMethod());
}


Tracing

The most important class for tracing is System.Diagostics.Trace. The Trace class has the same methods and properties as the Debug class except that its methods have the Conditional attribute of TRACE. In Visual Studio .NET TRACE is defined in both debug and release builds by default. So the methods of the Trace class are always executed by default. So this class should be used only when we always want the output.

As with Debug trace output goes to all listeners that are registered with the Trace class. The same listeners that are used for Debug can be used for Trace.

The only other difference is that while Debug has a binary nature of either being on or off Trace should be used in a multilevel hierarchy. To do this a switch called TraceSwitch is defined. This is similar to the BooleanSwitch class except that it has a level property that can have five values defined in the TraceLevel enumeration. Combined with the TraceSwitch class Trace can be used to provide flexible tracing of applications as shown below.

// Create a TraceSwitch.
static TraceSwitch traceSwitch = new TraceSwitch("trace", "Application");

staticpublicvoid TraceOutput()
{
// Verbose Tracing.
if(traceSwitch.TraceVerbose)
{
Trace.Write("Trace Output - Verbose");
}

// Error Tracing
if(traceSwitch.TraceError)
{
Trace.WriteLine("Trace Output - Error");
}
}

As always, though the switches, levels and listeners can be defined in code, they should be defined in the applications config file so that they can be changed whenever necessary without recompilation.


Conclusion


The System.Diagnostics namespace provides useful classes for debugging applications. These become important specifically while writing server side applications where IDE debuggers are difficult to use. These classes provide extensive debugging, tracing, event logging and performance monitoring features.

In this article we reviewed the debugging and tracing abilities. Event logging and performance monitoring will be described in the next article.



Utpal Chakraborty is Manager, Software Engineering at Organic (http://www.organic.com). He has extensive experience in developing enterprise applications using Microsoft and non-Microsoft technologies. He can be reached at uchakraborty@organic.com

Microsoft:
Microsoft largest software production company. Listed in Nasdaq as msft.
.NET:
The .NET Framework is a new computing platform that simplifies application development in the highly distributed environment of the Internet.
ASP.NET:
A set of technologies in the Microsoft .NET Framework for building Web applications and XML Web services. ASP.NET pages execute on the server and generate markup (such as HTML, WML, or XML) that is sent to a desktop or mobile browser. ASP.NET pages use a compiled, event-driven programming model that improves performance and enables the separation of application logic and user interface. ASP.NET pages and XML Web services files created using ASP.NET contain server-side (rather than client-side) logic written in Visual Basic .NET, C# .NET, or any .NET-compatible language. Web applications and XML Web services take advantage of the features of the common language runtime, such as type safety, inheritance, language interoperability, versioning, and integrated security.
Attribute:
A descriptive declaration that annotates programming elements such as types, fields, methods, and properties. Attributes are saved with the metadata of a .NET Framework file and can be used to describe code to the common language runtime or to affect application behavior at run time.
C#:
A new programming language designed for building enterprise applications that run on the .NET Framework. C#, which is an evolution of C and C++, is type safe and object oriented. Because it is compiled as managed code, it benefits from the services of the common language runtime, such as language interoperability, security, and garbage collection.
Class:
A reference type that encapsulates data (constants and fields) and behavior (methods, properties, indexers, events, operators, instance constructors, static constructors, and destructors), and can contain nested types. Class types support inheritance, a mechanism whereby a derived class can extend and specialize a base class. See also: encapsulation, indexer, property, reference type.
Namespace:
A logical naming scheme for grouping related types. The .NET Framework uses a hierarchical naming scheme for grouping types into logical categories of related functionality, such as the ASP.NET technology or remoting functionality. Design tools can use namespaces to make it easier for developers to browse and reference types in their code. A single assembly can contain types whose hierarchical names have different namespace roots, and a logical namespace root can span multiple assemblies. In the .NET Framework, a namespace is a logical design-time naming convenience, whereas an assembly establishes the name scope for types at run time. See also: assembly.
.NET Framework:
An integral Windows component that supports building and running the next generation of applications and XML Web services. It provides a highly productive, standards-based, multilanguage environment for integrating existing investments with next generation applications and services, as well as the agility to solve the challenges of deployment and operation of Internet-scale applications. The .NET Framework consists of three main parts: the common language runtime, a hierarchical set of unified class libraries, and a componentized version of ASP called ASP.NET. See also: ASP.NET, common language runtime, .NET Framework class library.
.NET Force is optimised for Microsoft Internet Explorer 5 browsers.
Copyright © 2004 .NET Force.
Terms and Condition. All rights reserved.