.NET 应用程序中的日志记录和跟踪
在继续开发应用程序且过程变得更复杂时,你需要对应用程序应用其他调试诊断。
跟踪是在应用程序运行时监视其执行情况的一种方式。 开发.NET 应用程序时,可以向其添加跟踪和调试检测。 可在开发应用程序期间以及部署应用程序后使用该检测。
这一简单技术功能非常强大。 可以在需要多个调试器的情况下使用:
- 传统的调试器可能难以调试长期存在的问题。 通过日志,可以对较长的时间跨度进行详细的事后剖析。 与此相反,调试器限制为只能进行实时分析。
- 多线程应用程序和分布式应用程序通常难以调试。 附加调试器往往会修改行为。 可以根据需要分析详细日志,以了解复杂的系统。
- 分布式应用程序中的问题可能是由许多组件之间的复杂交互导致的。 将调试器连接到系统的每个部分可能并不合理。
- 许多服务不应停止。 附加调试器往往会导致超时失败。
- 问题并非总是可预见的。 日志记录和跟踪旨在降低开销,以便在出现问题的情况下可以始终记录程序。
将信息写入输出窗口
到目前为止,我们一直在使用控制台向应用程序用户显示信息。 其他类型的应用程序是使用 .NET 生成的,其中包含移动应用、Web 应用和桌面应用等用户界面,并且没有可见的控制台。 在这些应用程序中,System.Console
可在“幕后”记录消息。这些消息可能会显示在 Visual Studio 或 Visual Studio Code 的输出窗口中。 它们还可能会输出到系统日志,如 Android 的 logcat
。 因此,当在非控制台应用程序中使用 System.Console.WriteLine
时,应慎重考虑。
在这里,除了 System.Console
,还可以使用 System.Diagnostics.Debug
和 System.Diagnostics.Trace
。 Debug
和 Trace
都是 System.Diagnostics
的一部分,并且仅在附加了相应的侦听器时写入日志。
选择使用哪种打印样式 API 由用户自己决定。 主要区别包括:
- System.Console
- 始终启用,并始终写入控制台。
- 适用于客户可能需要在发行版中看到的信息。
- 由于这是最简单的方法,所以常常用于临时调试。 此调试代码通常不会签入到源代码管理中。
- System.Diagnostics.Trace
- 仅在定义
TRACE
时启用。 - 写入附加侦听器,默认情况下为 DefaultTraceListener。
- 创建将在大多数生成中启用的日志时,请使用此 API。
- 仅在定义
- System.Diagnostics.Debug
- 仅在定义
DEBUG
时才启用(处于调试模式时)。 - 写入附加调试器。
- 创建仅在调试生成中启用的日志时,请使用此 API。
- 仅在定义
Console.WriteLine("This message is readable by the end user.");
Trace.WriteLine("This is a trace message when tracing the app.");
Debug.WriteLine("This is a debug message just for developers.");
设计跟踪和调试策略时,请考虑自己所需的输出形式。 使用不相关信息填充的多个 Write 语句会创建难以阅读的日志。 另一方面,如果使用 WriteLine 将相关语句放置在单独的行上,可能会难以区分哪些信息应该在一起。 通常,当需要将来自多个源的信息组合起来创建单个信息性消息时,使用多个 Write 语句。 当需要创建单个完整消息时,使用 WriteLine 语句。
Debug.Write("Debug - ");
Debug.WriteLine("This is a full line.");
Debug.WriteLine("This is another full line.");
此输出来自前面使用 Debug
生成的日志记录:
Debug - This is a full line.
This is another full line.
定义 TRACE 和 DEBUG 常数
默认情况下,当应用程序在调试模式下运行时,将定义 DEBUG
常数。 可以通过在属性组的项目文件中添加 DefineConstants
条目进行控制。 除了对 Debug
配置启用 DEBUG
之外,下面的示例还演示了对 Debug
和 Release
配置启用 TRACE
。
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DefineConstants>DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DefineConstants>TRACE</DefineConstants>
</PropertyGroup>
如果在未附加到调试器时使用 Trace
,则需要配置跟踪侦听器,如 dotnet-trace。
条件跟踪
除了简单的 Write
和 WriteLine
方法之外,还可以使用 WriteIf
和 WriteLineIf
添加条件。 例如,以下逻辑将检查计数是否为零,然后写入调试消息:
if(count == 0)
{
Debug.WriteLine("The count is 0 and this may cause an exception.");
}
可以在单个代码行中重写它:
Debug.WriteLineIf(count == 0, "The count is 0 and this may cause an exception.");
还可以将这些条件用于 Trace
以及在应用程序中定义的标志:
bool errorFlag = false;
System.Diagnostics.Trace.WriteIf(errorFlag, "Error in AppendData procedure.");
System.Diagnostics.Debug.WriteIf(errorFlag, "Transaction abandoned.");
System.Diagnostics.Trace.Write("Invalid value for data request");
验证是否存在特定条件
断言或 Assert
语句会测试指定为 Assert
语句参数的条件。 如果条件的计算结果为 true
,则不会发生任何操作。 如果条件的计算结果为 false
,则断言将失败。 如果运行的是调试生成,则程序会进入中断模式。
可以使用位于 System.Diagnostics
命名空间中的 Debug
或 Trace
的 Assert
方法。 程序的发行版中不包含 Debug
类方法,因此它们不增大发行代码的大小,也不会减慢发行代码的速度。
自由使用 System.Diagnostics.Debug.Assert
方法来测试代码正确时应为 true 的条件。 例如,假设你编写了一个整数除法函数。 根据数学规则,除数绝不能为零。 你可以使用断言测试此条件:
int IntegerDivide(int dividend, int divisor)
{
Debug.Assert(divisor != 0, $"{nameof(divisor)} is 0 and will cause an exception.");
return dividend / divisor;
}
当在调试器中运行此代码时,将对断言语句进行评估。 但在发行版中不会进行此比较,因此不会产生额外的开销。
注意
使用 System.Diagnostics.Debug.Assert
时,请确保在删除 Assert 后,Assert
内的任何代码都不会更改程序的结果。 否则,可能会意外引入仅出现在程序的发行版中的 bug。 请特别注意包含函数或过程调用的断言。
利用 System.Diagnostics
命名空间中的 Debug
和 Trace
是在运行和调试应用程序时提供附加上下文的好方法。