Tracing

To instrument an application, developers commonly use trace messages. By default, trace messages are displayed in the Output window. Trace messages are used for a variety of reasons but primarily for diagnostic purposes. Traces can validate program flow (often the cause of bugs). Trace messages can display the state of the application at different stages. You can display the values of objects, local variables, or other data throughout the lifetime of an application. Finally, trace messages are useful for tracking events, such as start, stop, and user-defined events.

Various classes in the Framework Class Library (FCL) support tracing. TraceSource is the primary tracing type. The TraceSource class is found in the System.Diagnostics namespace.

To enable tracing, the TRACE symbol must be defined in the application. In the source code, this is accomplished with the #define statement. Alternatively, define the symbol as a compiler option. The /d compiler option defines a symbol:

csc /d:TRACE test.cs

In Visual Studio, both the Debug and Release configurations define the TRACE symbol by default.

Table 15-12 explains the important methods of the TraceSource class.

Table 15-12. TraceSource methods

Level

Description

Constructors

Both constructors assign a name to the TraceSource object. The two-argument constructor also sets a default severity level for all trace messages sent from this trace source.

TraceSource(string name)

TraceSource(string name, SourceLevels defaultLevel)

Close

This method closes all the trace listeners assigned to this trace source.

void Close()

Flush

This method flushes the trace listeners assigned to this trace source, ensuring that cached trace messages are sent to listeners.

void Flush()

TraceData

This method creates a trace message consisting of the event type, a trace identifier, and any trace data. The event type is the severity level.

[ConditionalAttribute("TRACE")] public void TraceData(TraceEventType eventType, int id, Object data)

[ConditionalAttribute("TRACE")] public void TraceData(TraceEventType eventType, int id, Object[] data)

TraceEvent

This method creates a trace message consisting of an event type and message, which is written to the Listeners collection. The first overload—the method without a message or data parameter—writes an empty trace message. This is sufficient for simply acknowledging an event.

[ConditionalAttribute("TRACE")]public void TraceEvent(TraceEventType eventType, int id)

[ConditionalAttribute("TRACE")]public void TraceEvent(TraceEventType eventType, int id, string message)

[ConditionalAttribute("TRACE")]public void TraceEvent(TraceEventType eventType, int id, object [] data)

TraceInformation

This method creates an informational message, which is written to the Listeners collection.

[ConditionalAttribute("TRACE")]public void TraceInformation(string message)

[ConditionalAttribute("TRACE")]public void TraceInformation(string format, params Object[] args)

TraceTransfer

This method creates a transfer message, which is written to the listeners of this trace source.

[ConditionalAttribute("TRACE")]public void TraceTransfer(int id, string message, Guid relatedActivityID)

The TraceSource type also has several properties. Table 15-13 lists these properties.

Table 15-13. TraceSource properties

Property

Type

Attributes

This property gets the custom attributes that are defined in an application configuration file.

StringDictionary

Listeners

This property returns an array of listeners. Listeners are the targets of trace messages.

TraceListenerCollection

Name

This property returns the name of the TraceSource object. The name can be used with the SourceFilter property of a listener.

string

Switch

This property gets or sets the trace switch associated with the TraceSource object. Trace switches filter trace messages sent to listeners.

SourceSwitch

Tracing can be simple or complex. Severity levels, switches, listeners, and listener filters determine when, where, and how trace messages are reported. Severity levels are assigned to trace messages, which establish the priority of the message. Switches filter trace messages based on their severity levels. Trace messages are sent to listeners, where listener filters control the final messages reported to the user. Tracing is configurable either programmatically or using an application configuration file. The application configuration file is recommended over programmatic control. With the application configuration file, tracing is controlled, enabled, or disabled without needing to recompile the program. For a production application, this is essential.

Trace messages are assigned severity levels. Trace levels set the category or importance of a message. Some trace messages, such as the start and stop time of an application, are activity related. Informational trace messages are used to stub methods and to check program flow. The TraceEventType enumeration defines the trace levels, which are listed in Table 15-14. The levels are listed in descending order of priority.

Table 15-14. Trace levels

Level

Description

TraceEventType.Critical

Conditions that destabilize an application and are fatal

TraceEventType.Error

Conditions that destabilize an application but are recoverable

TraceEventType.Warning

Noncritical conditions

TraceEventType.Information

General information that might aid in the diagnosing of an error condition

TraceEventType.Verbose

General information not necessarily associated with an error condition

TraceEventType activities

See Table 15-15

