Programming with Trace Listeners

Trace and Debug text has to go somewhere. We already know that by default Write and WriteLine information is displayed in the Output window. The Output window and other such recipients of Trace and Debug information are referred to as trace listeners .

It is clearly possible that some internal mechanism writes to the Output window directly, but this isn't exactly what happens. Both the Trace and Debug classes have a Listeners property that is a collection. Trace and Debug share the same underlying Listeners collection, so adding a listener to one classfor example, Trace.Listeners effectively adds a listener to the other. There are four possible kinds of listeners: DefaultTraceListener (which is comprised of the Output window), TextWriterTraceListener , EventLogTraceListener , and any custom trace listener you define. A custom trace listener is any class that inherits from TraceListener and is added to a Listeners collection.

Every Listener collection already has the DefaultTraceListener added to it. A TextWriterTraceListener is literally an instance of the TextWriterTraceListener class initialized with any Stream class. The Stream class supports the mechanism for writing. Thus, to create a custom file-logging mechanism you can create an instance of the TextWriterTraceListener and initialize it with a FileStream object; Trace and Debug messages will be written to a file. The EventLogTraceListener directs Trace and Debug information to an event log, and a custom trace listener sends Trace and Debug information wherever you define it to go. Let's go over a couple of examples that demonstrate how to use each kind of trace listener (except for the default Trace and Debug behavior supported by DefaultTraceListener , which doesn't need further explanation).

Creating a FileStream Trace Listener

It is reasonable to want to send information about the internal behavior of your application to some kind of external log file, so you can come back at a later date and time to diagnose your application. You can certainly create some custom mechanism as you may have done with VB6, or you can simply use TextWriterTraceListener and a FileStream object. (Think of a FileStream object as a representation of the data in a file in one continuously flowing rivulet, with a beginning and an end.)

For example, if we elect to log calculations made by the Calculator application to support an external auditor verifying the accuracy of the engine's calculations, we could log all the calculations and results and e-mail the file. Upon certification the calculator engine could be approved for use in calculating mortgage payments at a bank. (It could happen.) The steps we would need to take appear below.

  1. Create a FileStream object.

  2. Initialize TextWriterTraceListener with the FileStream object.

  3. Add the listener to the Trace.Listeners collection.

  4. Ensure we have some agreed-upon statements that trace the application's behavior, then let it rip.

Listing 17.3 shows code we might add to support sending calculation information to an external log file. To actually send messages to the log file all we need to do is include Trace.WriteLine messages as we normally would.

Listing 17.3 Initializing an Instance of TextWriterTraceListener
 Public Sub New()   Dim Stream As FileStream = _     New FileStream("trace.log", FileMode.Create)   Dim Listener As TextWriterTraceListener = _     New TextWriterTraceListener(Stream)    Trace.Listeners.Add(Listener)    Trace.WriteLine("Calculator Constructor") End Sub 

The FileStream class requires the System.IO namespace, and TextWriterTraceListener comes to us from the System.Diagnostics namespace. The constructor in Listing 17.2 creates a new FileStream object, initializes a new trace listener with the FileStream object, and adds the TextWriterTraceListener instance to the Trace.Listeners collection.

Now if we add Trace.WriteLine (or Trace.Write ) statements to our program, they will appear in the Output window and in a file named trace.log . For example, adding Trace.WriteLine(Key) to the Calculator.SendKey method included in Listing 17.1 will tell us about all the buttons and keys sent to the calculator. Listing 17.4 is a sample listing of the contents of the trace.log file after Trace statements were added to the Calculator application's Constructor method, SendKey method, and nested Operand constructor.

Listing 17.4 An Example of the trace.log File
 Calculator Constructor 9 Key:9 New Operand: 9 8 Key:8 New Operand: 8 New Operand: 72 

It is a good idea to call Trace.Flush before closing the application, especially when using a FileStream object. This ensures that all the trace data has been written to file. I usually add Trace.Flush to the form's Closing event. Because it is safe to call Flush on Trace whether or not we have created an additional listener, we don't have to worry about timing issues.


Normally we would want to hang on to a FileStream object and call its Close method. I double-checked Rotor and verified that TextWriterTraceListener does this for us when we use FileStream in conjunction with TextWriterTraceListener . If you use a FileStream object as a separate close, you will want to close the stream when you are finished using it. You can verify this information for yourself by checking out the Dispose method for the TextWriterTraceListener class in the Rotor file with the same name .

Using the Trace class and trace listeners for application logging means that you can effectively manage logging based on whether or not the TRACE constant is defined. To turn off tracing you don't have to remove all your important Trace.WriteLine statements; simply undefine the TRACE constant in the Build Property Pages (Figure 17.8).

