January 25, 2001
Whenever I give a presentation on ASP.NET, it's always fun to watch the audience when the new tracing functionality is demonstrated. For those of us who have built solutions with ASP, tracing is a godsend!
In this month's column, we're going to look at the tracing feature in ASP.NET. Let's start by discussing the common tracing technique for ASP, Response.Write().
Tracing with ASP
If you're like me, when you code an ASP application you use Response.Write() statements when you're dissecting a troublesome section of code.
Here's some pseudo code to illustrate this point:
<% On Error Resume Next Response.Write("About to do data access...") Set objDB = Server.CreateObject("Custom.DataAccess") objDB.Open("PersonalizationData") user = objDB.Get("username") Response.Write("Done calling data access. Value of username is:" + user) %>
The Response.Write() calls in the code above should look quite familiar, i.e., we've got an error with the behavior of our code. The error isn't necessarily an application or system error, such as a failure to load the object, but rather a coding error in which the code executes as expected but doesn't return the expected value.
Of course, we can also use a debugger, but sometimes it's just faster to get a trace output to find out what the code is doing or to simply let us know how a particular section of code is being executed.
Although Repsonse.Write() statements do make it easy to debug the application rather quickly, they introduce unnecessary code into an application that can result in real bugs that break deployed applications. For example, generally it's not considered a good thing when we forget to remove a more colorful Response.Write() statement that might express our true feelings about the code we're working on.
Of course, the same functionality of Response.Write() statements is supported within ASP.NET. In fact, I still find myself using Response.Write() even though we now have a tracing feature. Old habits are hard to break.
However, we should all try to break those old habits because the new tracing functionality gives us something that didn't exist in ASP: debug mode.
Tracing with ASP.NET
The new tracing features of ASP.NET allow us to simulate Response.Write() statements but not worry about removing the statements before we deploy our applications. Instead, imagine writing the same code as above, but instead of using Response.Write(), we use Trace.Write().
The Trace object is now an intrinsic page object, similar to Request, Response, Server, etc. It is accessible directly with our page code.
Let's take a brief look at the Trace class.
Trace is exposed as a public property within ASP.NET pages. When we use the Trace property, we're working with an instance of the TraceContext class defined in the System.Web namespace.
The Trace class exposes two overloaded methods and two properties. The two methods,
Write(), both support two identical prototypes:
Public Sub [Warn | Write](category As String, message As String, errorInfo As Exception) End Sub Public Sub [Warn | Write](category As String, message As String) End Sub
public void [Warn | Write](String category, String message, Exception errorInfo) public void [Warn | Write](String category, String message)
The category parameter is used to identify the category name to write the message value into. This will become more apparent when we use the Write() method in an example below. The third parameter that both Warn() and Write() support in their overloaded methods is errorInfo. This allows us to pass exception details into the trace system.
The TraceContext class supports two public properties: IsEnabled and TraceMode.
- IsEnabled indicates whether tracing is enabled on the page or for the application.
- TraceMode sets or returns the sorting mode that tracing is using. We'll look at configuring the trace mode when we discuss application-level tracing below.
Now that we've seen what the class and the methods and properties supported by the TraceContext class look like, let's look at an example of Trace.Write().
<Script runat=server> Public Function Add(a As Integer, b As Integer) As Integer Trace.Write("Inside Add() a: ", a.ToString()) Trace.Write("Inside Add() b: ", b.ToString()) return a + b End Function </Script> Call the Add routine: 4 + 5 = <%=Add(4,5)%>
When this code is run the output is:
Figure 1. Example of Trace.Write() method
Obviously the Add() method is being called. However, unlike the Repsonse.Write() statements, the Trace.Write() statements do not appear within the resulting output. To view the results of the Trace.Write() statements, we need to enable tracing for this page or for the application.
To enable tracing, we can use either a page directive or a configuration option. Let's look at both.
Page Trace Directive
Page directives are special instructions that ASP.NET uses when processing a request for an ASP.NET resource. Page directives can be used to override or apply configuration settings for an ASP.NET page.
A directive must be the first element on a page. It is declared using the following syntax:
<%@ Directive Attribute="[Value]" %>
Using the above syntax, here's how we tell ASP.NET that we would like tracing enabled on a page:
<%@ Page Trace="true" %>
If we add the above directive to our sample code, our page output looks quite a bit different:
Figure 2. Tracing enabled
We're now getting the tracing output added to the bottom of the page. Trace output is always added at the end of the page.
Next, let's see how we enable tracing at an application level. Then we'll discuss what the trace output contains.
ASP.NET uses XML configuration files to set configuration settings for ASP.NET applications. Every ASP.NET installation installs a configuration file in the [system drive]\WinNt\Microsoft.NET\Framework\[version]\ directory named config.web.
The configuration file sets the default ASP.NET configuration, and in Beta 1 is known as the root configuration file. Changes made within this file affect all of our ASP.NET Web application. We can also use configuration files in our Web applications to add or remove settings acquired from the default config.web.
I will cover configuration in more detail in a future column. For now, we'll use the root configuration file to apply tracing settings for all ASP.NET applications.
If we open the root config.web file, we'll find a tracing section:
<configuration> <trace enabled="false" requestlimit="10" pageoutput="false" tracemode="SortByTime" /> </configuration>
There are four attributes for the trace element:
- enabled = "[true/false]
"—We can set the enabled option to true or false. Tracing is either enabled at an application level, or it is disabled at the application level. If we set enabled=false, page tracing is still supported using the Trace directive discussed earlier.
- requestlimit =
"—The total number of trace requests to keep cached in memory on a per-application basis. Tracing exposes a special resource—Trace.axd, which we'll look at momentarily—that is used to view trace output when pageoutput is set to false.
- pageoutput =
"—When tracing is enabled through the configuration file, the administrator can either enable or disable tracing on each page. The pageoutput tracing enables tracing details for every page within an application. However, pageoutput tracing may be turned off while application-level tracing is still enabled (enabled
"). This keeps trace requests in memory, such that they are available via trace.axd, which we'll look at momentarily, but not displayed within the output of a page.
= "[SortByTime | SortByCategory]
"—The tracemode setting gives us control over how trace detail information is output. Data may be sorted by time or category, where category is differentiated between the settings made by the system and the Trace.Write() settings enabled by the developer. For example, in our sample we used the following: Trace.Write(
.The first parameter of the Trace.Write() statement is the category. In this case we defined category as
"Inside Add() a:
". If we sorted by category (tracemode = "SortByCategory
"), this item would be sorted before the normal page function calls in the trace output. Sorting by time, on the other hand, sorts by the amount of time spent in the call.
Using application tracing
Using the same sample code from above, we could remove the page directive and enable the same functionality by modifying the tracing config.web settings.
<configuration> <trace enabled="true" requestlimit="10" pageoutput="true" tracemode="SortByTime" /> </configuration>
We can now call our sample application, removing the Page directive, and get the same output:
Figure 3. Tracing enabled, no Page directive
But what if we want to use the feature mentioned earlier, in which we enable tracing but disable output on the page?
<configuration> <trace enabled="true" requestlimit="10" pageoutput="false" tracemode="SortByTime" /> </configuration>
If we requested the sample application with the above configuration settings, we would get no tracing output, although tracing is still enabled. To view the trace output, we use a special application called trace.axd.
Trace.axd is an Http Handler. An Http Handler is a coding option that allows us to handle request/responses at the most basic level. Http Handlers are the equivalent of ISAPI extensions. However, unlike ISAPI, they can be authored using any .NET language. I'll discuss Http Handlers in more detail in a later column.
Trace.axd is an Http Handler that we can use to request application trace details. When requested, we will be given a trace log of the last n requests; n is determined by the value set by requestlimit="[int]" in our configuration file.
To use trace.axd, simply request trace.axd in the same application directory that the request for the sample application was made:
Figure 4. Application trace requests
As you can see from the screen shot, we are presented with a list of traces that we can view. These trace views are identical to the details that trace would add to the page, without the output from the page included:
Figure 5. Request details
Now that we've walked through how we configure and use tracing, let's discuss the trace output.
Interpreting the Trace Output
The output provided by tracing view, either through Trace.axd or on a page, provides six sections of detail:
- Request details—This is basic information, such as the session id, time of the request, the Http request type, and the Http response status code.
- Trace information—This section provides a table view of categories and messages. If we use Trace.Write() statements in our code, the two parameters that Trace.Write() accepts are used as the Category and Message values, respectively. Additionally we're provided with the time from first to last byte.
- Control tree—Although not yet discussed in this column, ASP.NET uses server controls to allow us to declaratively build applications. The Control Tree section provides us with information about controls within our page. We are provided with the id, type, render size, and view state size of the control. This information gives us an idea of which controls we're using, as well as the associated cost (render size and viewstate).
- Cookies collection—Any cookies that the client sends in the request headers are parsed, and their names, values, and sizes are displayed.
- Headers collection—Http headers presented by the client to the server are provided in this section. It is a simple table that lists Name/Value.
- Server variables—The server variables section presents a Name/Value pair table of server variables.
As you can clearly see, tracing is a powerful new ASP.NET feature designed to make developing Web applications easier. However, I thought it would also be worthwhile to mention when you should and shouldn't use tracing.
Tracing adds additional overhead to requests and should not be enabled for deployed applications. Trace.Write() statements, however, can be left in because they are ignored when tracing is not enabled.
For deployed applications in which we wish to capture data, I would suggest using the classes found in the System.Diagnostics namespace. These classes, which I'll try to cover in a future column, allow us to write directly to the Windows event log.
We should definitely use tracing when we're building an application, but disable it when we're ready to deploy the application, just as we would have removed those Response.Write() statements.
If you have installed ASP.NET on a production Web server, I would recommend explicitly disabling Trace.axd. Open the root config.web file, find the <
httphandlers> section, and add the following entry:
<configuration> <add verb="*" path="trace.axd" type="System.Web.Handlers.TraceHandler" /> <remove verb="*" path="trace.axd"/> </configuration>
Note: We could also cut the entire <
add/> entry, but the <
remove> option is better because it leaves the code in our configuration file and accomplishes the same goal, disabling trace.axd. This will be resolved in Beta 2 of ASP.NET.
The tracing feature in ASP.NET is a powerful new way to trace what our application is doing. In the past, we used Response.Write() statements to trace our application as it executed, but we can now use Trace.Write() statements and leave these statements in our deployed code. This leads to fewer bugs because we're not removing code when we're ready to deploy the application.
Finally, I wanted to call out some of the new ASP.NET resources available. Please visit http://www.asp.net to learn about more ASP.NET resources.
- Eraserver.net offers hosting for ASP.NET.
Rob Howard is a program manager for ASP.NET on the .NET Framework team. He spends whatever spare time he has either with his family or fly fishing in Eastern Washington.