Some trace levels are related to activities, which are linked to events. The activity traces are grouped at the same trace level, which is the lowest trace level, beneath the Verbose level. Table 15-15 shows the list of activity traces.

Table 15-15. Activity traces

Activity

Description

TraceEventType.Start

Indicates that an operation is starting

TraceEventType.Stop

Indicates that an operation is stopping

TraceEventType.Suspend

Indicates that an operation is suspended

TraceEventType.Resume

Indicates that an operation has resumed

TraceEventType.Transfer

Indicates a change of correlation identity

Switches filter trace messages based on trace levels. This determines the messages sent from a TraceSource object to listeners. With switches, you can enable or disable groups of trace messages selectively. If an application is crashing, you might decide to send only critical and error trace messages that could indicate the application is failing. When algorithms are performing incorrectly, you might want to enable general and activity traces to show program flow.

Tracing can be helpful but excessive tracing can harm performance. Unrestricted tracing can generate copious amounts of information. Filters reduce trace messages and conserve resources. Switch filters are defined by the SourceSwitch class. The SourceSwitch class has the Level property, which is a SourceLevels type. SourceLevels is an enumeration and defines the level of the filter. This is a bitwise flag, and the various SourceLevels can be combined. The source levels are detailed in Table 15-16.

Table 15-16. Source levels

Filter

Description

SourceLevels.ActivityTracing

Allows activity trace messages. Other messages are filtered.

SourceLevels.All

Allows all trace messages.

SourceLevels.Critical

Allows only critical trace messages.

SourceLevels.Error

Allows error and critical trace messages. Other trace levels are filtered.

SourceLevels.Information

Filters verbose and activity trace messages. Other trace messages are sent.

SourceLevels.Off

Excludes all trace messages.

SourceLevels.Verbose

Filters activity trace messages. All other messages are sent.

SourceLevels.Warning

Filters all trace messages except for Critical, Error, and Warning events.

To establish a filter, assign an instance of SourceSwitch to the TraceSource.Switch property. The SourceSwitch has two constructors:

public SourceSwitch(string name)

public SourceSwitch(string name, string defaultLevel)

SourceSwitch also has several useful properties, which are listed in Table 15-17.

Table 15-17. SourceSwitch properties

Property

Type

Attributes

This property gets custom attributes that are defined in the application configuration file.

StringDictionary

Description

This property returns a general description of the switch. It defaults to an empty string.

string

DisplayName

This property returns the name of the switch.

string

Level

This property gets or sets the level of the switch.

SourceLevels

The following code creates an instance of TraceSource and TraceSwitch. The switch then is assigned to the TraceSource:

TraceSource ts = new TraceSource("sample");
SourceSwitch sw = new SourceSwitch("switch");
sw.Level = SourceLevels.All;
ts.Switch = sw;

The event type of trace messages and the switch source level combine to determine which messages are sent. The following code sends trace messages of each event type: critical, error, information, and so on. The switch level, which is case-sensitive, is read from the command line and determines which messages are sent rather than filtered. A console listener is used in the application, which displays the unfiltered trace messages in the console. This is an excellent program for understanding the combination of trace event types and switch levels:

#define TRACE
using System;
using System.Diagnostics;

namespace Donis.CSharpBook {
    public class Starter {
        public static void Main(string[] argv) {
           TraceSource ts = new TraceSource("sample");
           SourceSwitch sw = new SourceSwitch("switch");

           sw.Level = (SourceLevels)Enum.Parse(
               typeof(SourceLevels), argv[0]);
           ts.Switch = sw;
           ConsoleTraceListener console =
               new ConsoleTraceListener();
           ts.Listeners.Add(console);
           ts.TraceEvent(TraceEventType.Start, 0,
               "Activity trace messages on");
           ts.TraceEvent(TraceEventType.Verbose, 0,
               "Verbose trace messages on");
           ts.TraceEvent(TraceEventType.Information, 0,
               "Information trace messages on");
           ts.TraceEvent(TraceEventType.Warning, 0,
               "Warning trace messages on");
           ts.TraceEvent(TraceEventType.Error, 0,
               "Error trace messages on");
           ts.TraceEvent(TraceEventType.Critical, 0,
               "Critical trace messages on");

           ts.Flush();
           ts.Close();
        }
    }
}

