现在的位置: 首页 > 综合 > 正文

Effective C#之Item 36:Leverage .NET Runtime Diagnostics

2014年01月05日 ⁄ 综合 ⁄ 共 9256字 ⁄ 字号 评论关闭
 

Item 36: Leverage .NET Runtime Diagnostics

利用.Net运行时诊断

Problems happen. They don't always happen in the lab, on machines
you can easily debug. The problems you can't fix always seem to occur on one
user's machine in the field, with no debugging environment and no way to figure
out the cause. Experienced developers have learned to build in the capability
to capture as much information as possible from systems running in the field.
The .NET Framework includes a set of classes that you can use to generate diagnostics.
These are configurable at runtime or compile time. If you leverage them, you
can more quickly find problems that occur only in the field. Using code already
in the framework, you can send diagnostic messages to a file, to the system
logger, or to a debugging terminal. In addition, you can specify the level of
debugging output that your program produces. You should use these features
early in your development and make sure that you can produce the output you
need to fix unanticipated problems in the field. Don't write your own
diagnostic library until you understand what's already provided.

总会出问题的。它们不总是发生在实验室,发生在你容易调试的机器上。你不能修复的问题看起来会发生在某个用户的机器上,没有调试环境,也没有方法来查找原因。有经验的开发者已经学会了构建从系统运行中捕捉尽可能多的信息的能力。.Net框架包含一系列类,你可以用来生成诊断信息。它们在运行时或者编译时是可配置的。如果利用好它们,可以更迅速的发现实际运行时的问题。使用在框架中已经存在的代码,可以将诊断信息存储在文件、系统日志或者调试终端。另外,你可以指定程序调试输出的级别。你应该在开发中尽早使用这些特性,确保生成需要的输出来修复不可预测的问题。不要编写自己的诊断库,除非你理解了框架已经提供了哪些。

The System.Diagnostics.Debug,
System.Diagnostics.Trace, and System.Diagnostics.EventLog classes provide all
the tools you need to create diagnostic information from a running program. The
first two classes have almost identical capabilities. The difference is that
the trace class methods are controlled by the TRACE preprocessor symbol, and
the Debug class methods are controlled by the DEBUG preprocessor symbol. When
you create a project with VS .NET, the TRACE symbol is defined for both release
and debug builds, while the DEBUG symbol is defined only for debug builds. You
create all your release build diagnostics using the TRACE class. The EventLog
class provides entry points so that your application can write to the system
event log. The EventLog class does not support runtime configuration, but you
can wrap it to conform to the same interface illustrated shortly.

System.Diagnostics.DebugSystem.Diagnostics.Trace System.Diagnostics.EventLog类提供了所有需要的工具,可以从运行中的程序生成诊断信息。前两个类几乎有同样的能力。它们的不同之处在于,trace类的方法是由TRACE预处理符号控制的,Debug类的方法是由DEBUG预处理符号控制的。当你使用VS.NET创建项目的时候,TRACE符号定义的对releasedebug版本都起作用,然而DEBUG符号仅仅对debug版本起作用。使用TRACE类创建所有的release版本的诊断机制。EventLog类提供了访问点,那样,你的应用程序可以写入系统事件日志。EventLog类不支持运行时配置,但是你可以对其进行包装来统一接口。

You can also control the diagnostic
output at runtime. The .NET Framework uses an application-configuration file to
control a variety of runtime settings. This file is an XML document, located in
the same directory as the main executable. The file shares the same name as the
executable, with .config appended. For example, MyApplication.exe would be
controlled by the MyApplication.exe.config XML document. All the configuration
information is contained in a configuration node:

你也可以在运行时控制诊断输出。.Net框架使用应用程序配置文件来控制一系列运行时设置。该文件是XML文档,和主执行程序位于同样的目录下。该文件和执行程序共享同样的名字,用.config做扩展名。例如,MyApplication.exe可能由MyApplication.exe.config文件控制。所有的配置信息都包含在一个configuration节点下面。

  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <configuration>
  3. </configuration>

The .NET Framework uses predefined keys to control the behavior of
framework classes. In addition, you can define your own configuration keys and
values.

