Sunday, January 20, 2008

About Trace Listeners

By using trace listeners we can achieve debugging and tracing tasks in the easy way. We can configure trace listeners by source code or at runtime using the application config file (app.config).

The source code method:

// First step: create the trace source object
TraceSource ts = new TraceSource("myTraceSource");
 
// Second step: create some trace listeners
TextWriterTraceListener tr = new TextWriterTraceListener("C:\\outfile.txt");
XmlWriterTraceListener xt = new XmlWriterTraceListener("C:\\outfile.xml");
DelimitedListTraceListener dt = new DelimitedListTraceListener("C:\\outfile.csv.txt");
EventLogTraceListener et = new EventLogTraceListener("myappp");
 
// Third step: configuring our trace source and trace listeners
ts.Switch = new SourceSwitch("mySwitch", "my switch");
ts.Switch.Level = SourceLevels.Warning; // Enable only warning, error and critical events
 
// Configuring trace listeners
dt.Delimiter = "|";
dt.TraceOutputOptions = TraceOptions.DateTime | TraceOptions.Timestamp;
xt.TraceOutputOptions = TraceOptions.DateTime | TraceOptions.Timestamp;
tr.TraceOutputOptions = TraceOptions.DateTime | TraceOptions.Timestamp | TraceOptions.Callstack;
 
// Adding our trace listeners
ts.Listeners.Clear();
ts.Listeners.Add(et);
ts.Listeners.Add(dt);
ts.Listeners.Add(tr);
ts.Listeners.Add(xt);
 
// Setting autoflush to save files automatically
Trace.AutoFlush = true;
 
// Writing out some events
ts.TraceEvent(TraceEventType.Warning, 0, "warning message");
ts.TraceEvent(TraceEventType.Error, 0, "error message");
ts.TraceEvent(TraceEventType.Information, 0, "information message");
ts.TraceEvent(TraceEventType.Critical, 0, "critical message");
The application configuration file method:

We can achieve the same task above using the application configuration file. This allow you to change settings at runtime when you need some tracing information.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.diagnostics>
    <trace autoflush="true"/>
    <sources>
      <source name="myTraceSource"
              switchName="mySwitch"
              switchType="System.Diagnostics.SourceSwitch" >
        <listeners>
          <clear/>
          <add name="evengloglistener"
            type="System.Diagnostics.EventLogTraceListener"
            initializeData="myApp" />
          <add name="delimitedListener"
            type="System.Diagnostics.DelimitedListTraceListener"
            delimiter="|"
            initializeData="c:\outfile.csv.txt"
            traceOutputOptions="ProcessId, DateTime" />
          <add name="textwriterListener"
            type="System.Diagnostics.TextWriterTraceListener"
            initializeData="c:\outfile.txt"
            traceOutputOptions="ProcessId, DateTime, Callstack" />
          <add name="xmlListener"
            type="System.Diagnostics.XmlWriterTraceListener"
            initializeData="c:\outfile.xml"
            traceOutputOptions="ProcessId, DateTime" />
        </listeners>
      </source>
    </sources>
    <switches>
      <add name="mySwitch" value="Warning" />
    </switches>
  </system.diagnostics>
</configuration>
The source code now looks simple like this:

// First step: create the trace source object
TraceSource ts = new TraceSource("myTraceSource");
 
// Writing out some events
ts.TraceEvent(TraceEventType.Warning, 0, "warning message");
ts.TraceEvent(TraceEventType.Error, 0, "error message");
ts.TraceEvent(TraceEventType.Information, 0, "information message");
ts.TraceEvent(TraceEventType.Critical, 0, "critical message");
CorrelationManager

We can use the CorrelationManager to group correlated traces. The switch level must include the LogicalOperationStack option.

Using source code configuration:

tr.TraceOutputOptions = TraceOptions.DateTime | TraceOptions.Timestamp | TraceOptions.LogicalOperationStack;
Or using the Application configuration file:

<add name="textwriterListener"
  type="System.Diagnostics.TextWriterTraceListener"
  initializeData="c:\outfile.txt"
  traceOutputOptions="ProcessId, DateTime, LogicalOperationStack" />
And the code must looks like:

// Start the first logical operation
Trace.CorrelationManager.StartLogicalOperation("first logical operation");
 
ts.TraceEvent(TraceEventType.Warning, 0, "warning message");
ts.TraceEvent(TraceEventType.Error, 0, "error message");
 
// Start a nested logical operation
Trace.CorrelationManager.StartLogicalOperation("second logical");
 
ts.TraceEvent(TraceEventType.Information, 0, "information message");
 
// Stop the second logical operation
Trace.CorrelationManager.StopLogicalOperation();
 
ts.TraceEvent(TraceEventType.Critical, 0, "critical message");
 
// Stop the first logical operation
Trace.CorrelationManager.StopLogicalOperation();
Another useful technique is filter some kind of events and redirect them to one specific listener, for example, critical errors must be logged to a file and warning events to the event log only.

tr.Filter = new EventTypeFilter(SourceLevels.Error);
Or

<add name="textwriterListener"
  type="System.Diagnostics.TextWriterTraceListener"
  initializeData="c:\outfile.txt"
  traceOutputOptions="ProcessId, DateTime, LogicalOperationStack">
  <filter type="System.Diagnostics.EventTypeFilter"
    initializeData="Error"/>
</add>
NOTE: you can have more than one trace source, for example, one for normal data and another for critical and private data.

By using the shareListeners setting you can add global listeners:

<sharedListeners>
  <add name="myListener" 
    type="System.Diagnostics.TextWriterTraceListener" 
    initializeData="myListener.log">
    <filter type="System.Diagnostics.EventTypeFilter" 
      initializeData="Error"/>
</add>

5 comments:

Anonymous said...

nice tutorial. thanks!

Anonymous said...

Extremely helpful, simple tutorial. Thank you!

Tester said...

Thanks you very much!

Tester said...
This comment has been removed by the author.
Anonymous said...

Thanks, much appreciated.


View My Stats