7.2 TracingTracing is an easy way to find out what is going on in your program. Back in the days of classic ASP, the only way to trace what was happening in your code was to insert Response.Write statements in strategic places. This allowed you to see that you had reached a known point in the code, and perhaps to display the value of some variables. The big problem with this hand-tracing technique, aside from the amount of work involved, was that you had to laboriously remove or comment out all those statements before the program went into production. ASP.NET provides better ways of gathering the trace information. You can add tracing at the application level or at the page level. With application-level tracing, every page is traced, while with page-level tracing, you choose the pages to which to add tracing. 7.2.1 Page-Level TracingTo add page-level tracing, modify the Page directive at the top of the .aspx page, by adding a Trace attribute and setting its value to true, as follows: <%@ Page language="c#" Codebehind="WebForm1.aspx.cs" AutoEventWireup="false" Inherits="DebuggingApp.WebForm1" Trace="true" %> When you view this page, there will now be tables at the bottom that contain a wealth of information about your web application. Select a book from the drop-down list and you will see something like Figure 7-2. Figure 7-2. Trace resultsThe top section, labeled Request Details, shows basic information, including the SessionID, the Time of Request, Request Type, and Status Code (Table 7-1). Every time the page is posted to the server, this information is updated. If you change the selection (remember that AutoPostBack is set to true), you will see that the Time of Request is updated, but the SessionID remains constant. The next section, labeled Trace Information, is the trace log, which provides lifecycle information. This includes elapsed times, in seconds, since the page was initialized (the From First(s) column) and since the previous event in the lifecycle (the From Last(s) column). You can add custom trace information to the trace log, as explained later in this chapter.
The next section in the trace lists all the controls on the page in a hierarchical manner, including the name of the control, its type, and its size in bytes, both on the page and in the ViewState state bag. This is followed by itemizations of the Cookies and Headers collections. Finally there is a list of all the server variables. 7.2.2 Inserting into the Trace LogYou can add custom information to the trace output by writing to the Trace object. This object exposes two methods for putting your own statements into the trace log, Write and Warn. The only difference between the two methods is that Warn writes to the log in red. The Warn and Write methods are overloaded to take either a single string, two strings, or two strings and an exception object. The following cases illustrate:
Example 7-5 contains the C# source code, and Example 7-6 contains the VB.NET source code for the Page_Load and ddlBooks_SelectedIndexChanged event procedures; this adds three messages to the trace. Changed lines of code are indicated in boldface. Example 7-5. Writing to the Trace object in C#private void Page_Load(object sender, System.EventArgs e) { // Put user code to initialize the page here Trace.Write("In Page_Load"); if (! IsPostBack) { Trace.Write("Page_Load", "Not Postback."); // Build 2 dimensional array for the lists // First dimension contains bookname // 2nd dimension contains ISBN number string[,] books = { {"Programming C#","0596001177"}, {"Programming ASP.NET","1234567890"}, {"WebClasses From Scratch","0789721260"}, {"Teach Yourself C++ in 21 Days","067232072X"}, {"Teach Yourself C++ in 10 Minutes","067231603X"}, {"XML & Java From Scratch","0789724766"}, {"Complete Idiot's Guide to a Career in Computer Programming","0789719959"}, {"XML Web Documents From Scratch","0789723166"}, {"Clouds To Code","1861000952"}, {"C++: An Introduction to Programming","1575760614"}, {"C++ Unleashed","0672312395"} }; // Now populate the lists. int i; for (i = 0; i < books.GetLength(0); i++) { // Add both Text and Value ddlBooks.Items.Add(new ListItem(books[i,0],books[i,1])); } } } private void ddlBooks_SelectedIndexChanged(object sender, System.EventArgs e) { // Force an exception try { int a = 0; int b = 5/a; } catch (System.Exception ex) { Trace.Warn("UserAction","Calling b=5/a",ex); } // Check to verify that something has been selected. if (ddlBooks.SelectedIndex != -1) { lblDdl.Text=ddlBooks.SelectedItem.Text + " ---> ISBN: " + ddlBooks.SelectedItem.Value; } } Example 7-6. Writing to the Trace object in VB.NETPrivate Sub Page_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load 'Put user code to initialize the page here Trace.Write("In Page_Load") If Not IsPostBack Then ' Build 2 dimensional array for the lists ' First dimension contains bookname ' 2nd dimension contains ISBN number Trace.Write("Page_Load", "Not Postback.") Dim books(,) As String = { _ {"Programming C#", "0596001177"}, _ {"Programming ASP.NET", "1234567890"}, _ {"WebClasses From Scratch", "0789721260"}, _ {"Teach Yourself C++ in 21 Days", "067232072X"}, _ {"Teach Yourself C++ in 10 Minutes", "067231603X"}, _ {"XML & Java From Scratch", "0789724766"}, _ {"Complete Idiot's Guide to a Career in Computer Programming", _ "0789719959"}, _ {"XML Web Documents From Scratch", "0789723166"}, _ {"Clouds To Code", "1861000952"}, _ {"C++: An Introduction to Programming", "1575760614"}, _ {"C++ Unleashed", "0672312395"} _ } ' Now populate the lists. Dim i As Integer For i = 0 To books.GetLength(0) - 1 ' Add both Text and Value ddlBooks.Items.Add(New ListItem(books(i, 0), books(i, 1))) Next End If End Sub Private Sub ddlBooks_SelectedIndexChanged(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles ddlBooks.SelectedIndexChanged ' Force an exception Try Dim a As Integer = 0 Dim b As Integer = 5 / a Catch ex As System.Exception Trace.Warn("UserAction", "Calling b=5/a", ex) End Try ' Check to verify that something has been selected. If ddlBooks.SelectedIndex <> -1 Then lblDdl.Text = ddlBooks.SelectedItem.Text & " ---> ISBN: " & _ ddlBooks.SelectedItem.Value End If End Sub The first message is added in the Page_Load method to signal that you've entered that method: Trace.Write("In Page_Load"); The second message is added if the page is not a postback: if (! IsPostBack) { Trace.Write("Page_Load", "Not Postback."); This second message is categorized as Page_Load; using a category can help you organize the trace output. The effect of these two Write statements is shown in Figure 7-3. Shading was added to make it easy to see these two statements. Figure 7-3. Two Trace.Write statementsThe third message is added to demonstrate the process of inserting an exception into the error log. The ddlBooks_SelectedIndexChanged event handler also contains code to force an exception by dividing by zero. The code catches that exception and logs the exception with a trace statement, as shown by the following code fragment: try { int a = 0; int b = 5/a; } catch (System.Exception ex) { Trace.Warn("UserAction","Calling b=5/a",ex); } The output from this trace statement is shown in Figure 7-4. Figure 7-4. Trace statement outputBecause this trace statement was written calling the Warn method rather than the Write method, the trace output appears in red onscreen (though not in your copy of this book). Notice that string you passed in, Calling b=5/a, is displayed, followed by an error message extracted automatically from the exception object. Not only is it easy to implement trace statements, but when it is time to put your page into production, all these statements can remain in place. The only modification you need to make is to change the Trace attribute in the Page directive from true to false. 7.2.3 Application-Level TracingApplication-level tracing applies to all the pages in a given application. It is configured through the web.config file, which will be described more fully in Chapter 20. The web.config file is typically located in the virtual root directory of the application. If there is a web.config file in a subdirectory of the virtual root, then that copy will apply only to the pages in that subdirectory and in the subdirectories under it. If tracing is enabled application-wide from the root directory, tracing will be applied across the application uniformly. The exception is when a specific page has a contradictory page directive, which supersedes the application directive. Web.config is an XML file that consists of sections delimited by tags. The trace configuration information is contained in the <trace> section within the <system.web> section, which is contained within the <configuration> section.
A typical trace configuration snippet will look something like Example 7-7. Example 7-7. Trace code snippet from web.config<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.web> . . . <trace enabled="true" requestLimit="10" pageOutput="false" traceMode="SortByTime" localOnly="true" /> You can easily edit the web.config file in VS.NET by double-clicking on the file in the Solution Explorer. (If the Solution Explorer is not visible, click on the View/Solution Explorer menu item.) Alternatively, this file can be edited in any text editor. There are five possible properties in the <trace> section. These properties appear in Table 7-2. Several of these properties affect the trace viewer, which will be described in the following section.
7.2.4 Trace ViewerIf application-level tracing is enabled, the trace log can be viewed directly from your browser for any application, even across multiple page requests. The trace facility provides a trace viewer, called trace.axd. Aim your browser toward trace.axd as though it were a page in the application, with the following URL, for example: http://localhost/DebuggingApp/trace.axd You will see a summary of all the entries in the trace log, as shown in Figure 7-5. Figure 7-5. Trace viewerClicking on any of the View Details links will bring you to the same page as would be seen in page-level tracing for that page. |