DekGenius.com
[ Team LiB ] Previous Section Next Section

Recipe 6.1 Controlling Tracing Output inProduction Code

Problem

Mysterious bugs often appear at the client's site, even after the application is thoroughly tested. Most of the time these bugs are difficult, if not impossible, to reproduce on your development machine. Knowing this, you want an application with built-in instrumentation that's off by default but can easily be turned on when you need it.

Solution

Use the Trace class for any tracing code that you might need to turn on after your application has been deployed. To turn on tracing at a client's site, provide the client with an application configuration file such as this one:

<?xml version="1.0" encoding="utf-8" ?> 
<configuration>
    <system.diagnostics>
        <switches>
            <add name="DatabaseSwitch" value="4"/>   
            <!-- 4 == TraceLevel.Verbose -->
        </switches>

        <trace autoflush = "true" indentsize = "2">
            <listeners>
                <add name = "MyListener" 
                     type = "System.Diagnostics.TextWriterTraceListener" 
                     initializeData = " MyFileName.log"/>
            </listeners>
        </trace>
    </system.diagnostics>
</configuration>

Discussion

Allowing tracing code to be enabled and used at a client site can be incredibly useful when debugging problems in release code. This technique is even more useful when the problem cannot easily be reproduced in-house. For this reason, it is—in some cases—a wise practice to use the Trace class instead of the Debug class when adding tracing code to your application.

To control the trace output at a client site, you can use an XML config file. This XML file must have the same base name as the executable that is to use these switches, followed by an extension of .exe.config. For example, if the executable name were Accounting.exe, the configuration file would be named Accounting.exe.config. This file should be placed in the same directory as the executable Accounting.exe.

The application configuration file always consists of the following two outer elements for diagnostic information:

<configuration>
    <system.diagnostics>
        ...
    </system.diagnostics>
</configuration>

However, the configuration element may contain other child elements besides the system.diagnostics element.

Within these elements, the switches and trace elements may be added. These two elements contain information specific to switches and listeners. If your code contains a TraceSwitch (as shown in the next example) or BooleanSwitch object—or any other type derived from the Switch class—you can control this object's trace level setting through the <switches> element in the configuration file:

private static TraceSwitch ts = new TraceSwitch("DatabaseSwitch", 
        "Only allow database transactions to be logged");

The <listeners> element shown in the Solution adds a new TraceListener derived object to the listeners collection. Any Trace or Debug statements will use this new listener.

The switches element of the Solution can contain the three elements defined here:


<clear/>

Clears any previously added switch.


<add name= "Switch_Name" value= "Number"/>

Adds new switch initialization information to be used at runtime. The name attribute defines the name of the switch that is used in your code. The value attribute is set to a number that either turns the switch on or off, in the case of a BooleanSwitch class, or defines the switch level (e.g., the amount of output you wish to receive), in the case of a TraceSwitch class. To turn on a BooleanSwitch, use a nonzero value (negative numbers work here, too); to turn it off, use zero.


<remove name= "Switch_Name"/>

Removes switch initialization information at runtime. The name attribute defines the name of the switch that is used in your code.

Immediately after the switches tags in the solution are the trace tags, although the ordering of these tags is up to you. The trace tags can contain the following two optional attributes:


autoflush = true|false

Indicates whether the listener automatically flushes its buffer after every write (true) or not (false).


indentsize = "4"

Specifies the number of indent characters to use when indenting the output.

Within the trace tags are the listeners tags, which, in turn, can contain any of the following defined tags:


<clear/>

Clears any previously added listeners. This tag also removes the DefaultTraceListener from the listeners collection.


<add name= "MyListener" type="System.Diagnostics.TextWriterTraceListener,System" initializeData= "MyFileName.log"/>

Adds a new listener to any Trace and Debug classes used in your application. The name attribute defines the name of the listener that is used in your code. The type attribute is set to the listener's class name. The optional initializeData attribute allows a string to be passed in to the constructor of this listener. If you are using a custom listener, you will need to include a constructor that accepts a string as the only argument to prevent an exception from being thrown.


<remove name = "MyListener"/>

Removes a listener at runtime. The name attribute defines the name of the listener to be removed. This could be useful if another configuration file, such as the machine.config file, has already added a listener or if any listeners were created through your application's code. If more than one listener is added, the output will be written out twice—once for each listener.

Regardless of whether your code defines TRACE and/or DEBUG, the code will attempt to access this file for switch initialization information if a class derived from Switch is instantiated. If you wish to prevent this behavior, place any code that instantiates a switch class inside of a method decorated with the ConditionalAttribute attribute:

public class Traceable
{
    BooleanSwitch DBSwitch = null;
    BooleanSwitch UISwitch = null;
    BooleanSwitch exceptionSwitch = null;

    [System.Diagnostics.ConditionalAttribute("TRACE")]   
    public void EnableTracing( )
    {
        DBSwitch = new BooleanSwitch("DatabaseSwitch", 
                   "Switch for database tracing");
        UISwitch = new BooleanSwitch("UISwitch", 
                   "Switch for user interface tracing");
        exceptionSwitch = new BooleanSwitch("ExceptionSwitch", 
                          "Switch for tracing thrown exceptions");
    }
}

The ConditionalAttribute attribute prevents the switches from attempting to access the application configuration file when TRACE is undefined by preventing your application from calling the EnableTracing method.

In addition to the application configuration file (MyApp.exe.config), there is also a machine.config file located in the directory %<runtime install path>%\CONFIG\. The configuration tags, and all of its containing elements may be placed in this file as well. However, doing so will enable these switches and listeners on a machine-wide level. This can cause applications that define their own listeners to behave strangely, especially if the listeners are duplicated. Additionally, the application will look for configuration information in the application configuration file first and the machine.config file second.

The application configuration file and the machine configuration file are both case-sensitive. Be sure that your tag names and their attributes are in the correct case. However, the string assigned to the name attribute does not seem to be case-sensitive, while other strings assigned to attributes are case-sensitive.

See Also

See the "Trace and Debug Settings Schema" topic in the MSDN documentation.

    [ Team LiB ] Previous Section Next Section