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.
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 SPAN
s 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, ...)
{
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