在你的 .NET 程式中經常會記錄一些日誌或者錯誤,為了實現這個功能,你可能會使用市面上那些現成的日誌框架(log4net,nlog ...),當然你也可以設計並開發一個自己的日誌框架,在這篇文章中,我將會帶你一起如何輕鬆愉快的建立一個自定義日誌框架,並且一步一步的構建這個簡單的 logger。
首先,你要知道什麼叫 log targets,從字面意思看就是你的日誌要輸送到哪裡?可以假定我們的日誌可以輸出到: 檔案,資料庫 或者 windows 日誌 中,下面我在 日誌框架 中定義一個列舉表示這三個輸出地。
public enum LogTarget { File, Database, EventLog }
構建 logger 類接下來實現一下 logger 類,我準備定義三個類來表示這三個output,FileLogger
, DBLogger
, EventLog
,所有的這些類都需要繼承基類 LogBase,下面上一下程式碼展示一下這些類的繼承關係。
public abstract class LogBase { public abstract void Log(string message); } public class FileLogger : LogBase { public string filePath = @”D:\IDGLog.txt”; public override void Log(string message) { using (StreamWriter streamWriter = new StreamWriter(filePath)) { streamWriter.WriteLine(message); streamWriter.Close(); } } } public class DBLogger : LogBase { string connectionString = string.Empty; public override void Log(string message) { //Code to log data to the database } } public class EventLogger: LogBase { public override void Log(string message) { EventLog eventLog = new EventLog(“”); eventLog.Source ="IDGEventLog"; eventLog.WriteEntry(message); } }
上面 DBLogger 的 Log 方法我故意沒有實現,你可以在學習完本文後自己來實現這一塊的邏輯,將日誌記錄到資料庫中。
正如你看到的,上面三個類:FileLogger
,EventLog
和 DBLogger
繼承了抽象類 LogBase,這個抽象類定義了一個抽象方法 Log(), 這個 Log() 方法中定義了一個 string 型別的引數,這個引數的內容將會被記錄到 file 或者 database 或者 windows event 中。
現在我們一起來構建一個 LogHelper 類,這個類可以用簡單工廠模式,根據引數的不同建立不同的 XXXLogger 子類,用簡單工廠簡化我們呼叫其中各個子類的 Log() 方法,下面展示了具體程式碼:
public static class LogHelper { private static LogBase logger = null; public static void Log(LogTarget target, string message) { switch(target) { case LogTarget.File: logger = new FileLogger(); logger.Log(message); break; case LogTarget.Database: logger = new DBLogger(); logger.Log(message); break; case LogTarget.EventLog: logger = new EventLogger(); logger.Log(message); break; default: return; } } }
LogHelper 類的 Log() 方法接收一個 string 引數和一個 LogTarget 列舉例項,然後使用 switch: case
結構去決定記錄日誌的 target 是哪一個。
我去,我忘了使用同步機制對這些子類的 log() 方法的呼叫,現在趕緊同步一下,我可以使用 C# 中的 lock 關鍵詞 在 各個子類的 log 方法的合適地方使用,可以參考下面程式碼的 LogBase 類,我在這個類中定義了一個 protected 型別的 lockObj 物件,這個物件會被所有子類中的 Log 方法所使用,下面就是這個類的修改版本:
public abstract class LogBase { protected readonly object lockObj = new object(); public abstract void Log(string message); } public class FileLogger : LogBase { public string filePath = @”D:\IDGLog.txt”; public override void Log(string message) { lock (lockObj) { using (StreamWriter streamWriter = new StreamWriter(filePath)) { streamWriter.WriteLine(message); streamWriter.Close(); } } } } public class EventLogger : LogBase { public override void Log(string message) { lock (lockObj) { EventLog m_EventLog = new EventLog(“”); m_EventLog.Source ="IDGEventLog"; m_EventLog.WriteEntry(message); } } } public class DBLogger : LogBase { string connectionString = string.Empty; public override void Log(string message) { lock (lockObj) { //Code to log data to the database } } }
現在你可以呼叫 LogHelper.Log 方法了,指定一個 LogTarget 列舉引數和一個需要記錄到日誌的文字,如下程式碼所示:
class Program { static void Main(string[] args) { LogHelper.Log(LogTarget.File, “Hello”); } }
如果你需要切換日誌的記錄方式,實現起來很簡單,在 LogHelper.Log 方法中傳遞一個不同的 LogTarget 引數即可。
這個框架做的非常簡單,還有太多需要實現的功能去完善這個 logger framework,比如說,你可以加入 非同步 和 佇列,以便應對有大量的 message 被灌入,因為有了非同步,logger 在寫入日誌的時候不會被當前執行緒阻塞,同樣,你也可以實現一些 message 的級別,比如說 info,warning,error 等等。