一個簡單的 C# 非同步日誌記錄器

2018-04-13    分類:.NET開發、程式設計開發、首頁精華0人評論發表於2018-04-13

本文由碼農網 – 小峰原創翻譯,轉載請看清文末的轉載要求,歡迎參與我們的付費投稿計劃

Clearcove.Logging是一個非常簡單的日誌庫,旨在通過直接許可條款滿足大多數日誌記錄需求。

介紹

我知道你在想什麼——程式碼世界真的需要另一個日誌庫嗎?

如果你在.NET中尋找一個日誌庫,那麼你有很多選擇。有NLog,Log4Net,Enterprise Logging,erilog and Common.Logging,這些只是我現在暫時能想到的。我們不難找到一些由才華橫溢的開發人員編寫的日誌庫,他們花費了大量時間和精力建立了一些功能強大且功能豐富的軟體。

那麼,這個問題是否還需要解決呢?

背景

幾個月前,我進入了一個日誌庫市場。我是一個商業桌面應用程式的建立者,該商業桌面應用程式通過網際網路分發。因此,我有三個硬性需求:

  1. 非同步寫入日誌條目。我看到過有太多的應用程式因為是同步日誌記錄,因而出現了嚴峻的效能問題。
  2. 庫應該儘可能小。我不希望我的使用者就為了一個簡單的日誌功能就得下載和載入1 MB的DLL。越小越好。
  3. 我不想增加應用程式許可的複雜性。目前,我的客戶必須同意我的許可條款。新增具有單獨許可條款的第三方元件可能意味著需要額外的工作來評估我的產品。也許這有點偏執,但我只是想保持簡單。

我認為這些都是非常簡單的要求,但事實證明,我找不到任何符合我需求的產品。特別是,我發現許多日誌庫的許可條款是我無法接受的,因為我不想被迫分發“另一個”許可證。

所以我決定自己來寫日誌庫——Clearcove.Logging。它只有83行程式碼,非常輕巧。完整的實現放在一個單獨的.cs檔案中,以便在不必匯入庫的情況下重用。程式碼是用VS 2017編寫的,但我曾試圖編寫可與早期版本相容的程式碼。日誌庫以.NET 2.0為目標,為了吸引更多的使用者。

我認為這種日誌記錄方法是一個很好的選擇,是因為:

  1. 應用程式沒有複雜的日誌記錄需求
  2. 這是一個小程式,讓你從簡化的部署中受益
  3. 許可複雜性必須保持在最低限度

那麼它是怎樣工作的?

使用程式碼

首先,我要知道我想記錄什麼資訊。我想要一個簡單的API,可以用來記錄時間戳、記錄器名稱、執行緒ID和訊息等資訊。我對Log4Net API非常熟悉,並且從中借鑑了很多。

要宣告和使用記錄器,可以使用如下語法:

var log = new Logger(typeof(Program));    // Class level declaration.
log.Error("My error message", exception); // Logging from within a method.
log.Info("My info message");

如果你以前使用過其他日誌記錄庫,那麼你可能熟悉這種語法。

資料封裝

接下來,我要將我的日誌條目表示為一個簡單的物件。這樣做的主要原因是我希望我的記錄器能夠提升日誌記錄事件。有時我會在建立單元和整合測試時使用這些事件,因為我發現它們可以提供幫助。這只是個人喜好。如果你對提升日誌記錄事件不感興趣,則可以簡化此程式碼。

日誌記錄事件封裝在LogMessageInfo 物件中,該物件的實現方式如下:

public sealed class LogMessageInfo
{
    public readonly DateTime Timestamp;
    public readonly string ThreadId;
    public readonly string Level;
    public readonly string Logger;
    public readonly string Message;
}

編寫日誌條目

上面用於API實現和資料封裝部分的程式碼雖然冗長但非常簡單。但是,非同步日誌記錄略有差別。例如,如果丟擲導致應用程式關閉的異常,那麼會發生什麼情況?我們如何知道所有日誌條目將按照接收到的順序編寫?有若干方法可以解決這個問題。 Clearcove.Logger 就是其中一種簡單但不優雅的方式:

static void Main(string[] args)
{
      var targetLogFile = new FileInfo("./MyApp.log");
      Logger.Start(targetLogFile); // Loggers will complains if you skip initialization
      try
      {
          Run(args);
      }
      finally
      {
          Logger.ShutDown(); // Removing this line may result in lost log entries.
      }
}

這是一個Clearcove.Logging 背離其他實現,例如Log4Net的示例。我們必須告訴我們的記錄器何時開始記錄以及何時停止記錄。在嘗試將任何日誌條目寫入日誌檔案之前,我們必須執行這個操作。將Logger.ShutDown() 呼叫放在finally語句中應該使我們的記錄器有機會在應用程式關閉之前將所有待處理的日誌條目寫入日誌檔案。當然,也有日誌條目不寫入的情況。例如,如果機器沒電了。如果其中有些邊緣情況是你所關心的,則可能需要考慮同步日誌記錄。

Clearcove.Logging 通過使用單個System.Thread.Timer 例項來實現非同步日誌寫入。我們沒有設定執行緒計時器的週期,因此計時器只會觸發一次。在所有待處理日誌條目被成功寫入日誌檔案後,計時器將重置,等到下一個間隔觸發。這種行為類似於在計時器上設定一段時間,但會阻止計時器在間隔延遲的情況下被多次觸發。

最後,通過簡單呼叫File.AppendAllText將日誌條目寫入檔案。這種呼叫可能不是多次寫入日誌檔案最有效的方法,但是出於保持程式碼儘可能簡單的前提。

好了。一個非常簡單的日誌實現完成了,它完全能夠滿足大多數應用程式的日誌需求。而且非常適合我,幫助我解決了所有的日誌記錄問題,同時將依賴關係降到最低。

進一步的工作

記錄器簡單的好處之一是它很容易理解,並且可以快速定製以滿足你的需求。例子包括滾動日誌檔案,同步日誌記錄,外部配置等。這些功能的實現就留給大家練習吧。玩的開心!

此記錄器實現的一個很大的缺點是它只是.NET。我打算儘快釋出這個日誌記錄庫的Java實現。

另外,請注意,一些CodeProject使用者可能會在下面釋出增強功能。我會嘗試在不增加複雜性的情況下合併更改,但如果你發現此記錄器不怎麼滿足你的需求,則可能需要閱讀下面的註釋。

興趣點

讓我陷入混亂的一件事是希望簡化軟體許可。我努力想使得Clearcove.Logger 免費,而不增加許可複雜性。根據我的研究,我相信Ms-PL是最寬容的許可證。不但簡單,易於閱讀和理解,並且重要的是要求你的二進位制發行版“在遵循本許可證的許可下”釋出。在我看來,這個陳述是一個開放的解釋,給了我們很大的靈活性。當然,我的看法是,一方面軟體應該儘可能免費,另一方面應該仍然給予使用者需要的保護。如果你有更加開放的許可建議,請告訴我。

下載原始碼 – 7.7 KB

譯文連結:http://www.codeceo.com/article/simple-csharp-logger.html
英文原文:A Simple Asynchronous Logger in C#
翻譯作者:碼農網 – 小峰
轉載必須在正文中標註並保留原文連結、譯文連結和譯者等資訊。]

相關文章