Trace messages can be sent to a variety of targets, including the console, a text file, and an XML file. Trace targets are called listeners. An array of listeners called the Listeners collection can be associated with a trace source. By default, the Listeners collection has a single item, the DefaultTraceListener, which displays trace messages in the Output window of Visual Studio. When the Listeners collection contains more than one listener, trace messages are sent to multiple targets. If the collection contains a ConsoleTraceListener and the XMLWriterTraceListener, trace messages are displayed in the console and are saved in an XML file. Trace listeners receive trace messages sent from a trace source. Only messages not filtered at the trace source are sent to the listeners.

Table 15-18 reviews the available predefined listeners.

Table 15-18. Trace listeners

Listeners

Description

TextWriterTraceListener

Sends trace messages to a stream-related destination, such as a TextWriter class.

EventLogTraceListener

Sends trace messages to the event log.

DefaultTraceListener

Sends trace messages to the Output window. This is the default listener and is initially the only member of the Listeners collection.

ConsoleTraceListener

Sends trace messages to a standard output or error stream.

DelimitedListTraceListener

Similar to the TextWriterTraceListener class, sends messages to an instance of a stream-related class. However, the messages are separated with user-defined delimiters.

XmlWriterTraceListener

Saves traces messages as XML-encoded text.

Trace listeners inherit the TraceListener base class, which provides the core functionality of a listener. All listeners have a default constructor. Some listeners have multi-argument constructors that assign the listener a name or that define a destination. For example, the following code creates a new TextWriterTraceListener. It is named samplelistener and writes to the test.txt file.

TextWriterTraceListener file = new
    TextWriterTraceListener("samplelistener", "test.txt");

Table 15-19 details the properties of the TraceListener class.

Table 15-19. TraceListener properties

Property

Type

Attributes

Gets custom attributes that are defined in the application configuration file.

StringDictionary

Filter

Gets and sets the filter of the listener.

TraceFilter

IndentLevel

Gets and sets the level of indentation in the target.

int

IndentSize

Gets and sets the amount of indentation per indentation level in the target.

int

IsThreadSafe

Indicates whether the listener is thread-safe.

bool

Name

Gets and sets the name of the listener.

string

NeedIndent

Enables or disables indentation. This is a protected property.

bool

TraceOutputOptions

Gets and sets an enumeration that controls additional data sent to the listener.

TraceOptions

The values of the TraceOptions enumeration are listed in Table 15-20. These values are bitwise, which allows multiple options to be combined.

Table 15-20. TraceOptions values

Value

Description

TraceOptions.CallStack

Adds the call stack to tracing. The Environment.StackTrace property provides the call stack.

TraceOptions.DateTime

Adds the date and time in each trace message.

TraceOptions.LogicalOperationStack

Adds the logical operation stack to tracing. This information is provided by the orrelationManager.LogicalOperationStack property.

TraceOptions.None

Excludes any additional data.

TraceOptions.ProcessId

Adds the current process identifier in each trace message. This information is found at the Process.Id property.

TraceOptions.ThreadId

Adds the current thread identifier in each trace message. This information is found at the Thread.ManagedThreadId property.

TraceOptions.TimeStamp

Adds a timestamp in each trace message. This information is returned from the Stopwatch.GetTimeStamp method.

The listeners of a TraceSource type are set at the TraceSource.Listeners property. The Listeners property is a TraceListenerCollection type, which implements the standard interfaces of a collection. You can add a listener with the Add method. The TraceListenerCollection type also implements the AddRange method. Call this method to add multiple listeners to the Listeners collection in a single call. The AddRange method is overloaded to accept an array of listeners or a TraceListenerCollection parameter. The following code adds a ConsoleTraceListener to a Listeners collection:

ConsoleTraceListener console =
    new ConsoleTraceListener();
ts.Listeners.Add(console);

Trace messages can be filtered at the Listeners collection. Listener filters are perfect for identifying important messages when sent a flood of messages from the trace source. Both listener filters and trace switches are filters. A trace switch filters messages from the TraceSource object before being sent to a listener. The listener can filter the trace messages further. There are two types of listener filters: SourceFilter and EventTypeFilter. SourceFilter constrains the listener to a specific source. For example, when tracing messages from several classes, you use SourceFilter to limit trace messages to those generated by a specific type. EventTypeFilter filters trace messages based on priority. You can use SourceFilter and EventTypeFilter together or independently.

Here is the constructor of the SourceFilter type:

public SourceFilter(string source)

The only parameter is the name of the source. The listener will output only messages from the specific source. If the name is invalid, the filter is ignored.

Here is the constructor of the EventTypeFilter type:

public EventTypeFilter(SourceLevels level)

