冠军

导航

调试,测试与日志

 问题

“程序的显示看起来有点问题,你能不能修改一下?”
“没问题,改完我就提交给你。”
“不会太久吧?”
“5分钟就够了!”

在软件开发过程中,这种场景屡见不鲜,但是,看起来很见的任务,其实往往不是那么简单。
当修改了部分代码之后,程序不是像我们想象的那样工作,奇奇怪怪地问题开始出现,我们开始设置一个个的断点,一遍一遍地重复执行,眼睛紧紧盯着显示器上的监视窗口,时间在飞快地流逝,然而,我们的思路却渐渐变得模糊......有的时候,甚至没有监视到希望的步骤,不得不再一次重新开始。

调试

调试,又称 Debug,是开发工具赋予我们的一大利器,允许我们将程序的执行过程暂停在某一个点上,然后仔细观察当前程序的执行状态,以便于发现错误的蛛丝马迹。因此,成为程序员解决开发中问题的终极武器,甚至有的程序员在没有调试器的情况下,难以解决问题。

但是,有的程序确实是难以调试的,例如,已经部署到客户环境中的程序,或者是 Windows 的服务程序,这个时候,我们是没有调试器可用的,那么,我们怎么办呢?

于是,问题来了,我们为什么一定要调试呢?
通过调试器,我们可以知道程序的运行状态。

那么,我们为什么要知道程序的运行状态呢?
因为程序太复杂了,我们不能一目了然地知道程序的运行状态。

测试

还有一种解决开发中问题的思路称为测试,又称为 Test,通过为程序编写一个个的测试用例,来保证程序在我们的掌控之中。由于从程序的第一步开始,我们就会通过测试来保证程序的正确,那么,在每一次的修改之后,我们就可以迅速地发现修改造成的影响。

为了保证能够迅速写出测试,测试驱动的开发会强制你写出短小易懂、功能内聚、耦合松散的代码,把你从调试中解放出来。

但是,对于复杂的操作,我们就不需要知道程序的状态了吗?完全通过测试就可以解决所有的问题吗?

当然不是,不过,可以借助于日志来帮助我们记录程序的状态。而不再需要我们紧盯着监视窗口。

日志

日志,又称为 Log,是我们开发人员的又一利器,其实,不管是在调试还是测试的时候,日志都可以帮助我们解决问题,不过,很多的程序员迷恋于调试器,而忽视了日志。尤其是在测试驱动的开发中,日志更是我们的得力助手。
所谓的日志,其实是一种记录机制,允许我们在程序代码中插入一些特殊的输出代码,将程序当前的运行状态随时输出,以便于在无人值守的情况下记录信息,在事后对程序的处理过程进行分析。

最简单的日志就是直接通过 Console 来输出,或者使用 alert, 或者 MessageBox 来输出,没准你就使用过这些手法。这些方法会给程序带来副作用,在开发完成之后,往往需要你手工删除。不删除的话会造成程序的效率问题。要是又发现有新的问题存在呢?是不是又要再来一遍?这可是开发人员的噩梦呀!

一个完善的日志系统绝不是简单地在控制台输出,它至少需要支持下面的几个特征:

  • 使用一种方式就支持输出到多个目的地,例如:控制台,文本文件,数据库,甚至电子邮件等等。
  • 允许控制输出的级别,过滤输出的内容,而不需要大幅度修改日志程序
  • 使用简单,可以使用简单的语法来记录日志

目前,存在着多个成熟的日志系统供我们选择,在 .NET 开发平台上,主要有两个日志系统:.NET 平台直接支持的日志系统和开源的 Log4Net。

今天,我们先看看 .NET 平台内置的日志系统。

这里我们首先了解三个概念:

  • 日志器:用来发送日志,在开发中,主要是用日志器来输出日志信息。
  • 监听器:日志器用来输出日志信息,日志信息输出到哪里呢? 监听器用来完成实际的日志记录功能,在日志系统中,存在多种监听器,用来将日志信息记录到不同的目的地。
  • 日志控制开关:在软件生命周期的不同阶段,我们需要不同的日志信息,在开发阶段,可能需要比较详细的日志来检查错误,在运行阶段,大部分的问题已经被处理,我们可能仅仅需要记录一些关键信息,通过控制开关,可以在不需要修改代码的情况下,调整日志输出的内容。

在 .NET 中,关于日志处理的相关类型,定义在命名空间 System.Diagnostics 中,有两个预定义的日志器类型,Debug 和 Trace。
这两个日志记录器的工作机制是相同的,区别仅仅在于 Debug 仅仅在编译器定义了 DEBUG 常量的情况下工作,而 Trace 仅仅在定义了 TRACE 常量的情况下工作,默认情况下,当 我们使用 Debug 模式编译的时候,默认已经定义了这两个常量。所以,不管使用 Debug 还是 Trace 都可以输出日志信息。如果我们将程序编译为发布模式,那么,将仅仅定义 TRACE 常量,导致忽略 Debug 的存在,只记录 Trace 的日志信息。

