Introduction to Instrumentation and Tracing
The term instrumentation refers to an ability to monitor or measure the level of a product's performance and to diagnose errors. In programming, this means the ability of an application to incorporate:
- Code tracing - Receiving informative messages about the execution of an application at run time.
- Debugging - Tracking down and fixing programming errors in an application under development. For more information, see Debugging.
- Performance counters - Components that allow you to track the performance of your application. For more information, see Monitoring Performance Thresholds.
- Event logs - Components that allow you receive and track major events in the execution of your application. For more information, see Logging Application, Server, and Security Events.
The Trace and Debug classes provide the means to monitor and examine application performance either during development or after deployment. For example, you can use the Trace class 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
During development, you can use the output methods of the Debug class to display messages in the Output window of the Visual Studio integrated development environment (IDE). For example:
' Visual Basic Trace.WriteLine("Hello World!") Debug.WriteLine("Hello World!") // C# System.Diagnostics.Trace.WriteLine("Hello World!"); System.Diagnostics.Debug.WriteLine("Hello World!");
Each of these examples will display "Hello World!" in the Output window when the application is run in the debugger.
This enables you to debug your applications and optimize their performance based on their behavior in your test environment. You can debug your application in your debug build with the Debug conditional attribute turned on so that you receive all debugging output. When your application is ready for release, you can compile your release build without turning on the Debug conditional attribute, so that the compiler will not include your debugging code in the final executable. For more information, see Compiling Conditionally with Trace and Debug. For more information on different build configurations for your application, see Default and Custom Builds.
You can also trace code execution in an installed application, using methods of the Trace class. By placing Trace Switches in your code, you can control whether tracing occurs and how extensive it is. This lets you monitor the status of your application in a production environment. This is especially important in a business application that uses multiple components running on multiple computers. You can control how the switches are used after deployment through the configuration file. For more information, see Configuring Trace Switches.
When you are developing an application for which you intend to use tracing, you usually include both tracing and debugging messages in the application code. When you are ready to deploy the application, you can compile your release build without turning on the Debug conditional attribute. However, you can turn on the Trace conditional attribute so that the compiler includes your trace code in the executable. For more information, see Compiling Conditionally with Trace and Debug.
Phases of Code Tracing
There are three phases of code tracing:
- Instrumentation — you add trace code to your application.
- Tracing — the tracing code writes information to the specified target.
- Analysis — you evaluate the tracing information to identify and understand problems in the application.
During development, all debug and trace output methods write information to the Output window in Visual Studio by default. In a deployed application, the methods write tracing information to the targets you specify. For more information on specifying an output target for tracing or debugging, see Trace Listeners.
Trace Instrumentation and Distributed Applications
When you create a distributed application, you might find it difficult to test the application in the manner in which it will be used. Few development teams have the capability to test all possible combinations of operating systems or Web browsers (including all the localized language options), or to simulate the high number of users that will access the application at the same time. Under these circumstances, you cannot test how a distributed application will respond to high volumes, different setups, and unique end-user behaviors. Also, many parts of a distributed application have no user interface with which you can interact directly or view the activity of those parts.
However, you can compensate for this by enabling distributed applications to describe certain events of interest to system administrators, especially things that go wrong, by instrumenting the application — that is, by placing trace statements at strategic locations in your code. Then if something unexpected occurs at run time (for example, excessively slow response time), you can determine the likely cause.
With trace statements you can avoid the difficult task of examining the original source code, modifying it, recompiling, and attempting to produce the run-time error within the debugging environment. Remember that you can instrument an application not only to display errors, but also to monitor performance.
Strategic Placement of Trace Statements
You must exercise special care when placing your trace statements for use during run time. You must consider what tracing information is likely to be needed in a deployed application, so that all likely tracing scenarios are adequately covered. Because applications that use tracing vary widely, however, there are no general guidelines for strategic placement of tracing. For more information on placing trace statements, see Adding Trace Statements to Application Code.
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 you have deleted the DefaultTraceListener without adding any other listeners to the Listeners collection, you will not receive any tracing messages. For more information, see Trace Listeners.
|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.
You can produce custom results by implementing your own listener. A custom trace listener might, for example, display the messages to a message box, or connect to a database to add messages to a table. All custom listeners should support the six methods mentioned above. For more information on creating developer-defined listeners, see TraceListener Class in the .NET Framework reference.
Visual Basic Note In Visual Basic .NET, the Debug.Write, Debug.WriteIf, Debug.WriteLine, and Debug.WriteLineIf methods have replaced the Debug.Print method that was available in earlier versions of Visual Basic.
The Write and WriteLine methods always write the text that you specify. 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. For more information, see Adding Trace Statements to Application Code and the .NET Framework reference.
If you do not disable tracing and debugging before deploying an ASP.NET application, your application may reveal information about itself that could be exploited by a malicious program. For more information, see Compiling Conditionally with Trace and Debug, Default and Custom Builds, and Configuring Trace Switches. Debugging is also configurable through Internet Information Services (IIS).