Effective C# 原則36:利用.Net運行時診斷(譯)

來源:互聯網
上載者:User

Effective C# 原則36:利用.Net運行時診斷
Item 36: Leverage .NET Runtime Diagnostics

當有問題發生時,它們往往並不是在實驗的時候發生的,機器有輕鬆調試的工具。在很多實際情況中,你不好修正的問題總是發生在使用者的機器上,那裡沒有調試環境,也沒有好的方法計算出問題的情況。在實際情況中,有經驗的開發人員會建立一個方法,讓系統在運行時捕獲儘可能多的資訊。.Net架構已經包含一些類集合,利用這些集合,你可以做一些通用的調試。而且這些類可以在運行時或者編譯時間進行配置。如果你利用它們,你就可以輕鬆的發現在實際運行時的問題。使用架構裡已經存在的代碼,你可以發送一條診斷資訊到一個檔案,或者到調試終端。另外,你還可以為你的產品指定特殊的調試輸出層級。你應該儘快的在你的開發環境中使用這些功能,以確保你可以利用這些輸出資訊來修正在實際運行中沒有預料到的一些問題。不要自己寫診斷庫除非你已經明白架構已經提供了哪些。

System.Diagnostics.Debug, System.Diagnostics.Trace和System.Diagnostics.EventLog類提供了你在運行程式時要建立診斷資訊的所有工具。前面兩個類功能是基本上是一樣的。不同之外是Trace類是由預先處理符TRACE控制的,而Debug類則是由DEBUG預先處理符控制的。當你用VS.net開發一個項目時,TRACE符號是同時在調試版和發布版中定義的。你可以為所有的發布版使用Trace類來建立診斷資訊。EventLog類提供了一個入口,通過這個入口,你的程式可以寫一些系統日誌。EventLog類不支援運行時配置,但你可以把它封裝到一個統一的簡單介面中。

你可以在運行時控制診斷輸出,.Net架構使用一個應用程式設定檔來控制變化多樣的運行時設定。這個是一個XML檔案,在主應用程式運行時的目錄中。這個檔案與應用程式同名,但添加了一個.config尾碼。務更制塊例如MyApplication.exe 可能會有一個MyApplication.exe.config的XML檔案來控制它。所所有的配置資訊包含在一個configuration節點中:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>

</configuration>

.Net架構使用預定義的關鍵字來控制架構中一些類的行為。另外,你可以定義你自己的配置關鍵字和值。

你可以組合輸出開關和Trace.WriteLineIf()方法來控制應用程式的輸出。你可以在應用程式外以預設的方式關閉這個輸出,以便應用程式得到最好的效能。當發現問題時,你可以開啟這個輸出用於診斷和修正在實際中遇到的問題。WriteLineIf()只有在運算式為真時才輸出:

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

你所建立的輸出開關用於控制輸出的層級,一個輸出開關可以是由應用程式設定檔定義的變數,可以是五種狀態之一:關閉(Off),錯誤(Error),警告(Warning),資訊(Info)和詳細(Verbose)。這些狀態是環境的一部份,而且它們的值可以是從0到4。這樣你就可能為所有的子系統資訊建立一個控制。 定義一個輸出開關類然後初始化它就可以建立一個開關了:

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

第一個參數是開關顯示的名字,第二個參數是描述。這樣,在運行時可以在應用程式設定檔中配置它們的值。下面就把librarySwitch設定成Info:

<system.diagnostics>
  <switches>
    <add name="MyAssembly" value="3" />
  </switches>
</system.diagnostics>

如果你編輯了這個設定檔中開關的值,那麼就修改了所有由那個開關控制的輸出語句。

另一個任務:你須要配置你的輸出到什麼地方去。 預設是一個連結到Trace類上的監聽者:一個DefaultTraceListener對象。DefaultTraceListener發送資訊到調試器,而且在它的失敗方法(宣告失敗時調用)會列印一些診斷資訊然後終止程式。在產品發布環境中,你不可能看到這樣的資訊。但你可是以配置不同的監聽對象到產品發布環境中:那就是在應用程式的設定檔中添加監聽者。下面就添加了一個TextWriterTraceListener 到應用程式中:

<system.diagnostics>
  <trace autoflush="true" indentsize="0">
    <listeners>
      <add name="MyListener"
        type="System.Diagnostics.TextWriterTraceListener"
        initializeData="MyListener.log"/>
    </listeners>
  </trace>
</system.diagnostics>

TextWriterTraceListener把所有的診斷資訊到列印到一個MyListener.log檔案中。名字屬性指定了監聽者的名字,類型指定了作者監聽對象的類型,它必須是從System.Diagnostics.TraceListener派生下來的。只有在極少數情況下你才建立自己的監聽類,那就是你覺得.Net架構的監聽類不夠用。initializeData的值是一個字串,用於傳給對象的建構函式。而TextWriterTraceListeners把它用於檔案名稱。

