VB.NET 2005 Tutorials : Tracing a Windows Application
In this tutorial you will learn about Tracing, Using Trace and Debug to display information, Code Tracing and Debugging, Output from Tracing, The six Debug Members and Trace Methods, Trace Listeners, Types of predefined Trace Listeners, Trace Switches, Conditional Compilation and Conditional Compilation Constants.
As explained earlier, Tracing is a method of monitoring the execution of the application while it is running. Tracing and debugging instrumentation can be added to the .NET application when it is being developed, and that instrumentation can be used both while developing the application and after it has been deployed. With the Trace and Debug classes, information can be recorded about errors and application execution to logs, text files, or other devices for later analysis.
Instrumenting the application by placing trace statements at strategic locations in the code is especially useful for distributed applications. With trace statements an application can be instrumented to display information when things go wrong. This information can be displayed to monitor how well the application is performing.
The Trace and Debug classes are identical except that procedures and functions of the Trace class are compiled by default into release builds, but those of the Debug class are not. Let us see how instrumentation is used in applications.
Using Trace and Debug to display information
The Trace and Debug classes provide the means to monitor and examine application performance either during development or after deployment. For example, the Trace class can be used to track particular types of actions in a deployed application as they occur (for example, creation of new database connections), and can therefore monitor the application’s efficiency.
Code Tracing and Debugging
The output methods of the Debug class can be used to display messages during development. For example:
Each of these examples will display "Hello World!" in the Output window when the application is run in the debugger.
Output from Tracing
Trace output is collected by objects called listeners. A listener is an object that receives trace output and writes it to an output device (usually a window, log, or text file). When a trace listener is created, it is typically added to the Trace.Listeners collection, allowing the listener to receive all trace output.
Tracing information is always written at least to the default Trace output target, the DefaultTraceListener. If for some reason the DefaultTraceListener has been deleted without adding any other listeners to the Listeners collection, tracing messages will not be received.
The six Debug Members and Trace methods that write tracing information are listed in the following table.
Method and Output
Assert – The specified text; or, if none is specified, the Call Stack. The output is written only if the condition specified as an argument in the Assert statement is false.
Fail – The specified text; or, if none is specified, the Call Stack.
Write – The specified text.
WriteIf – The specified text, if the condition specified as an argument in the WriteIf statement is satisfied.
WriteLine – The specified text and a carriage return.
WriteLineIf – The specified text and a carriage return, if the condition specified as an argument in the WriteLineIf statement is satisfied.
All listeners in the Listeners collection receive the messages described in the above table, but the actions taken may vary depending on what kind of listener receives the message. For example, the DefaultTraceListener displays an assertion dialog box when it receives a Fail or failed Assert notification, but a TextWriterTraceListener simply writes the output to its stream.
Custom results can be produced by implementing a user defined listener. A custom trace listener might, for example, displays the messages to a message box, or connects to a database to add messages to a table. All custom listeners should support the six methods mentioned above.
In Visual Basic.NET 2005, the Debug.Write, Debug.WriteIf, Debug.WriteLine, and Debug.WriteLineIf methods have been replaced with the Debug.Print method that was available in earlier versions of Visual Basic.
The Write and WriteLine methods always write the text that is specified. Assert, WriteIf, and WriteLineIf require a Boolean argument that controls whether or not they write the specified text; they write the specified text only if the expression is true (for WriteIf and WriteLineIf), or false (for Assert). The Fail method always writes the specified text.
When using Trace and Debug, a mechanism for collecting and recording the messages that are sent is to be put in place. Trace messages are received by listeners. The purpose of a listener is to collect, store, and route tracing messages. Listeners direct the tracing output to an appropriate target, such as a log, window, or text file.
Listeners are available to both the Debug class and the Trace class, each of which can send its output to a variety of listener objects. There are three types of predefined listeners:
- A TextWriterTraceListener redirects output to an instance of the TextWriter class or to anything that is a Stream class. It can also write to the console or to a file, because these are Stream classes.
- An EventLogTraceListener redirects output to an event log.
- A DefaultTraceListener emits Write and WriteLine messages to the OutputDebugString and to the Debugger.Log method. In Visual Studio, this causes the debugging messages to appear in the Output window. Fail and failed Assert messages also emit to the OutputDebugString Windows API and the Debugger.Log method, and also cause a message box to be displayed. This behavior is the default behavior for Debug and Trace messages, because DefaultTraceListener is automatically included in every Listeners collection and is the only listener automatically included.
By default the DefaultTraceListener receives the Debug and Trace output. It the user requires the output to be routed to another Listener the same must be specified and added to the collection. Any listener in the Listeners collection gets the same messages from the trace output methods. If two listeners are set up –a TextWriterTraceListener and an EventLogTraceListener, each listener receives the same message. The TextWriterTraceListener would direct its output to a stream, and the EventLogTraceListener would direct its output to an event log.
The following example shows how to send output to the Listeners collection.
‘ Use this example when debugging.
Debug.WriteLine("Error in Widget 42")
‘ Use this example when tracing.
Trace.WriteLine("Error in Widget 42")
Trace switches allow the user enable, disable, and filter tracing output. They are objects that exist in the code and can be configured externally through the .config file. There are two types of trace switches provided in the .NET Framework: BooleanSwitch Class and TraceSwitch Class. The BooleanSwitch class acts as a toggle switch, either enabling or disabling a variety of trace statements. The TraceSwitch class allows the user enable a trace switch for a particular tracing level so that the trace messages specified for that level and all levels below it appear. If the switch is disabled, the trace messages will not appear. Both these classes derive from the abstract (MustInherit) class Switch, as should any user-developed switches.
Trace switches can be useful for filtering information. For example, a user, might want to see every tracing message in a data access module, but only error messages in the rest of the application. In that case, one trace switch would be used for the data access module and one switch for the rest of the application. By using the .config file to configure the switches to the appropriate settings, types of trace messages can be controlled.
Typically, a deployed application is executed with its switches disabled, so that users need not observe a lot of irrelevant trace messages appearing on a screen or filling up a log file as the application runs. If a problem arises during application execution, you can stop the application, enable the switches, and restart the application. Then the tracing messages will be displayed.
To use a switch, a switch object must be created from a BooleanSwitch class, a TraceSwitch class, or a developer-defined switch class. Then a configuration value can be set to specify when the switch object is to be used. The setting of the switch object is tested in various Trace (or Debug) tracing methods.
Conditional compilation can be used to select particular sections of code to compile while excluding other sections. For example, debugging statements may have to be written, that compare the speed of different approaches to the same programming task, or an application may have to be localized for multiple languages. Conditional compilation statements are designed to run during compile time, not at run time.
A conditional compiler constant can be declared in code with the #Const directive, and blocks of code can be denoted to be conditionally compiled with the #If…Then…#Else directive. For example, to create French and German language versions of the same application from the same source code, platform-specific code segments will have to be embedded in #If…Then statements using the predefined constants FrenchVersion and GermanVersion. The following example demonstrates the point:
#If FrenchVersion Then
‘ < code specific to the French language version >.
#ElseIf GermanVersion Then
‘< code specific to the German language version >.
‘ < code specific to other versions >.
If the value of the FrenchVersion constant is set to True at compile time, the conditional code for the French version is compiled. If the value of the GermanVersion constant is set to True, the compiler uses the German version. If neither is set to True, the code in the last Else block runs. It must be noted here that Autocompletion will not function when editing code and using conditional compilation directives if the code is not part of the current branch.
Conditional Compilation Constants
Conditional compilation allows the user control at compile time what code to include in the program.
The following table lists the predefined constants available for conditional compilation.
CONFIG – A string that corresponds to the current setting of the Active Solution Configuration box in the Configuration Manager.
DEBUG – A Boolean value that can be set in the Project Properties dialog box. By default, the Debug configuration for a project defines DEBUG. When DEBUG is defined, System.Diagnostics.Debug class methods generate output to the Output window. When it is not defined, System.Diagnostics.Debug class methods are not compiled and no Debug output is generated.
TARGET – A string representing the output type for the project or the setting of the command-line /target option. The possible values of TARGET are: "winexe" for a Windows application, "exe" for a console application, "library" for a class library, and "module" for a module. The /target option may be set in the Visual Studio integrated development environment. For more information, see /target.
TRACE – A Boolean value that can be set in the Project Properties dialog box. By default, all configurations for a project define TRACE. When TRACE is defined, System.Diagnostics.Trace class methods generate output to the Output window. When it is not defined, System.Diagnostics.Trace class methods are not compiled and no Trace output is generated.
VBC_VER – A number representing the Visual Basic version, in major.minor format. The version number for Visual Basic 2005 is 8.0.
These constants can be used only for conditional compilation; they cannot be used in executable code.
This example uses the TARGET conditional compilation constant to determine whether to compile certain statements.
The Visual Studio integrated development environment defines the CONFIG, DEBUG, and TRACE conditional compilation constants.
The Visual Basic compiler defines the TARGET and VBC_VER conditional compilation constants. The TARGET and VBC_VER constants are not available in compiler versions before Visual Basic 2005
Conditionally compiles selected blocks of Visual Basic code.
Required for #If and #ElseIf statements, optional elsewhere. Any expression, consisting exclusively of one or more conditional compiler constants, literals, and operators, that evaluates to True or False.
Required for #If statement block, optional elsewhere. Visual Basic program lines or compiler directives that are compiled if the associated expression evaluates to True.
Terminates the #If statement block.
On the surface, the behavior of the #If…Then…#Else directives appears the same as that of the If…Then…Else statements. However, the #If…Then…#Else directives evaluate what is compiled by the compiler, whereas the If…Then…Else statements evaluate conditions at run time.
Conditional compilation is typically used to compile the same program for different platforms. It is also used to prevent debugging code from appearing in an executable file. Code excluded during conditional compilation is completely omitted from the final executable file, so it has no effect on size or performance.
This example uses the #If…Then…#Else construct to determine whether to compile certain statements.
#Const CustomerNumber = 36
#If CustomerNumber = 35 Then
‘ Insert code to be compiled for customer # 35.
#ElseIf CustomerNumber = 36 Then
‘ Insert code to be compiled for customer # 36.
‘ Insert code to be compiled for all other customers.