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.
|