.Net框架使用预定义关键字来控制框架类的行为。另外,你可以定义自己的配置关键字和值。

You combine the Trace.WriteLineIf() method and TraceSwitches to
control the granularity of the output that your application generates. You turn
off output by default so that you get the most performance possible out of your
application. When you find problems, you can ratchet up the output to diagnose
and correct any problems you find in the field. WriteLineIf() generates output
only when an expression evaluates to true:

你可以将Trace.WriteLineIf()方法和TraceSwitch进行合并,来控制应用程序输出的粒度。应用程序在默认情况下关闭输出,那样就可以获得最大的性能。当你发现问题时,可以逐渐输出诊断信息,改正任何发现的问题。WriteLineIf(),只有当表达式为true时才生成输出。

  1. bool _printDiagnostics = true;
  2. Trace.WriteLineIf(_printDiagnostics, "Printing Diagnostics Today""MySubSystem");

You create traceSwitches to control the level of output. A
traceSwitch is a variable set using the application-configuration file to one
of five states: Off, Error, Warning, Info, and Verbose. These states are part
of an enumeration and have values from 0 to 4. You can create a switch for each
subsystem to control its messages. To create the switch, declare a variable of
the traceSwitch class and construct it:

你创建TraceSwitch对象来控制输出的等级。一个TraceSwitch对象就是一个变量设置,使用了应用程序配置文件,设置为五个状态之一:Off,
Error, Warning, Info,
Verbose。这些状态是一个枚举的一部分,它们的值从04。你可以为每个子系统创建一个switch来控制它的消息。为了创建这个switch,需要声明一个TraceSwitch的变量,进行构造。

  1.     static private TraceSwitch librarySwitch =
  2.         new TraceSwitch( "MyAssembly","The switch for this assembly" );

The first parameter is the display name for the switch; the second
parameter is the description. You set the value of the switch at runtime in the
application configuration file. The following snippet sets the librarySwitch to
Info:

第一个参数是该switch显示的名字,第二个参数是对它的描述。在应用程序的配置文件里面,对switch的运行时的值进行设置。下面的代码段将librarySwitch设置为Info

  1. <system.diagnostics>
  2.   <switches>
  3.     <add name="MyAssembly" value="3" />
  4.   </switches>
  5. </system.diagnostics>

If you edit the config file's value of the switch, you modify the
output generated by all statements controlled by that switch.

如果你对配置文件的switch的值进行编辑,就修改了由该switch控制的所有语句的输出。

One more task: You need to configure where your trace output goes.
By default, one listener is connected to the Trace class: a Default TraceListener
object. The DefaultTraceListener sends messages to the debugger, and its Fail
method (called when asserts fail) prints a diagnostic messages and terminates
the program. In a production environment, you won't see any of the messages.
You can configure a different listener in a production environment; you add
listeners in the application configuration file. The following snippet adds a
TextWriterTraceListener to your application:

还有个任务:你需要对你race输出到哪里进行配置。默认情况下,是有一个监听者和Trace类相联系的:一个DefaultTraceListener类对象。该DefaultTraceListener对象向调试器输出消息,同时,它的Fail方法(assert失败时被调用)打印出一个诊断信息,同时终结该程序。在一个生产环境下,你将看不到任何信息。在那样的环境下,你可以配置另外一个不同的监听者,向应用程序配置文件添加一个监听者。下面的代码段向你的应用程序添加了一个TextWriterTraceListener类:

  1. <system.diagnostics>
  2.   <trace autoflush="true" indentsize="0">
  3.     <listeners>
  4.       <add name="MyListener"
  5.         type="System.Diagnostics.TextWriterTraceListener"
  6.         initializeData="MyListener.log"/>
  7.     </listeners>
  8.   </trace>
  9. </system.diagnostics>

This TextWriterTraceListener prints all diagnostic information to
the MyListener.log file. The name attribute specifies the name for the
listener. The type specifies the type of object to create as a listener; it
must be derived from System.Diagnostics.TraceListener. On those rare occasions
when the standard listener classes in the .NET Framework are not enough for
you, create your own listener class. The initializeData value is a string that
gets passed to the object's constructor. TextWriterTraceListeners use this
value for the filename.