The SourceLevels parameter states the priority of trace messages that the listener will output. Other trace messages are ignored.

The following code demonstrates the SourceFilter type. The EventTypeFilter is demonstrated in an example later in this chapter. Three TraceSource instances are defined in the code. A ConsoleTraceListener is also defined, which displays trace messages in the console. ConsoleTraceListener.Filter then is updated to display trace messages from only the second trace source. Later, the SourceFilter is changed to limit trace messages to the first trace source:

#define TRACE

using System;
using System.Diagnostics;

namespace Donis.CSharpBook {
    public class Starter {

        public static void Main() {
          TraceSource ts1 = new TraceSource("ts1");
          TraceSource ts2 = new TraceSource("ts2");
          TraceSource ts3 = new TraceSource("ts3");
          SourceSwitch sw = new SourceSwitch("sw",
              "Information");
          ts1.Switch = sw;
          ts2.Switch = sw;
          ts3.Switch = sw;
          ConsoleTraceListener cs = new ConsoleTraceListener();
          ts1.Listeners.Add(cs);
          ts2.Listeners.Add(cs);
          ts3.Listeners.Add(cs);
          // Include only the ts2 source

          Console.WriteLine("Filters t1 and t3 messages");
          ts1.Listeners[1].Filter = new SourceFilter("ts2");
          ts1.TraceInformation("ts1:trace");
          ts2.TraceInformation("ts2:trace");
          ts3.TraceInformation("ts3:trace");

          // Include only the ts1 source

          Console.WriteLine("
Filters t2 and t3 messages");
          ts1.Listeners[1].Filter = new SourceFilter("ts1");
          ts1.TraceInformation("ts1:trace");
          ts2.TraceInformation("ts2:trace");
          ts3.TraceInformation("ts3:trace");

          ts1.Flush();
          ts2.Flush();
          ts3.Flush();

          ts1.Close();
          ts2.Close();
          ts3.Close();
        }
    }
}

Tracing Example

The following example code declares a ZClass and a YClass; both classes contain a TraceSource instance. It is a best practice to maintain separate TraceSource instances for different classes, which affords individualized management of tracing at the class level. For example, you could filter or expose trace messages from specific classes. Expose TraceSource as a static member of the class. Class methods use the static TraceSource member to send trace messages. If using the disposable pattern, clean up the TraceSource object in the Dispose method:

public class ZClass : IDisposable {
    static ZClass() {
        ts = new TraceSource("ZTrace");
        ts.Switch = new SourceSwitch("sw1", "All");
        ts.Listeners.Add(new ConsoleTraceListener());
        TextWriterTraceListener file = new
            TextWriterTraceListener("samplelistener", "test.txt");
        file.Filter = new EventTypeFilter(SourceLevels.Critical);
        file.TraceOutputOptions = TraceOptions.DateTime;
        ts.Listeners.Add(file);
    }
    static private TraceSource ts;
    public void MethodA() {
        ts.TraceEvent(TraceEventType.Error,
            GetHashCode(), "ZClass.MethodA");
    }

    public void MethodB(int parama, int paramb,
            int paramc) {
        ts.TraceData(TraceEventType.Critical,
            GetHashCode(), "ZClass.MethodB", parama,
            paramb, paramc);
    }
    public void Dispose() {
        ts.Flush();
        ts.Close();
    }
}
public class YClass : IDisposable {
    static YClass() {
        ts = new TraceSource("YTrace");
        ts.Switch = new SourceSwitch("sw2", "Information");
        ts.Listeners.Add(new ConsoleTraceListener());
        ts.Listeners[1].IndentSize = 4;
        ts.Listeners[1].IndentLevel = 2;
    }
    static private TraceSource ts;
    public void MethodC() {
        ts.TraceEvent(TraceEventType.Error,
            GetHashCode(), "YClass.MethodC");
    }
    public void Dispose() {
        ts.Flush();
        ts.Close();
    }
}

The following code is a complete listing of the sample code used in this section. Main also has a trace source, which sends trace messages to listeners for the Output window and the console. Both ZClass and YClass have separate trace sources. The methods of ZClass and YClass use their respective TraceSource instances to send trace messages. To demonstrate different methods for tracing, the TraceEvent and TraceData methods are called. The hash codes of the objects are used as the trace identifier, which identifies trace messages by class type and instance. It is sometimes useful to associate a trace message with a specific object. The trace source of ZClass sends trace messages to listeners for the Output window, for the console, and for a text file. The trace switch of the trace source does not filter trace messages—all messages are sent. However, the filter for the text file limits tracing to critical messages. The YClass trace source sends informational trace messages to listeners for the Output window and the console:

#define TRACE

using System;
using System.Diagnostics;

namespace Donis.CSharpBook {
    public class Starter {
    public static void Main() {

        TraceSource ts =
            new TraceSource("StarterTrace");
        ts.Switch = new SourceSwitch("sw3");
        ts.Switch.Level = SourceLevels.ActivityTracing;
        ts.Listeners.Add(new ConsoleTraceListener());
        ts.TraceEvent(TraceEventType.Start,
            0, "Starting");

        ZClass obj1 = new ZClass();
        obj1.MethodA();
        obj1.MethodB(1,2,3);

        YClass obj2 = new YClass();
        obj2.MethodC();

        ZClass obj3 = new ZClass();
        obj1.MethodA();
        obj1.MethodB(4,5,6);

        ts.TraceEvent(TraceEventType.Stop,
            0, "Stopping");

        obj1.Dispose();
        obj2.Dispose();
    }
}

public class ZClass: IDisposable {

