先談談簡單的模塊,日志。在系統中日志模塊是必須的,什麼系統日志,操作日志,調試日志。這裡用的是log4net。
對log4net還不熟悉的小伙伴們趕快去搜索基礎教程哦, 我這裡就不溫故了。
那麼有人要問了,log4net確實很強大,而且我們也會用。還要單獨寫一篇文章來介紹,有必要嗎?
我簡單的舉兩個場景:
1:log4net寫入DB 還需要在 log4net中配置數據庫連接字符串, 我想log4net 和 我的 connectionStrings 用1個配置不行嗎?
2:log4net寫入參數擴展問題,我配置文件想寫入ip地址,那我代碼還要定義一個ip的參數。 那我再擴展,還需要再定義,這改動量太大了,能不能只傳一個實體類,讓log4net自己去映射那?這樣我就可以寫一些通用的方法,好多項目都可以直接拿過來用,代碼修改量也少了點。
有人覺得這都不是問題,高手請跳過。
我這裡將日志模塊單獨封裝了一個HY.Log,截圖如下:
看圖有點亂,下面我給大家捋一捋(念lv 念成lu的去面壁思過 ):
我們要在PatternConverter文件夾中定義一些可擴展參數,這樣就可以在log4net配置文件中隨心使用了,哪截圖中我實現了獲取客戶端ip、獲取服務器端ip
或許服務器mac地址。
就拿ClientIpPatternConverter.cs來說需要繼承log4net下 log4net.Layout.Pattern.PatternLayoutConverter類,來實現擴展。
using System.IO; using log4net.Core; using log4net.Layout.Pattern; namespace HY.Log.PatternConverter { /// <summary> /// B/S 客戶端地址擴展 /// </summary> internal class ClientIpPatternConverter : PatternLayoutConverter { protected override void Convert(TextWriter writer, LoggingEvent loggingEvent) { writer.Write(HY.Utilities.IPHelper.GetRemoteIPAddress()); } } }
接著在分別實現 MAC、服務端IP、 以及其它你任何想擴展的參數。
ObjectPatternConverter.cs 這個類就比較特殊了。這個自定義參數其實是讓你傳入一個實體類,然後通過反射技術,讓log4net通過配置文件的 配置自動映射要傳入的值。
上面的類 是針對特定的通用功能擴展,這個類只需要定義一個即可。
using System.IO; using System.Reflection; using log4net.Core; using log4net.Layout.Pattern; namespace HY.Log.PatternConverter { internal class ObjectPatternConverter : PatternLayoutConverter { protected override void Convert(TextWriter writer, LoggingEvent loggingEvent) { if (Option != null) { // Write the value for the specified key WriteObject(writer, loggingEvent.Repository, LookupProperty(Option, loggingEvent)); } else { // Write all the key value pairs WriteDictionary(writer, loggingEvent.Repository, loggingEvent.GetProperties()); } } /// <summary> /// 通過反射獲取傳入的日志對象的某個屬性的值 /// </summary> /// <param name="property"></param> /// <param name="loggingEvent"></param> /// <returns></returns> private object LookupProperty(string property, LoggingEvent loggingEvent) { object propertyValue = string.Empty; PropertyInfo propertyInfo = loggingEvent.MessageObject.GetType().GetProperty(property); if (propertyInfo != null) propertyValue = propertyInfo.GetValue(loggingEvent.MessageObject, null); return propertyValue; } } }
CustomLayout.cs
using HY.Log.PatternConverter; using log4net.Layout; namespace HY.Log { /// <summary> /// 定義log日志布局的參數信息 /// </summary> public class CustomLayout : PatternLayout { /// <summary> /// 構造函數 /// </summary> public CustomLayout() { #region 內部自定義 AddConverter("ServerIP", typeof(ServerIpPatternConverter)); AddConverter("ClientIP", typeof(ClientIpPatternConverter)); AddConverter("MAC", typeof(MacPatternConverter)); #endregion #region 支持開發人員自定義 AddConverter("Object", typeof(ObjectPatternConverter)); #endregion } } }
代碼將自定義的參數以 key value 的形式進行注冊, 以後我們再log4net進行配置的時候 就要記住這些關鍵字了, 這都是你自己定義的。
你敢在代碼中實現 AddConverter("XXOO", typeof(XXOOPatternConverter)); 嗎?
沒啥可說的,看代碼吧。
LogMessage.cs 這個類其實就是寫了一個傳入的的自定義參數的實體類,你可以自己寫。不過最好是繼承這個類進行擴展。
using System; namespace HY.Log { /// <summary> /// 用於記錄日志信息 /// </summary> [Serializable] public class LogMessage { /// <summary> /// 日志信息 /// </summary> public string Message { get; set; } } }
ILog.cs
using System; namespace HY.Log { public interface ILog { /// <summary> /// 寫入Debug日志, /// </summary> /// <param name="message">日志信息,占位符為 %Object{Message}</param> void Debug(string message, Exception exception = null); /// <summary> /// 寫入Debug日志 /// </summary> /// <param name="messageEntity">日志實體信息, 占位符為 %Object{實體類的屬性名稱}</param> void Debug(object messageEntity, Exception exception = null); /// <summary> /// 寫入Info日志, /// </summary> /// <param name="message">日志信息,占位符為 %Object{Message}</param> void Info(string message, Exception exception = null); /// <summary> /// 寫入Info日志 /// </summary> /// <param name="messageEntity">日志實體信息, 占位符為 %Object{實體類的屬性名稱}</param> void Info(object messageEntity, Exception exception = null); /// <summary> /// 寫入Warn日志, /// </summary> /// <param name="message">日志信息,占位符為 %Object{Message}</param> void Warn(string message, Exception exception = null); /// <summary> /// 寫入Warn日志 /// </summary> /// <param name="messageEntity">日志實體信息, 占位符為 %Object{實體類的屬性名稱}</param> void Warn(object messageEntity, Exception exception = null); /// <summary> /// 寫入Error日志, /// </summary> /// <param name="message">日志信息,占位符為 %Object{Message}</param> void Error(string message, Exception exception = null); /// <summary> /// 寫入Error日志 /// </summary> /// <param name="messageEntity">日志實體信息, 占位符為 %Object{實體類的屬性名稱}</param> void Error(object messageEntity, Exception exception = null); /// <summary> /// 寫入Fatal日志, /// </summary> /// <param name="message">日志信息,占位符為 %Object{Message}</param> void Fatal(string message, Exception exception = null); /// <summary> /// 寫入Fatal日志 /// </summary> /// <param name="messageEntity">日志實體信息, 占位符為 %Object{實體類的屬性名稱}</param> void Fatal(object messageEntity, Exception exception = null); } }
NormalLog.cs
using System; using System.Collections.Generic; namespace HY.Log { public class NormalLog : ILog { #region private private static Dictionary<string, log4net.ILog> listILog = new Dictionary<string, log4net.ILog>(); //Log對象集合 private log4net.ILog iLog; //當前日志對象的實例 #endregion #region 構造函數 /// <summary> /// 構造函數,傳入Log4NET 的ILog對象 /// </summary> /// <param name="logger"></param> public NormalLog(log4net.ILog logger) { string LoggerName = logger.Logger.Name; //logger 配置節名稱 if (!listILog.ContainsKey(LoggerName)) { lock (listILog) { if (!listILog.ContainsKey(LoggerName)) { listILog.Add(LoggerName, logger); } else { listILog[LoggerName] = logger; } } } else if (listILog[LoggerName] == null) { listILog[LoggerName] = logger; } iLog = listILog[LoggerName]; } public NormalLog(string loggerName) { log4net.ILog logger = log4net.LogManager.GetLogger(loggerName); string LoggerName = logger.Logger.Name; //logger 配置節名稱 if (!listILog.ContainsKey(LoggerName)) { listILog.Add(LoggerName, logger); } else if (listILog[LoggerName] == null) { listILog[LoggerName] = logger; } iLog = listILog[LoggerName]; } #endregion #region 寫入日志 /// <summary> /// 寫入Debug日志, /// </summary> /// <param name="message">日志信息,占位符為 %Object{Message}</param> public void Debug(string message, Exception exception = null) { LogMessage messageEntity = new LogMessage { Message = message }; Debug(messageEntity, exception); } /// <summary> /// 寫入Debug日志 /// </summary> /// <param name="messageEntity">日志實體信息, 占位符為 %Object{實體類的屬性名稱}</param> public void Debug(object messageEntity, Exception exception = null) { if (iLog.IsDebugEnabled) { if (exception != null) { iLog.Debug(messageEntity, exception); } else { iLog.Debug(messageEntity); } } } /// <summary> /// 寫入Info日志, /// </summary> /// <param name="message">日志信息,占位符為 %Object{Message}</param> public void Info(string message, Exception exception = null) { LogMessage messageEntity = new LogMessage { Message = message }; Info(messageEntity, exception); } /// <summary> /// 寫入Info日志 /// </summary> /// <param name="messageEntity">日志實體信息, 占位符為 %Object{實體類的屬性名稱}</param> public void Info(object messageEntity, Exception exception = null) { if (iLog.IsInfoEnabled) { if (exception != null) { iLog.Info(messageEntity, exception); } else { iLog.Info(messageEntity); } } } /// <summary> /// 寫入Warn日志, /// </summary> /// <param name="message">日志信息,占位符為 %Object{Message}</param> public void Warn(string message, Exception exception = null) { LogMessage messageEntity = new LogMessage { Message = message }; Warn(messageEntity, exception); } /// <summary> /// 寫入Warn日志 /// </summary> /// <param name="messageEntity">日志實體信息, 占位符為 %Object{實體類的屬性名稱}</param> public void Warn(object messageEntity, Exception exception = null) { if (iLog.IsWarnEnabled) { if (exception != null) { iLog.Warn(messageEntity, exception); } else { iLog.Warn(messageEntity); } } } /// <summary> /// 寫入Error日志, /// </summary> /// <param name="message">日志信息,占位符為 %Object{Message}</param> public void Error(string message, Exception exception = null) { LogMessage messageEntity = new LogMessage { Message = message }; Error(messageEntity, exception); } /// <summary> /// 寫入Error日志 /// </summary> /// <param name="messageEntity">日志實體信息, 占位符為 %Object{實體類的屬性名稱}</param> public void Error(object messageEntity, Exception exception = null) { if (iLog.IsErrorEnabled) { if (exception != null) { iLog.Error(messageEntity, exception); } else { iLog.Error(messageEntity); } } } /// <summary> /// 寫入Fatal日志, /// </summary> /// <param name="message">日志信息,占位符為 %Object{Message}</param> public void Fatal(string message, Exception exception = null) { LogMessage messageEntity = new LogMessage { Message = message }; Fatal(messageEntity, exception); } /// <summary> /// 寫入Fatal日志 /// </summary> /// <param name="messageEntity">日志實體信息, 占位符為 %Object{實體類的屬性名稱}</param> public void Fatal(object messageEntity, Exception exception = null) { if (iLog.IsFatalEnabled) { if (exception != null) { iLog.Fatal(messageEntity, exception); } else { iLog.Fatal(messageEntity); } } } #endregion } }
LogManager.cs
這個類定義的了log4net模塊的初始化,數據庫連接設置……
using System; using System.IO; using log4net.Appender; using log4net.Config; using log4net.Repository.Hierarchy; namespace HY.Log { public class LogManager { /// <summary> /// 設置DB連接字符串 /// </summary> /// <param name="conString">連接字符串</param> /// <param name="loggerName">loggerName</param> /// <param name="appenderName">appenderName</param> public static void ConfigConnection(string conString, string loggerName, string appenderName = "ADONetAppender") { try { Hierarchy h = log4net.LogManager.GetRepository() as Hierarchy; if (h != null) { AdoNetAppender adoAppender = (AdoNetAppender)h.GetLogger(loggerName, h.LoggerFactory).GetAppender(appenderName); if (adoAppender != null) { adoAppender.ConnectionString = conString; adoAppender.ActivateOptions(); } } } catch (NullReferenceException) { } } /// <summary> /// 初始化HY.Log, Log配置文件需要寫到 Web.config OR App.config /// </summary> public static void Init() { XmlConfigurator.Configure(); } /// <summary> /// 初始化HY.Log, /// </summary> /// <param name="configFileName">制定Log配置文件的文件絕對路徑</param> public static void Init(string configFileName) { XmlConfigurator.Configure(new FileInfo(configFileName)); } /// <summary> /// 檢索Logger名稱返回日志處理接口 /// </summary> /// <param name="name">Logger名稱</param> /// <returns>日志接口</returns> public static ILog GetLogger(string name) { var log4Logger = log4net.LogManager.GetLogger(name); return new NormalLog(log4Logger); } } }
上面的代碼大家看到了,已經可以成功編譯一個dll 文件了。
先來編輯一個log4net配置文件
<?xml version="1.0"?> <configuration> <!--Log4net Begin--> <configSections> <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" /> </configSections> <log4net> <!--Log4net Begin,程序運行異常記錄--> <logger name="LogInfoDB"> <level value="ALL" /> <appender-ref ref="ADONetAppender" /> </logger> <appender name="ADONetAppender" type="log4net.Appender.ADONetAppender"> <bufferSize value="1" /> <connectionType value="System.Data.SqlClient.SqlConnection, System.Data, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> <!--<connectionString value="Data Source=127.0.0.1;Database=test;uid=sa;pwd=test;Max Pool Size=300;Connect Timeout=15;" />--> <commandText value="INSERT INTO HY_Log ([LogType],[ModelName],[Message],[Exception],[IP],[Log_Date],[UserID]) VALUES (@LogType,@ModelName, @Message, @Exception, @IP,@Log_Date,@UserID)" /> <parameter> <parameterName value="@LogType" /> <dbType value="String" /> <size value="20" /> <layout type="HY.Log.CustomLayout"> <conversionPattern value="%Object{LogType}" /> </layout> </parameter> <parameter> <parameterName value="@ModelName" /> <dbType value="String" /> <size value="110" /> <layout type="HY.Log.CustomLayout"> <conversionPattern value="%Object{ModelName}" /> </layout> </parameter> <parameter> <parameterName value="@Message" /> <dbType value="String" /> <size value="2000" /> <layout type="HY.Log.CustomLayout"> <conversionPattern value="%Object{Message}" /> </layout> </parameter> <parameter> <parameterName value="@Exception" /> <dbType value="String" /> <size value="8000" /> <layout type="HY.Log.CustomLayout"> <conversionPattern value="%Object{Exception}" /> </layout> </parameter> <parameter> <parameterName value="@IP" /> <dbType value="String" /> <size value="20" /> <layout type="HY.Log.CustomLayout"> <conversionPattern value="%ClientIP" /> </layout> </parameter> <parameter> <parameterName value="@Log_Date" /> <dbType value="DateTime" /> <layout type="log4net.Layout.RawTimeStampLayout" /> </parameter> <parameter> <parameterName value="@UserID" /> <dbType value="String" /> <size value="100" /> <layout type="HY.Log.CustomLayout"> <conversionPattern value="%Object{UserID}" /> </layout> </parameter> </appender> </log4net> <!--Log4net End--> </configuration> <!--%m(message):輸出的日志消息,如ILog.Debug(…)輸出的一條消息 %n(new line):換行 %d(datetime):輸出當前語句運行的時刻 %r(run time):輸出程序從運行到執行到當前語句時消耗的毫秒數 %t(thread id):當前語句所在的線程ID %p(priority): 日志的當前優先級別,即DEBUG、INFO、WARN…等 %c(class):當前日志對象的名稱,例如: %f(file):輸出語句所在的文件名。 %l(line):輸出語句所在的行號。-->
上面的配置文件是一個寫入DB的配置
請注意如下配置節:
<layout type="HY.Log.CustomLayout">
<conversionPattern value="%Object{Exception}" />
<conversionPattern value="%Object{LogType}" />
<conversionPattern value="%ClientIP" />
這便是采用了自定義配置。
創建HY.ILog對象
public static ILog ilog = null;
HY.Log.LogManager.Init(HttpContext.Current.Server.MapPath(@"~/Config/log4net.config"));//加載配置文件
HY.Log.LogManager.ConfigConnection(ConfigurationManager.ConnectionStrings["Connection"].ToString(), "LogInfoDB");//修改連接字符串
ilog = LogManager.GetLogger("LogInfoText"); //獲取Ilog對象 這裡可以采用單例模式。代碼就不貼了,
自定義實體類:
[Serializable] public class LogEntity { /// <summary> /// 日志類型 /// </summary> public LogType LogType { get; set; } /// <summary> /// 模塊名稱 /// </summary> public string ModelName { get; set; } /// <summary> /// 信息 /// </summary> public new string Message { get; set; } /// <summary> /// 異常信息 /// </summary> public string Exception { get; set; } public string UserID { get; set; }
LogEntity loginfo = new LogEntity();
loginfo.ModelName = "ModelName";
loginfo.Message = "Message";
loginfo.Exception = "Exception";
loginfo.UserID = "UserID";
loginfo.LogType = "LogType.Business";
ilog.Error(loginfo,ex);
到這裡就已經完了。 關於使用的代碼比較粗糙,這塊就需要根據你程序的具體需求來實現了。這裡點到為止。 因為後續我們將會采用spring.net來注入。 將在後續的文章介紹。
各位如果有更好的建立請給我留言, 請各位不吝賜教。
相關文章:
搭建一套自己實用的.net架構(1)【概述】
搭建一套自己實用的.net架構(2)【日志模塊-log4net】
搭建一套自己實用的.net架構(3)【ORM-Dapper+DapperExtensions】
搭建一套自己實用的.net架構(4)【CodeBuilder-RazorEngine】