BaseLib.Tracer: 正确进行跟踪






4.30/5 (10投票s)
近实时、多线程安全跟踪,适用于任何 .NET 技术
Content
简介
在每个 .NET 项目中,无论使用何种技术,从一开始就需要一些基本功能
跟踪以及如何处理异常。
当然,.NET 和 Visual Studio 提供了一些跟踪功能,但是
- 使用起来很不方便(
Trace
,Debug
,TraceSource
,TraceListener
,TraceSource
,TraceSwitch
, ...) - Visual Studio 的跟踪太慢而且阻塞,对于多线程应用程序来说毫无用处
- 跟踪处理在调用线程上运行,跟踪异常会给调用者带来问题
跟踪和错误处理的要求
- 从第一行代码即可使用(无需初始设置)
- 花费时间非常少(大约一微秒)
- 跟踪处理在其自己的线程上执行
- 跟踪持续运行,不会引起任何存储问题
- 跟踪消息过滤和相应处理
- 易于扩展(将跟踪存储在数据库中,发送电子邮件处理某些问题,在 GUI 中显示某些消息等)
BaseLib.Tracer
将跟踪消息写入一个快速缓冲区。另一个线程清空缓冲区,处理跟踪,最后将消息存储在一个环形缓冲区中,旧的跟踪消息会被覆盖。对于调试和异常调查,通常只需要最新的跟踪消息就足够了。
它可以配置为将一些跟踪消息永久存储到文件,和/或使用任何自定义实现的跟踪处理。
典型用例
- 应用程序从第一行代码开始就将其正在发生的事情写入跟踪,例如它尝试打开哪些文件或其他它尝试访问的资源。如果应用程序未能启动,跟踪信息将显示哪些功能已成功完成,以及哪些功能导致启动失败。当应用程序在远程计算机上运行时,此信息非常有用,因为在远程计算机上很难观察到正在发生的事情。
- 当发生异常时,跟踪信息会包含在异常消息中,显示在异常发生之前 (!) 发生了什么。
- 异常和警告消息将存储在日志文件中。如果文件达到一定大小,将创建一个新的日志文件。如果日志文件过多,最早的将被删除。
- 异常可能会通过电子邮件发送给开发人员,包括跟踪信息。
跟踪消息
跟踪的目的是将(调试)消息写入跟踪。然后可以使用跟踪来调查为什么会发生某个问题,或者更准确地说,应用程序在问题发生之前做了什么。Tracer
根据调用哪种跟踪方法,区分四种类型的跟踪消息
Tracer.Trace()
Tracer.TraceWarning()
Tracer.TraceError()
Tracer.TraceException()
应用程序可以决定普通跟踪、警告或错误之间的区别。Tracer
以相同的方式将它们全部写入跟踪,并相应地标记它们。此外,应用程序可以配置,例如将所有消息存储在 RAM 中,仅将警告和错误写入跟踪文件,并将错误通过电子邮件发送到开发帐户。
Tracer.TraceWithFilter()
除了跟踪消息之外,还接受一个文本,该文本以后可用于过滤。
Tracer.TraceException()
应该在捕获到异常时使用。它们被 Tracer
视为其他消息,尽管所有异常详细信息都会在跟踪中显示,并且如果附加了调试器,它可能会执行 Break()
。
跟踪应用程序启动
为了从执行的第一行代码开始使用完整的跟踪功能,Tracer
被设计为一个 static
类。
像这样跟踪应用程序的启动可能很有用
Tracer.Trace("Application started");
Configuration configuration;
Tracer.Trace("Read Configuration started");
try {
configuration = ReadConfiguration();
} catch (Exception ex) {
Tracer.TraceException(ex, "Read Configuration failed, used default configuration");
configuration = Configuration .Default;
}
Tracer.Trace(configuration.ToString());
Tracer.Trace("Read Configuration completed");
Tracer.Trace("Start Connect DB");
try {
ConnectDB();
Tracer.Trace("DB connected");
} catch (Exception ex) {
Tracer.TraceException(ex, "Connect DB failed");
}
注释
- 第一行写入跟踪。除其他外,这对于在跟踪中显示跟踪何时确切启动非常有用。
- 对于设置应用程序所需的每一步,请使用自己的
_try_
语句。如果发生异常,请执行其他操作,以便应用程序仍能启动并显示一些(错误)信息。
设置跟踪
应用程序可以配置一些跟踪功能
Tracer.IsTracing = true;
Tracer.IsWarningTracing = true;
Tracer.IsErrorTracing = true;
Tracer.IsExceptionTracing = true;
Tracer.IsBreakOnWarning = false;
Tracer.IsBreakOnError = true;
Tracer.IsBreakOnException = true;
//setup tracing file
TraceLogFileWriter =new TraceLogFileWriter(
directoryPath: Environment.CurrentDirectory + @"\LogFiles",
fileName: "FinanceLog",
fileExtension: "log"
maxFileByteCount: 10*1000*1000,
maxFileCount: 5,
logFileWriterTimerInitialDelay: 1000, //msec
logFileWriterTimerInterval: 10*1000 //msec
newFileCreated,
filter);
这段代码不必在应用程序的最开始,因为 Tracer
可以在没有丢失任何消息的情况下使用其默认设置。可以读取一些(跟踪)配置并在出现问题时进行跟踪,即使 Tracer
尚未设置,然后使用读取的配置数据设置 Tracer
。
IsXxxTracing
标志控制普通消息、Warning
、Error
或 Exception
是否被写入 Tracer
RAM 缓冲区或立即丢弃。
IsBreakOnXxx
控制在跟踪 Xxx 类型消息时调试器是否应中断。开发人员可以通过调试器更改这些标志的值,从而轻松控制调试过程中调试器中断的频率。
TraceLogFileWriter
的构造函数会自动注册到 Tracer.MessagesTraced
事件。即使是已经跟踪的消息也会被写入指定的日志文件。一旦日志文件大小超过 maxFileByteCount
,就会创建一个新文件。如果存在的文件超过 maxFileCount
,最早的文件将被删除,以防止跟踪耗尽所有磁盘空间。将其中任何一个参数设置为 0
将阻止检查相应的 max
值。newFileCreated
是在创建新日志文件时调用的方法。filter
是在写入日志文件时对消息进行调用的方法。如果返回 true
,则该特定消息不会写入日志文件。
访问跟踪消息
要获取所有存储的跟踪消息,只需调用 Tracer.GetTrace()
。它返回一个 TraceMessage
数组,按创建时间排序。在执行 GetTrace()
期间,不会添加新的跟踪消息以保证结果的一致性。但是,它们不会丢失,只是在 GetTrace()
完成后写入。
使用 TraceMessage.ToString()
可以简单地显示跟踪消息。
处理异常
抛出异常
通常,调试器会在抛出异常后中断,并在 catch
语句处停止。此时,抛出异常的局部数据已丢失。使用 Tracer.Exception()
来解决此问题
//an error was detected
throw Tracer.Exception(new MyException("Some info"));
Tracer.Exception()
会在调试器中中断,并且导致异常抛出的数据仍然可用。然后开发人员可以继续让调试器运行,然后抛出异常。开发人员可以通过 IsBreakOnException
在运行时控制是否发生中断。
捕获异常
某些异常可能偶尔会发生。如果程序应该通过执行其他操作来响应异常,则应该在 try catch
语句中捕获异常。其余的异常应该在中心位置捕获,这在不同的 .NET 技术中有所不同
技术栈 | |
控制台应用程序 | try catch 语句 |
Windows Forms 应用程序 | Application.ThreadException |
WPF 应用程序 | Application.DispatcherUnhandledException |
ASP.NET | Global.asax: Application_Error |
多线程 | 每个线程都需要自己的 try catch 语句 |
无论是本地还是全局捕获异常,在这两种情况下,最好使用 Tracer.TraceException(exception)
进行跟踪。这会将带有所有异常详细信息的异常写入跟踪。如果附加了调试器并且 IsBreakOnException
为 true
,则详细信息也会显示在调试器输出窗口中。
通常,了解用户在异常发生前做了什么会很有趣。存储用户与应用程序的所有主要交互将产生过多的信息。但是,在使用 Tracer
时,信息仅写入 RAM,并在一段时间后被覆盖。如果抛出异常,则可以获取异常之前的跟踪消息,并将它们添加到异常中,然后可能会将其写入日志文件。
扩展跟踪处理与 GUI 集成
通常,应用程序需要对跟踪消息进行进一步处理,例如向用户(GUI)显示警告、错误和异常,将所有错误和异常收集到中央数据库中,或将异常通过电子邮件发送给开发人员。通过注册 Tracer.MessagesTraced
事件可以轻松完成此操作。
具有用户界面的应用程序可以提供以下功能
- 显示和编辑
Tracer
设置 - 向用户显示错误和异常。应该可以轻松复制跟踪信息,以便将其发送给支持人员。
- 显示当前存储的跟踪
变更历史
版本 1.1:添加刷新功能,即按需将跟踪消息从临时缓冲区移动到最终缓冲区
每条跟踪消息都会暂时存储在一个环形缓冲区中,因此执行跟踪的线程几乎不会延迟(约 1 微秒)。另一个线程运行一个计时器,每 100 毫秒将跟踪消息复制到最终跟踪环形缓冲区,并调用 MessagesTraced
事件的任何侦听器进行进一步处理,这可能很慢,例如写入文件。
这导致了一些问题,例如,当应用程序进行跟踪后立即关闭时。100 毫秒的计时器未运行,也没有调用事件侦听器。看起来跟踪不起作用。当在异常后使用 GetTrace()
检查跟踪时,也发生了类似的问题,因为计时器又没有运行。对于这些情况,添加了一个 Flush()
方法,该方法强制计时器立即运行,将所有消息从临时缓冲区移动到永久缓冲区,调用事件侦听器,然后才返回。
LogFileWriter
获得了 Dispose()
方法和析构函数,当应用程序关闭时,它们保证在调用这些方法之前,所有跟踪消息都会被写入跟踪文件。在此之前,丢失了 0 到 100 毫秒之间的跟踪消息。
DotNet 框架版本已降至 4.0。
重大更改
TraceMessage[] GetTrace(Action<TraceMessage[]> MessagesTracedHandler)
已重命名为 TraceMessage[] AddMessagesTracedListener(Action<TraceMessage[]> MessagesTracedHandler)
,以更好地反映该方法的实际用途。
将 ACoreLib 从 CodePlex 迁移到 Github 上的 BaseLib
原始文章是为我在 CodePlex.com 上共享的 ACoreLib
编写的。不幸的是,微软关闭了这个网站,我不再拥有 ACoreLib
的源代码,因为我不断添加新代码并将库重命名为 BaseLib
,我在 GitHub.com 上共享它。功能仍然相同,但有些名称已更改。我已使用新名称更新了文章。
下载代码
本文的代码位于 BaseLib/Tracer.cs。
如果您对更快、非阻塞的跟踪感兴趣,这有助于调试竞态条件,请阅读本文