Windows 窗体的 HTML 跟踪侦听器
在浏览器控件中显示格式化的跟踪消息。

引言
HtmlTraceListener
是一个 `TraceListener`,它将输出格式化为 HTML。然后,HTML 显示在 Windows 窗体上的 `TraceBrowser` 控件中。级联样式表和脚本可用于格式化文本并控制其显示方式。
背景
一个良好插桩的程序将包含用于跟踪执行和记录重要事件的语句。通常,跟踪消息会写入文件或数据库以供以后检查。然而,能够查看正在生成的跟踪消息可能会非常有益。在调试器输出窗口不可用的发布版本中诊断问题时尤其如此。
.NET Framework 类库中的 `Debug`、`Trace` 和 `TraceSource` 类允许程序员输出诊断消息。`Trace` 类具有 `Write`、`TraceInformation`、`TraceWarning` 等方法来写入消息。`Debug` 类与 `Trace` 非常相似,但在调试构建中没有效果。`TraceSource` 类(.NET Framework 2.0 新增)具有 `TraceEvent` 和 `TraceData` 方法,允许在跟踪输出中包含额外信息。这些信息包括日期和时间、线程 ID、事件类型以及完整的调用堆栈。
跟踪类将其输出定向到一个或多个 `TraceListeners`。`DefaultTraceListener` 写入调试器输出窗口,`EventLogTraceListener` 写入 NT 事件日志,`TextWriterTraceListener` 写入流。Code Project 包含几个额外的 `TraceListeners`,它们写入 UDP、XML 文件、命名管道、文本框等。
将跟踪消息写入文本框在调试发布版本中的问题时可能非常有益,它提供了与调试器输出窗口等效的功能。然而,通过输出到 HTML,跟踪消息可以以有用的方式进行格式化。例如,错误可以显示为红色,而信息性消息可以显示为绿色。脚本可用于显示隐藏的详细信息,如线程 ID 或调用堆栈。
Using the Code
要使用 HTML 跟踪,首先将 `TraceBrowser` 控件添加到 Windows 窗体中,方法是从工具箱将其拖到 Windows 窗体上。`TraceBrowser` 支持在“属性”窗口中设置的多个属性
MaxMessages
/MinMessages
- 为了限制 HTML 页面上的消息数量,当达到 `MaxMessages` 时,`TraceBrowser` 控件会丢弃最旧的消息。它会丢弃消息,直到剩下 `MinMessages`。EmptyDocument
- 插入消息的初始 HTML 页面。通常,它包含一个样式元素用于格式化消息。它还可能包含一个脚本元素,为页面添加附加功能。DateTimeFormat
,MessageTypeFormat
,SourceFormat
,StackContentsFormat
,ThreadIdFormat
- 用于格式化这些值的格式化字符串。格式字符串可能包含额外的文本来标记字段,例如,“Thread ID {0}”用于 `ThreadIdFormat` 属性。通过提供一个空字符串来完全隐藏一个字段。DingbatText
,CallstackText
- 要插入到消息中的静态文本。这些将在“消息格式”下进一步解释。
接下来,初始化 `TraceListener`。一个不错的地方是在窗体的 `OnLoad` 方法中。
// 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 );
发出跟踪消息非常简单。例如
Trace.TraceError("The input file was invalid.");
myTraceSource.TraceEvent(TraceEventType.Warning, 3,
"The input file {0} is missing.", fileName);
Debug.Print("Entering MyClass.MyMethod");
设计
HTML 跟踪在两个类中实现。`HtmlTraceListener` 派生自 `TraceListener`。它重写了 `Write`、`WriteLine`、`TraceEvent` 和 `TraceData` 方法。本质上,这些方法中的每一个都将消息传递给 `TraceBrowser` 对象。`TraceBrowser` 类派生自 `WebBrowser`。它包含将消息格式化为 HTML 并将其添加到 HTML 文档的方法。
消息格式
每条跟踪消息都格式化为一个 `DIV`,其中包含多个 `SPAN` 标签。
<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>
`DIV` 标签包含整个消息。在 `DIV` 中,每个消息字段都有单独的 `SPAN`。
- `dingbat` 用于标记消息的开始。`onclick` 处理程序可用于展开和折叠消息。
DateTime
是发出消息的时间。Source
表示消息的来源,通常是程序名称。如果程序有多个 `TraceSource` 对象,此字段会更有用。MessageType
表示消息的类型:`WriteLine`、`Warning`、`Error` 等。MessageText
是实际的消息。Callstack
包含生成消息时的完整调用堆栈。`Callstack` 标签包含静态文本,而嵌套的 `CallstackContents` 标签包含实际的调用堆栈。
关注点
多线程
如果应用程序有多个线程,每个线程都可能生成跟踪消息。`TraceBrowser` 确保所有对 HTML 文档的修改都发生在消息循环线程上,使用标准的序列
public void AppendMessage(MessageType messageType, ...)
{
// Switch to the message-loop thread if necessary.
if( InvokeRequired )
{ this.Invoke( m_appendMessage, messageType, ...);
return;
}
...
TraceSource.TraceEvent
调用 `TraceListener.TraceEvent` 来输出消息。后者方法接受一个 `TraceEventCache` 对象,其中包含进程 ID、线程 ID、调用堆栈和其他值。但是,`TraceEventCache` 对象会推迟获取线程 ID,直到调用属性的 `get` 方法。因此,如果一个 `TraceEventCache` 从一个线程传递到另一个线程,线程 ID 会意外更改,导致结果不正确。为了避免这个问题,`HtmlTraceListener` 会从 `TraceEventCache` 中提取所需的值,并将它们单独传递给 `TraceBrowser`。
Trace.Write
写入消息文本但不换行。`Trace.Writeline` 和所有 `TraceSource.TraceXxx` 方法都会换行。事实上,`TraceEvent` 的默认实现会调用 `Write` 和 `WriteLine` 来创建其输出。在多线程应用程序中,用户代码通常不应调用 `Trace.Write`,因为文本可能会混合在一起。`TraceBrowser` 会注意不将来自多个线程的文本插入到同一条消息中。
样式和脚本
TraceBrowser.EmptyDocument
属性包含一个完整的 HTML 页面,消息元素将插入其中。默认页面根据消息的重要性对其文本进行着色 - 关键消息为红色,警告为紫色等。默认样式表还会抑制某些字段的显示。通过提供页面模板,程序员可以彻底改变页面的外观,甚至改变显示的信息。通过向页面添加脚本,可以获得更大的灵活性。演示应用程序附带一个 *DynamicTrace.htm* 页面,该页面最初只显示消息,但可以通过单击每条消息前面的加号来展开。
历史
- 2007 年 11 月 20 日 -- 发布初始版本