A Tracing Primer - Part II (A) [Mike Rousos]

In my introduction to tracing (https://blogs.msdn.com/bclteam/archive/2005/03/15/396431.aspx), I outlined the basics of how to use TraceSources, TraceListeners, and SourceSwitches to trace the flow of an application. I also covered how to configure Whidbey tracing with a configuration file.

In this series of three follow-up articles, I plan to discuss the use of TraceFilters, custom listeners, and a recent change we made to where config-defined listeners create their output files.

This first section (part A), I will focus on TraceFilters.

Filters

In the previous article, I discussed all of the items in my tracing illustration tracing illustration except for filters. As depicted, they fit between the switch and the listeners. They function very much like switches in that they limit what actually gets traced by the listeners. The difference is that each listener can have its own filter, whereas a switch is applied to a trace source and all attached filters. This means that a filter allows for more granular tracing. For example, the switch may be loose allowing most traces through, but individual filters can allow some listeners to trace all of the switch-approved messages, while others will only trace those that meet some more restrictive condition.

The Event Type Filter

The most common filter is the EventTypeFilter. I will use it as an example of how filters in general might be used. Every EventTypeFilter has a single property – EventType which is of type SourceLevels. This is the same type as a switch’s level. A listener’s event type filter can have its event type set and from then on, the listener will only trace events which are that event type or more important. Below is a configuration file that demonstrates this sort of granular switching.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.diagnostics>
<sources>
<source name="mySrc" switchName="mySwitch" >
<listeners>
<clear/>
<add name="XmlListener" type="System.Diagnostics.XmlWriterTraceListener" initializeData="output.xml">
<filter type="System.Diagnostics.EventTypeFilter" initializeData="Error" />
</add>
<add name="ConsoleListener" type="System.Diagnostics.ConsoleTraceListener" >
<filter type="System.Diagnostics.EventTypeFilter" initializeData="All" />
</add>
</listeners>
</source>
</sources>
<switches>
<add name="mySwitch" value="Information" />
</switches>
</system.diagnostics>
</configuration>

In the config file above, the switch is set to trace any event of information-level importance or greater. The two listeners on the source, though, will trace different events. The console trace listener will trace any events that come to it (the filter is set to all). Of course, with the current setup, it will not trace activity level trace events because they won’t make it by the source’s switch. If the source switch were to loosen, though, this listener would trace these events as well.

By contrast, the xml writer trace listener will only record relatively severe events (those of error severity or greater). This will help to keep the ouput.xml file smaller while still recording the most important messages in this more verbose format.

Of course, filters can also be used programmatically (like any tracing component), as demonstrated in the sample application below.

using System;
using System.Diagnostics;
 
class myClass
{
    static TraceSource mySource = new TraceSource("mySrc");
 
    static void Main()
    {
        mySource.Switch.Level = SourceLevels.Information;
 
        ConsoleTraceListener ctl = new ConsoleTraceListener();
        ctl.Filter = new EventTypeFilter(SourceLevels.All);
        mySource.Listeners.Add(ctl);
 
        XmlWriterTraceListener xwtl = new XmlWriterTraceListener("output.xml");
        xwtl.Filter = new EventTypeFilter(SourceLevels.Error);
        mySource.Listeners.Add(xwtl);
 
 
        mySource.TraceEvent(TraceEventType.Error, 5, "Testing... This is an error.");
        mySource.TraceEvent(TraceEventType.Information, 11, "Testing... This is some information.");
        mySource.TraceEvent(TraceEventType.Start, 4, "Testing... This is an activity event.");
        mySource.TraceEvent(TraceEventType.Warning, 20, "Testing... This is a warning.");
 
        mySource.Flush();
        mySource.Close();
    }
}

The Source Filter

The source filter is the second filter type that comes with the framework and is less commonly used than the event type filter. It also has a single property. In this case, though, the filter’s listener will only trace if the source tracing has the name specified by the filter’s source property.

The most common scenario for this listener is to limit how much tracing is done in a case where multiple sources are sharing a listener and a user wants to quickly focus on one source’s output.

The below configuration file demonstrates the use of this filter.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <system.diagnostics>
      <sources>
            <source name="mySrc" switchName="mySwitch" >
            <listeners>
                  <add name="ConsoleListener" />
            </listeners>
          </source>
            <source name="myOtherSrc" switchName="mySwitch" >
            <listeners>
                  <add name="ConsoleListener" />
            </listeners>
          </source>
            <source name="myThirdSrc" switchName="mySwitch" >
            <listeners>
                  <add name="ConsoleListener" />
            </listeners>
          </source>
        </sources>
      <switches>
            <add name="mySwitch" value="Information" />
      </switches>
      <sharedListeners>
            <add name="ConsoleListener" type="System.Diagnostics.ConsoleTraceListener" >
                      <filter type="System.Diagnostics.SourceFilter" initializeData="mySrc" />
            </add>           
      </sharedListeners>
    </system.diagnostics>
</configuration>

In the above example, there are three trace sources. This may indicate that there are three sections of the application that each will have tracing instrumentation. Having distinct sources for different components of an application is a common method of keeping the trace messages easily distinguishable. Each source, though, writes to the same listener. Again, this is a common practice because it is convenient to not have many tracing output files come out of a single app. Here, all tracing output will be consolidated which is useful.

 

There may be a time, though, when the user is interested only in trace output from a particular source. Although this could be accomplished in multiple ways, one of the easiest is to simply add a source filter to the listener. This solution is what is demonstrated in the configuration file above.

 

Closing

 

I hope that this overview gives a good introduction to using filters in tracing. Parts B and C of this tracing primer will delve into other topics - namely custom listeners and how relative paths work in config files.