Directing Trace Information to an Event Log

Instead of creating a custom log file format, why not use an existing format that comes with a known tool, the event log? Just as easily as we created the TextWriterTraceListener instance, we can create an EventLogTraceListener instance and use the Microsoft Management Console and snap-in to view our trace data as event log entries. Listing 17.5 shows an alternate constructor for the Calculator application that will send all our Trace statements to the event log. Note that I didn't change anything else about the sample code other than the revision to the constructor from Listing 17.3.

Listing 17.5 Using EventLogTraceListener to Send Data to the Event Log
 Public Sub New()   Dim Listener As EventLogTraceListener = _     New EventLogTraceListener("Calculator")   Trace.Listeners.Add(Listener)   Trace.WriteLine("Calculator Constructor") End Sub 

The alternate constructor in Listing 17.5 creates an EventLogTraceListener instance. We need not modify any of our Trace statements; the results will now simply go to the event log in addition to anywhere else we may be sending Trace and Debug data. Figure 17.11 shows the event log version of one of the Trace statements output to the earlier trace.log text file (Listing 17.4).

Figure 17.11. Trace information as it appears when written to the event log by using EventLogTraceListener .


You have to be circumspect when using the event log. If you send too much information to the event log, you are likely to fill it up and induce problems related to a full event log. (Refer to the Logging Application Events section later in this chapter for more information on using the event log.)

Creating a Custom Trace Listener

We need to define a new class that inherits from TraceListener to implement a custom trace listener. TraceListener requires that we provide an implementation for an overridable and overloadable version of a Write and WriteLine method. Inheriting from TraceListener and implementing Write and WriteLine is technically very easy. Deciding what to do with your custom trace listener is where the work is involved. How much effort you put into your listener is up to your imagination .

Listing 17.6 demonstrates a custom trace listener that dynamically creates a form with a text box; Trace information is sent to that form's TextBox control. As is the case with trace listeners in general, you do not have to change your Trace statements simply because you have added an additional trace listener to the Listeners collection.

Listing 17.6 Implementing a Custom Trace Listener
 1:  Imports System.Diagnostics 2:  Imports System.Windows.Forms 3: 4:  Public Class MyListener 5:    Inherits TraceListener 6: 7:    Public Overloads Overrides Sub WriteLine(ByVal Message As String) 8:      Form.Controls(0).Text = Formatted(Message) + vbNewLine + _ 9:        Form.Controls(0).Text + vbNewLine 10:   End Sub 11: 12:   Public Overloads Overrides Sub Write(ByVal Message As String) 13:     Form.Controls(0).Text = Formatted(Message) + _ 14:       Form.Controls(0).Text 15:   End Sub 16: 17:   Private Function Formatted(ByVal Message As String) As String 18:     Return String.Format("{0}: {1}", DateTime.Now, Message) 19:   End Function 20: 21:   Private ReadOnly Property Form() As Form 22:   Get 23:     Return GetForm() 24:   End Get 25:   End Property 26: 27:   Private OutputForm As Form = Nothing 28:   Private Function GetForm() As Form 29: 30:     If (OutputForm Is Nothing) Then 31:       OutputForm = New Form() 32:       AddHandler OutputForm.Closed, AddressOf OnClosed 33:       Dim TextBox As TextBox = New TextBox() 34:       OutputForm.Controls.Add(TextBox) 35:       TextBox.Dock = DockStyle.Fill 36:       TextBox.Multiline = True 37:       OutputForm.Show() 38:     End If 39: 40:     Return OutputForm 41:   End Function 42: 43:   Private Sub OnClosed(ByVal sender As Object, _ 44:     ByVal e As EventArgs) 45: 46:     OutputForm = Nothing 47: 48:   End Sub 49: 50: End Class 

The requirements of inheriting the TraceListener class (line 5) are fulfilled by providing an implementation for WriteLine (lines 7 through 10) and an implementation for Write (lines 12 through 15). The rest of the code uses lazy instantiationsee GetForm in lines 28 through 41to create a form with a text box as the actual recipient of the output text. (You can find the code for Listing 17.6 in the MyListener.vb source code file as part of the sample application for this chapter.)


Did you know that you can send Trace information to the console by initializing a TextWriterTraceListener instance with the standard output stream represented by Console.Out ? Check out the TraceListener.sln file for a quick example.

Visual Basic. NET Power Coding
Visual Basic(R) .NET Power Coding
ISBN: 0672324075
EAN: 2147483647
Year: 2005
Pages: 215
Authors: Paul Kimmel © 2008-2017.
If you may any questions please contact us: