Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

An HTML Trace Listener for Windows Forms

0.00/5 (No votes)
20 Nov 2007 1  
Display formatted trace messages in a browser control
Screenshot - HtmlTrace.jpg

Introduction

HtmlTraceListener is a TraceListener that formats its output as HTML. The HTML is then displayed in a TraceBrowser control on a Windows Form. Cascading Style Sheets and scripts may be used to format the text and to control its presentation.

Background

A well instrumented program will contain statements for tracing execution and for logging important events. Typically, trace messages are written to a file or database for later examination. However, it can be very useful to view the trace messages as they are generated. This is especially so when diagnosing problems in a release build where the debugger's output window is not available.

The .NET Framework Class Library's Debug, Trace, and TraceSource classes allow a programmer to output diagnostic messages. The Trace class has Write, TraceInformation, TraceWarning, and other methods to write the messages. The Debug class is very similar to Trace but has no effect in debug builds. The TraceSource class, which is new to .NET Framework 2.0, has TraceEvent and TraceData methods which allow additional information to be placed in the trace output. Such information includes the date and time, the thread ID, the event type, and the complete call stack.

The trace classes direct their output to one or more TraceListeners. The DefaultTraceListener writes to the debugger's output window, the EventLogTraceListener to the NT event log, and the TextWriterTraceListener to a stream. The Code Project contains several additional TraceListeners that write to UDP, to XML files, to named pipes, to a text box, and elsewhere.

Writing trace message to a text box can be very useful when diagnosing a problem in a release build, providing the equivalent of the debugger's output window. However, by outputting to HTML, the trace messages may be formatted in helpful ways. For example, errors can be colored red while informational messages are colored green. Scripts may be used to reveal hidden details such as the thread ID or callstack.

Using the Code

To use the HTML trace, first add a TraceBrowser control to the Windows Form by dragging it from the toolbox onto the Windows Form. TraceBrowser supports several properties that can be set in the Properties window:

  • MaxMessages/MinMessages - To limit the number of messages on the HTML page, the TraceBrowser control discards the oldest messages when MaxMessages has been reached. It discards messages until MinMessages remain.
  • EmptyDocument - The initial HTML page into which messages are inserted. Typically, it contains a style element for formatting the messages. It may also contain a script element to give the page additional features.
  • DateTimeFormat, MessageTypeFormat, SourceFormat, StackContentsFormat, ThreadIdFormat - The format string used to format these values. The format string may contain additional text to label the fields, e.g. "Thread ID {0}" for the ThreadIdFormat property. Suppress a field entirely by supplying an empty string.
  • DingbatText, CallstackText - Static text to be inserted into the message. These are explained further under Message format.

Next, initialize the TraceListener. A good place to do this is in the form's OnLoad method.

// Begin loading the empty document into the browser control,
// then initialize the trace listener.
this.traceBrowser.BeginInitialize();
HtmlTraceListener listener = new 
  HtmlTraceListener("HtmlListener", this.traceBrowser);
listener.TraceOutputOptions = 
  TraceOptions.DateTime | TraceOptions.ThreadId | 
  TraceOptions.Callstack;
Trace.Listeners.Add( listener );

Issuing trace message is trivial. For example:

Trace.TraceError("The input file was invalid.");
myTraceSource.TraceEvent(TraceEventType.Warning, 3, 
  "The input file {0} is missing.", fileName);
Debug.Print("Entering MyClass.MyMethod");

Design

HTML trace is implemented in two classes. HtmlTraceListener is derived from TraceListener. It overrides the Write, WriteLine, TraceEvent, and TraceData methods. Essentially, each of these methods pass the message to the TraceBrowser object. The TraceBrowser class is derived from WebBrowser. It contains methods for formatting a message as HTML and for adding it to the HTML document.

Message Format

Each trace message is formatted as a DIV containing a number of SPAN tags.

<DIV class='Warning' >
  <SPAN class='Dingbat'>+</SPAN>
  <SPAN class='DateTime'>1:23</SPAN>
  <SPAN class='Source'>foo.exe</SPAN>
  <SPAN class='ThreadId'>1</SPAN>
  <SPAN class='MessageType' >Warning</SPAN>
  <SPAN class='MessageText'>
    Line 1.<BR />
    Line 2
  </SPAN>
  <SPAN class='Callstack'>Call Stack
    <SPAN class='StackContents'>
      a calls b<BR />
      b call c.<BR />
    </SPAN>
  </SPAN>
</DIV>

The DIV tag encompasses the entire message. Within the DIV are separate SPANs for each field of the message.

  • The dingbat is used to mark the start of the message. An onclick handler can be used to expand and collapse the message.
  • DateTime is the time the message was issued.
  • Source indicates the source of the message, typically the program name. This field is more useful if the program has multiple TraceSource objects.
  • MessageType indicates the type of message: WriteLine, Warning, Error, etc.
  • MessageText is the actual message.
  • Callstack contains the complete callstack when the message was generated. The Callstack tag contains static text while the nested CallstackContents tag contains the actual call stack.

Points of Interest

MultiThreading

If the application has multiple threads, each thread may be generating trace messages. TraceBrowser ensures that all modifications to the HTML document occur on the message-loop thread using the standard sequence:

public void AppendMessage(MessageType messageType, ...)
{
  // Switch to the message-loop thread if necessary.
  if( InvokeRequired ) 
  { this.Invoke( m_appendMessage, messageType, ...);
    return;
  }
  ...

TraceSource.TraceEvent calls TraceListener.TraceEvent to output a message. The latter method accepts a TraceEventCache object that contains the process ID, the thread ID, callstack and other values. However, TraceEventCache object defers obtaining the thread ID until the property's get method is called. Thus, if a TraceEventCache is passed from one thread to another, the thread ID will spontaneously change, giving incorrect results. To avoid this problem, HtmlTraceListener extracts the needed values from the TraceEventCache and passes them individually to TraceBrowser.

Trace.Write writes message text but does not terminate the line. Trace.Writeline and all TraceSource.TraceXxx methods terminate the line. In fact, the default implementation of TraceEvent calls both Write and WriteLine to create its output. User code should generally not call Trace.Write in a multithreaded application because the text can become intermingled. TraceBrowser takes care not to insert text from multiple threads into the same message.

Styles and Scripts

The TraceBrowser.EmptyDocument property contains a complete HTML page into which message elements are inserted. The default page colorizes the message text according to its importance - critical messages are red, warnings are purple, etc. The default style sheet also suppresses display of some fields. By providing a page template, the programmer can radically change the appearance of the page and even the information that is presented. Even greater versatility is gained by adding scripts to the page. The demonstration application comes with a DynamicTrace.htm page that initially shows only the message, but can be expanded by clicking on the plus sign preceding each message.

History

  • 20th November, 2007 -- Original version posted

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here