This article has been
excerpted from book "The Complete Visual C# Programmer's Guide" from the Authors
of C# Corner.
Among the many diagnostic classes the .NET Framework provides are the Debug and
Trace classes. The Debug class helps us debug code, and the Trace class helps us
trace the execution of code. The Debug class is intended for debug builds, and
the Trace class is used for release builds.
Table 21.3 describe the members of the Debug and Trace classes.
Table 21.3: Debug/Trace Class Members
You must define the DEBUG and TRACE compiler directives to activate debugging
and tracing respectively. If you neglect to define either of these directives,
Trace or Debug calls will be ignored during compilation. You can define these in
Visual Studio .NET by choosing Properties from the Project menu.
It is generally recommended that you define TRACE in your code, since at a
minimum, end users and administrators may turn on lightweight diagnostic tracing
out in the field when they encounter a problem related to the application.
Microsoft tends to deliver both versions of its applications, release and
checked (or the debug) build. The checked build versions of commercial
applications are larger in size and slower in performance than the release build
versions, because they perform verbose and extensive logging to the computer
disk to help you find the culprits or bugs in support issues. Both the Debug and
Trace classes contain the exact same methods. But unlike Debug, Trace is meant
for monitoring the application's well-being in the field. Even if you debugged
things in your testing phase, you may find it difficult to find some of the
fuzzy problems, particularly in distributed n-tier environments. For example,
some types of problems arise only when the application is under an intense load,
or the problems may occur randomly or intermittently. Tracing may prove more
useful in finding these problems.
Although Debug and Trace have identical members, you should use Debug during
development and Trace for diagnosing an application after deployment. The output
methods of the Trace and Debug classes are Write(), WriteIf(), WriteLine(), and
WriteLineIf(). WriteLine puts a carriage return and line feed (CRLF) on the end
of the output, but Write does not. WriteIf and WriteLineIf are similar to Write
and WriteLine respectively, but they provide conditional tracing capabilities
since they trace only if the first parameter evaluates to true. The Indent() and
UnIndent() methods of the Debug and Trace classes increase and decrease the
current IndentLevel, which determines how much the output will be indented by
one, respectively. The Fail() method of the Trace and Debug classes produces an
error message so you can handle exceptions and errors during debugging more
easily. The Flush() method of the Trace and Debug classes forces buffered data
to pour to the Listeners collection immediately. We will talk about listeners
later. The Close() method of the Trace and Debug classes performs a Flush()
implicitly and then closes the Listeners. The Assert() method of the Trace and
Debug classes evaluates the passed condition, then if the condition is false, it
pours out its diagnostics messages into the Listeners collection. The Assert
method has three overloads, as shown in Listing 21.5.
Listing 21.5: Assert Overloads
// Assert without description
Debug.Assert(a > b);
// Assert with description
Debug.Assert(a > b,
"a is not greater than b");
// Assert with description and more detail
Debug.Assert(x > y,
" x is not greater than y",
"Assertion... y is less than x");
Although there are many debug and trace methods, when in the past you used
Microsoft Foundation Classes in Visual Studio 6, Microsoft made extensive use of
methods similar to the Debug.Assert() and Trace.WriteLine() methods. You should
consider that an implicit, but strong, recommendation for your code. You can
enable debugging or tracing by adding a #define DEBUG or #define TRACE line to
the top of your code or using the /d:DEBUG or /d:TRACE compiler switch when you
compile. See the example in Listing 21.7.
The C# .NET compiler provides the following techniques to define compilation
variables:
- Compiler command-line switches. For
example, /define:DEBUG or /d:DEBUG; /define:TRACE or /d:TRACE.
- Environment variables in the operating
system shell. For example, SET DEBUG=1, SET TRACE=1.
- Pragmas in the source code. For example,
#define DEBUG to define the compilation variable or #undef DEBUG to undefine
it; #define TRACE to define the compilation variable or #undef TRACE to
undefine it.
Autoflush is used to automatically flush the
output buffer after each write to the listeners if set to true. In the code you
can enable it with a Debug.AutoFlush = true; statement.
IndentSize is the number of spaces in an indent, with the default being 4. The
IndentLevel property and Indent() methods indent the content IndentSize times
any and 1 respectively. In the code you can enable it with a Debug.IndentSize =
6; statement.
Other than programmatic options, you can change your <Application>.EXE.CONFIG
application configuration file such as in Listing 21.6 to set the AutoFlush and
IndentSize properties.
Listing 21.6: Configuration file EXE.CONFIG
<configuration>
<system.diagnostics>
<debug
autoflush="true"
indentsize="6"/>
<listeners>
</listeners>
</debug>
<trace
autoflush="true"
indentsize="6"/>
<listeners>
</listeners>
</trace
</system.diagnostics>
</configuration>
The sample code in Listing 21.7 uses the configuration file shown in Listing
21.6 and outputs with Debug and Trace statements to the console.
Please note the following. Open the Debug Monitor utility (DbMon.exe) of the
Platform SDK for Windows NT, 2000, and XP in a separate command prompt console
before running the listing. DbMon.exe will show any debug and trace messages
coming from any application running on your system that are sent to
DefaultTraceListener (OutputDebugString application programming interface [API]
method of the Windows operating system). We will talk about TraceListener
classes later. For now just know that Debug Monitor runs in its own console
window and displays messages sent by your application using the
OutputDebugString function. If you do not have or do not want to use DbMon.exe,
uncomment the two following code lines to send debug and trace output to the
console:
Debug.Listeners.Add(new
TextWriterTraceListener(Console.Out));
Trace.Listeners.Add(new
TextWriterTraceListener(Console.Out));
Listing 21.7: Using Debug and Trace
#define DEBUG
// for debugging
//or
#define TRACE
// for tracing
using System;
using System.IO;
using
System.Diagnostics;
public
class
TraceListenerExample
{
public static
void Main()
{
// If you do not have DBMON.EXE, then to watch
debug and trace messages,
// make sure that the system will
output debug and trace to the console.
// Otherwise you cannot monitor Debug
and Trace messages.
// The DefaultTraceListener will output
// to OutputDebugString of Windows
operating systems.
// We will talk about TraceListener
classes later.
// Debug.Listeners.Add(new
TextWriterTraceListener(Console.Out));
// Trace.Listeners.Add(new
TextWriterTraceListener(Console.Out));
// Indent by one IndentSize times 1.
Indent this time 6 x 1 = 6!
Debug.Indent();
Trace.Indent();
Debug.WriteLine("Debugged
1!");
Trace.WriteLine("Traced
1!");
bool bDebugTrace =
false;
// test for a boolean flag to output debug or
trace.
Debug.WriteLineIf(bDebugTrace,
"Not Debugged 1!");
Trace.WriteLineIf(bDebugTrace,
"Not Traced 1!");
bDebugTrace = true;
// test for a boolean flag to output debug or
trace.
Debug.WriteLineIf(bDebugTrace,
"Debugged 2!");
Trace.WriteLineIf(bDebugTrace,
"Traced 2!");
// this is faster than WriteLineIf! So prefer
the block below.
if (bDebugTrace ==
true)
{
Debug.WriteLine("Debugged
3!");
Trace.WriteLine("Traced
3!");
}
Console.Read();
}
}
Conditional Debug Attributes
You can use a conditional attribute in front of the method declaration so your
method is callable when the condition is true. Conditional attributes, like
other C# attributes, do not put any burden on the code calling them. For
example, in Listing 21.8, if you put #define IwantDEBUG at the top of your code
or compile your code with the /D:IwantDEBUG option, then myMethod can be called.
Otherwise, the Microsoft intermediate language code for the myMethod code will
be omitted (not generated) in your final assembly.
Listing 21.8: Conditional Attribute Example 1
using System;
using
System.Diagnostics;
class
Test
{
[Conditional("IwantDEBUG")]
public static
void myMethod()
{
Console.WriteLine("only
for conditional DEBUG...");
}
public static
void Main()
{
myMethod();
}
}
Listing 21.9 is a more sophisticated example of using conditional attributes.
Listing 21.9: Conditional Attribute Example 2 (debug2.cs)
using System;
using
System.Diagnostics;
public
class Debugging
{
private int
r = 0;
public Debugging(int
r)
{
this.r = r;
}
public int R
{
get { return
r; }
set { r = value;
}
}
public static
void Main()
{
methodA();
methodB();
methodC();
methodD();
methodE();
methodF();
}
public static
void methodA()
{
Console.WriteLine("Method
A");
}
[Conditional("saygin")]
public static
void methodB()
{
Console.WriteLine("Method
B - has [conditional(saygin)]");
}
public static
void methodC()
{
Console.WriteLine("Method
C");
}
public static
void methodD()
{
Console.WriteLine("Method
D");
}
[Conditional("alex")]
public static
void methodE()
{
Console.WriteLine("Method
E - has [conditional(alex)]");
}
[Conditional("saygin"),
Conditional("alex")]
public static
void methodF()
{
Console.WriteLine("Method
F - has [conditional(saygin),conditional(alex)]");
}
}
Conclusion
Hope this article would have helped you in understanding the
Debugging and Tracing in C#. See other articles on the website on .NET and C#.
|
The Complete Visual
C# Programmer's Guide covers most of the major components that make
up C# and the .net environment. The book is geared toward the
intermediate programmer, but contains enough material to satisfy the
advanced developer. |