TextWriterTraceListener将所有的诊断信息打印到MyListener.log文件中。name特性指定了监听者的名字。type指定了作为监听者而被创建的对象的类型,它必须是由System.Diagnostics.TraceListener派生来的。在那些极少的场合,即.Net框架里的标准监听者类不能满足你的时候,才创建自己的监听者类。initializeData的值是一个字符串,传递给对象的构造函数。TextWriterTraceListeners使用该值作为文件名。

You can extend these basics a bit to make it easier to create
diagnostics for each assembly you distribute in your application. For each
assembly you create, add a class to track the diagnostics generated by that
assembly:

你可以在这些基础上进行小小的扩展,使得为你的应用程序的每个已部署的程序集创建诊断更加容易。对每个你创建的程序集,添加一个类来跟踪该程序集生成的诊断信息。

 

  1.   internal class MyAssemblyDiagnostics
  2.     {
  3.         static private TraceSwitch myAssemblySwitch =
  4.           new TraceSwitch("MyAssembly","The switch for this assembly");
  5.  
  6.         internal static void Msg(TraceLevel l, object o)
  7.         {
  8.             Trace.WriteLineIf(myAssemblySwitch.Level >= l,o, "MyAssembly");
  9.         }
  10.  
  11.         internal static void Msg(TraceLevel l, string s)
  12.         {
  13.             Trace.WriteLineIf(myAssemblySwitch.Level >= l,s, "MyAssembly");
  14.         }
  15.         // Add additional output methods to suit.
  16.  }

The MyAssemblyDiagnostices class creates diagnostic messages for the
assembly, depending on a switch for that assembly. To generate a message, call
either of the overloaded Msg routines:

MyAssemblyDiagnostices类为程序集创建诊断信息,依赖于该程序集的switch。为了生成信息,需要调用任何一个重载的Msg子程序。

 

  1.   public void Method1()
  2.     {
  3.         MyAssemblyDiagnostics.Msg(TraceLevel.Info, "Entering Method1.");
  4.         bool rVal = DoMoreWork();
  5.         if (rVal == false)
  6.         {
  7.             MyAssemblyDiagnostics.Msg(TraceLevel.Warning,"DoMoreWork Failed in Method1");
  8.         }
  9.         MyAssemblyDiagnostics.Msg(TraceLevel.Info,"Exiting Method1.");
  10.   }

You can also combine the assembly-specific switch with a global switch
to control the entire application's output:

你也可以将程序集指定的switch和全局switch进行合并,来控制整个应用程序的输出:

  1. internal static void Msg(TraceLevel l, object o)
  2. {
  3.     Trace.WriteLineIf(librarySwitch.Level >= l ||globalSwitch.Level >= l,o, "MyLibrary");
  4. }
  5.  
  6. internal static void Msg(TraceLevel l, string s)
  7. {
  8.     Trace.WriteLineIf(librarySwitch.Level >= l || globalSwitch.Level >= l,s, "MyLibrary");
  9. }

This enables you to control application-wide diagnostics and more
finely control an individual library's output. You can set the application-level
diagnostics to the Error level to find errors anywhere in the application. When
you have isolated the problem, you can raise the level of that one library's
output to a higher level and find the exact source of the problem.

这使你能够控制应用程序范围的诊断信息,同时更好的控制独立的库的输出。你可以将应用程序的诊断级别设置为Error,在应用程序里面查找任何错误。当你已经隔离了问题后,就可以将那个库的输出级别进行提升来查找更精确的问题。

Diagnostic libraries are necessary to diagnose and maintain programs
that have been distributed to the field. Don't write your own diagnostic
library: The .NET FCL already has the core features you need. Use it to the
fullest and then extend it for your own purposes, and you will capture all
problems, even in production environments.

诊断库对于已经部署的程序的诊断与维护是很必要的。不要编写自己的诊断库。.Net
FCL
已经有了你需要的核心特性。充分进行利用,只有在为自己的特殊目的才对其进行扩展。即使是在(产品已经部署的)生产的环境中,你也将能捕获所有的错误。

抱歉!评论已关闭.