    static ZClass() {
        ts = new TraceSource("ZTrace");
        ts.Switch = new SourceSwitch("sw1", "All");
        ts.Listeners.Add(new ConsoleTraceListener());
        TextWriterTraceListener file = new
            TextWriterTraceListener("samplelistener", "test.txt");
        file.Filter = new EventTypeFilter(SourceLevels.Critical);
        file.TraceOutputOptions = TraceOptions.DateTime;
        ts.Listeners.Add(file);

    }

    static private TraceSource ts;

    public void MethodA() {
        ts.TraceEvent(TraceEventType.Error,
            GetHashCode(), "ZClass.MethodA");
    }

    public void MethodB(int parama, int paramb,
            int paramc) {
        ts.TraceData(TraceEventType.Critical,
            GetHashCode(), "ZClass.MethodB",
            parama,  paramb, paramc);
    }

    public void Dispose() {
        ts.Flush();
        ts.Close();
    }
}

public class YClass: IDisposable {

    static YClass() {
        ts = new TraceSource("YTrace");
        ts.Switch = new SourceSwitch("sw2", "Information");
        ts.Listeners.Add(new ConsoleTraceListener());
        ts.Listeners[1].IndentSize = 4;
        ts.Listeners[1].IndentLevel = 2;
    }

    static private TraceSource ts;

    public void MethodC() {
        ts.TraceEvent(TraceEventType.Error,
            GetHashCode(), "YClass.MethodC");
    }