我们先看一看监听器,以便能够看到输出的日志信息。
所有的监听器都要从 TraceListener  派生,这是定义在命名空间 System.Diagnostics 中的一个抽象基类。通常我们直接使用系统的一些派生类。

System.Diagnostics.DefaultTraceListener
System.Diagnostics.Eventing.EventProviderTraceListener
System.Diagnostics.EventLogTraceListener
System.Diagnostics.TextWriterTraceListener
System.Web.WebPageTraceListener

作为文本格式的日志, System.Diagnostics.TextWriterTraceListener 又有几个常用的派生类

System.Diagnostics.ConsoleTraceListener
System.Diagnostics.DelimitedListTraceListener
System.Diagnostics.EventSchemaTraceListener
System.Diagnostics.XmlWriterTraceListener


Debug 和 Trace 使用相同的监听器,默认情况下,在它们的监听器集合属性 Listeners 中,已经添加了一个 System.Diagnostics.DefaultTraceListener 的实例,以便输出到 Visual Studio 的 Output 窗口中,所以,在使用了日志之后,我们可以打开 Output 窗口来看看实际的输出。如果我们希望能够在控制台窗口中看到输出,那么只需要增加一个 ConsoleTraceListener 就可以了。

// 增加一个可以输出到控制台的 Listener
System.Diagnostics.Trace.Listeners.Add(
    new System.Diagnostics.ConsoleTraceListener()
    );

当然,在实际的开发中,我们可能需要的是一个文本文件,现在,你还需要提供一个文件名了。

System.Diagnostics.Trace.Listeners.Add(
    new System.Diagnostics.TextWriterTraceListener("log.txt")
    );

这些监听器也可以不在程序中固定声明,而是通过配置文件来更加方便地定义。这样的话,我们就可以动态地修改监听器,而不需要修改我们的代码了。
在程序的配置文件中,配置节 system.diagnostics 用来定义日志的配置参数,其子元素 trace 定义日志的参数,trace 的子元素 listeners 定义日志的监听器。我们可以通过下面的配置参数来增加一个文件的监听器。其中的 initializeData 用来配置文件名。

<add name="fileListener"
        type="System.Diagnostics.TextWriterTraceListener"
        initializeData="log.txt" />

当监听器配置好之后,就可以使用日志器输出日志了。不管是 Debug 还是 Trace 都提供了多个方法来输出日志。
最简单的方式就是使用 WriteLine 来输出日志,就跟使用 Console 一样。
不过,对于日志来说,存在的一个问题就是,我们并不总想输出所有的日志,比如程序已经经过测试,我们可能并不需要大量的日志信息来干扰我们,怎么减少实际输出的日志呢?修改程序当然不是一个好主意,我们可以通过条件输出来限制输出的日志内容,仅仅在某种条件下,才输出日志,通过 WriteLineIf ,我们可以指定一个条件,仅仅当条件满足的时候,才会输出日志。

条件仅仅是一个条件,什么样的条件都可以,只要你需要。

为了方便使用,在 .NET 中又提供了一个日志的开关,来方便我们指定条件,所谓日志的开关其实就是一个从 0 到 4 的整数,通过一个枚举 TraceLevel 来方便使用这个整数

  • 0. 关闭,不希望输出日志
  • 1. 错误级别的日志
  • 2. 建议输出错误和警告级别的日志
  • 3. 一般信息的日志也输出
  • 4. 详细日志

不过,实际上输出什么日志还是看你的日志输出语句,你在日志输出语句中可以通过这个开关来判断该不该输出。

那么,这个开关从哪里取得呢?还是配置文件。在配置文件的 system.diagnostics 中,子元素 switches 用来配置一个日志级别,你需要为你的级别起一个名字,以便在程序中

取得这个设置。

<add name="traceSwitch" value="0"/>

在程序中,你可以这样取得配置文件中日志的开关

System.Diagnostics.TraceSwitch myTraceSwitch =
    new System.Diagnostics.TraceSwitch("traceSwitch", string.Empty);

在程序中,你可以通过这个开关的设置来决定输出什么,配合开关的级别,在 Trace 中又提供了几个匹配的方法

System.Diagnostics.Trace.TraceError("Error!!!");
System.Diagnostics.Trace.TraceWarning("Warning!!!");

当然,它们仅仅用来输出分类的日志,注意,判断是否输出是你的事情,所以,程序的代码往往如下:

if( myTraceSwitch.TraceError)
    System.Diagnostics.Trace.TraceError("Error!!!");

if( myTraceSwitch.TraceWarning)
    System.Diagnostics.Trace.TraceWarning("Warning!!!");

posted on 2011-08-07 18:34  冠军  阅读(6225)  评论(5编辑  收藏  举报