你可以小做一個擴充,讓它可以在應用中每個部署的程式集上都可以簡單的使用。對於每個程式集,添加一個類來跟蹤程式集建立的診斷:

internal class MyAssemblyDiagnostics
{
  static private TraceSwitch myAssemblySwitch =
    new TraceSwitch( "MyAssembly",
    "The switch for this assembly" );

  internal static void Msg( TraceLevel l, object o )
  {
    Trace.WriteLineIf( myAssemblySwitch.Level >= l,
      o, "MyAssembly" );
  }

  internal static void Msg( TraceLevel l, string s )
  {
    Trace.WriteLineIf( myAssemblySwitch.Level >= l,
      s, "MyAssembly" );
  }

  // Add additional output methods to suit.
}

MyAssemblyDiagnostices類根據一個開關來為這個程式集建立診斷資訊。為了建立資訊,調用按常規調用重載的Msg的任何一個就行了:

public void Method1( )
{
  MyAssemblyDiagnostics.Msg( TraceLevel.Info,
    "Entering Method1." );

  bool rVal = DoMoreWork( );

  if( rVal == false )
  {
    MyAssemblyDiagnostics.Msg( TraceLevel.Warning,
      "DoMoreWork Failed in Method1" );
  }

  MyAssemblyDiagnostics.Msg( TraceLevel.Info,
    "Exiting Method1." );
}

利用一個全域的開關,你還可以組件特殊的程式集開關,來控制整個應用程式的輸出:

internal static void Msg( TraceLevel l, object o )
{
  Trace.WriteLineIf ( librarySwitch.Level >= l ||
    globalSwitch.Level >= l,
    o, "MyLibrary" );
}

internal static void Msg( TraceLevel l, string s )
{
  Trace.WriteLineIf( librarySwitch.Level >= l ||
    globalSwitch.Level >= l,
    s, "MyLibrary" );
}

這樣,你就可以在應用程式上診斷資訊,而且更友好的控制個別庫檔案的輸出。在應用程式的任何地方,你都可以設定應用程式級的診斷到錯誤級,從而發現錯誤。當你有一個獨立的問題時,你可以通過提高這個庫的輸出層級,從而精確的發現問題的源頭。

在實際環境中,對於已經布署的應用程式,診斷庫對於程式診斷和維護是必須的。但你自己不必寫這些診斷庫:.Net FCL已經完成了核心的功能。儘可能完全的使用它們,然後在滿足特殊要求時擴充它們。這樣,即使是在產品發布的環境中也可以捕獲所有的問題。
==================
   

Item 36: Leverage .NET Runtime Diagnostics
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.

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.

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:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>

</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.

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:

bool _printDiagnostics = true;
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:

static private TraceSwitch librarySwitch = 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:

<system.diagnostics>
  <switches>
    <add name="MyAssembly" value="3" />
  </switches>
</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.

One more task: You need to configure where your trace output goes. By default, one listener is connected to the TRace class: a DefaultTraceListener 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:

<system.diagnostics>
  <trace autoflush="true" indentsize="0">
    <listeners>
      <add name="MyListener"
        type="System.Diagnostics.TextWriterTraceListener"
        initializeData="MyListener.log"/>
    </listeners>
  </trace>
</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.

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:

internal class MyAssemblyDiagnostics
{
  static private TraceSwitch myAssemblySwitch =
    new TraceSwitch( "MyAssembly",
    "The switch for this assembly" );

  internal static void Msg( TraceLevel l, object o )
  {
    Trace.WriteLineIf( myAssemblySwitch.Level >= l,
      o, "MyAssembly" );
  }

  internal static void Msg( TraceLevel l, string s )
  {
    Trace.WriteLineIf( myAssemblySwitch.Level >= l,
      s, "MyAssembly" );
  }

  // Add additional output methods to suit.
}

 

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:

public void Method1( )
{
  MyAssemblyDiagnostics.Msg( TraceLevel.Info,
    "Entering Method1." );

  bool rVal = DoMoreWork( );

  if( rVal == false )
  {
    MyAssemblyDiagnostics.Msg( TraceLevel.Warning,
      "DoMoreWork Failed in Method1" );
  }

  MyAssemblyDiagnostics.Msg( TraceLevel.Info,
    "Exiting Method1." );
}

 

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

internal static void Msg( TraceLevel l, object o )
{
  Trace.WriteLineIf ( librarySwitch.Level >= l ||
    globalSwitch.Level >= l,
    o, "MyLibrary" );
}

internal static void Msg( TraceLevel l, string s )
{
  Trace.WriteLineIf( librarySwitch.Level >= l ||
    globalSwitch.Level >= l,
    s, "MyLibrary" );
}

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.

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

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.