    public void Dispose() {
        ts.Flush();
        ts.Close();
    }
  }
}

Configuration File

Trace switches, listeners, and listener filters are configurable in an application configuration file. Using an application configuration file is the best practice and is preferable to programmatic configuration. That way, tracing is configurable without recompiling the application. Where there is a conflict between programmatic configuration and a configuration file, programmatic settings take precedence.

The application configuration file has the same name as the target assembly plus a .config extension. For example, the application configuration file for hello.exe is hello.exe.config. The application configuration file should be in the same directory as the assembly. For a software system, you can configure multiple applications within the system with a single publisher policy file, which is deployed in the global assembly cache. Visit this link for a primer on publisher policy files: http://msdn2.microsoft.com/en-us/library/dz32563a.

In the configuration file, the tracing configuration settings are placed within the system.diagnostics element. For the complete explanation of the system.diagnostics element, visit this link: http://msdn2.microsoft.com/en-us/library/1txedc80. Here is an example:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.diagnostics>
    </system.diagnostics>
</configuration>

The sources Element

Define the trace sources within the sources element. A specific trace source is declared in a sources element. The important attributes of the sources element are name and switchName. The name attribute is the name of the trace source, and switchName names the switch assigned to the trace source. Here is an example:

<sources>
    <source name="ZTrace" switchName="sw1">
    </source>
</sources>

The listeners Element

The listeners of a particular source are listed within the sources elements. The listeners element encapsulates the listeners of a trace source. Individual listeners are added to the Listeners collection with the add element. The key attributes of the add element are type, name, traceOutputOptions, and initializeData. The type attribute is the kind of listener, which is the target. The name attribute is the name assigned to the listener. The initializeData attribute is additional data used to create the listener, such as the target file name. The traceOutputOtions attribute adds optional data to trace messages sent to the listener, such as a timestamp. Here are example elements:

<sources>
    <source name="ZTrace" switchName="sw1">
        <listeners>
            <add name="tListener"
                type="System.Diagnostics.TextWriterTraceListener"
                initializeData="data.txt" />
            <add name="cListener"
                type="System.Diagnostics.ConsoleTraceListener"/>
        </listeners>
    </source>
</sources>

The sharedListeners Element

As shown in the previous code, the listeners element assigns a listener to a specific trace source. You also can share listeners. Listeners can be shared between multiple trace sources. Use the sharedListeners element to define shared listeners. A shared listener is added with an add element. Shared listeners are added to a specific trace source the same way as a regular listener, as demonstrated in the preceding example. For a shared listener, the add element need only have the name attribute, which identifies the shared listener:

<system.diagnostics>
    <sharedListeners>
        <add type="System.Diagnostics.ConsoleTraceListener"
            name="cListener"
            traceOutputOptions="None" />
    </sharedListeners>
</ system.diagnostics >

The switches Element

Switches are defined within the switches element. An individual switch is added with an add element. The basic attributes are name and value. The name attribute is the name of the switch, and the value attribute is the filter level. Here is an example:

<system.diagnostics>
    <switches>
        <add name="sw1" value="Critical" />
        <add name="sw2" value="Information" />
    </switches>
</system.diagnostics>

Tracing Example with a Configuration File

The previous example of configuring tracing programmatically has been rewritten to leverage an application configuration file. The following code is the new source code for the application. The switch and collection code is removed from the source code and placed in a configuration file:

#define TRACE

using System;
using System.Diagnostics;

namespace Donis.CSharpBook {
    public class Starter {
        public static void Main() {

            TraceSource ts =
                new TraceSource("StarterTrace");
            ts.TraceEvent(TraceEventType.Start,
                0, "Starting");

            ZClass obj1 = new ZClass();
            obj1.MethodA();
            obj1.MethodB(1,2,3);

            YClass obj2 = new YClass();
            obj2.MethodC();

            ZClass obj3 = new ZClass();
            obj1.MethodA();
            obj1.MethodB(4,5,6);

            ts.TraceEvent(TraceEventType.Stop,
                0, "Stopping");

            obj1.Dispose();
            obj2.Dispose();
    }
}

public class ZClass: IDisposable {

    static ZClass() {
        ts = new TraceSource("ZTrace");
    }

    static private TraceSource ts;

    public void MethodA() {
        ts.TraceEvent(TraceEventType.Error,
            GetHashCode(), "ZClass.MethodA");
    }

    public void MethodB(int parama, int paramb, int paramc) {
        ts.TraceData(TraceEventType.Critical,
            GetHashCode(), "ZClass.MethodB",
            parama, paramb, paramc);
    }

    public void Dispose() {
        ts.Flush();
        ts.Close();
    }
}

public class YClass: IDisposable {

    static YClass() {
        ts = new TraceSource("YTrace");
    }
    static private TraceSource ts;

    public void MethodC() {
        ts.TraceEvent(TraceEventType.Error,
            GetHashCode(), "YClass.MethodC");
    }

    public void Dispose() {
        ts.Flush();
        ts.Close();
    }
  }
}

The following configuration file sets up tracing for the application. The three trace sources share the same Console listener. The YTrace trace source also has an XML listener. The XML and Console listeners also record a date/time stamp with each trace. Otherwise, the results are essentially the same as the previous example using programmatic tracing:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.diagnostics>
        <sources>
            <source name="StarterTrace" switchName="sw3">
                <listeners>
                    <add name="cListener" />
                </listeners>
            </source>
            <source name="ZTrace" switchName="sw1">
                <listeners>
                    <add initializeData="data.txt"
                        type="System.Diagnostics.TextWriterTraceListener"
                        name="tListener" />
                    <add name="cListener" />
                </listeners>
            </source>
            <source name="YTrace" switchName="sw2">
                <listeners>
                   <add name="cListener" />
                   <add name="xListener" />
                </listeners>
            </source>
        </sources>
        <sharedListeners>
            <add initializeData="data.xml"
                type="System.Diagnostics.XmlWriterTraceListener"
                name="xListener" traceOutputOptions="DateTime" />
            <add type="System.Diagnostics.ConsoleTraceListener" name="cListener"
                traceOutputOptions="DateTime" />
        </sharedListeners>
        <switches>
            <add name="sw1" value="Information" />
            <add name="sw2" value="All" />
            <add name="sw3" value="ActivityTracing" />
        </switches>
    </system.diagnostics>
</configuration>
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.14.131.212