<?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net"/> </configSections> <log4net> <!-- ConversionPattern 解釋 %m(message):輸出的日誌消息,如ILog.Debug(…)輸出的一條消息 %n(new line):換行 %d(datetime):輸出當前語句運行的時刻 %r(run time):輸出程序從運行到執行到當前語句時消耗的毫秒數 %t(thread id):當前語句所在的線程ID %p(priority): 日誌的當前優先級別,即DEBUG、INFO、WARN…等 %c(class):當前日誌對象的名稱 %L:輸出語句所在的行號 %F:輸出語句所在的文件名 %-數字:表示該項的最小長度,若是不夠,則用空格填充 --> <root> <!--控制級別,由低到高: ALL|DEBUG|INFO|WARN|ERROR|FATAL|OFF--> <!--好比定義級別爲INFO,則INFO級別向下的級別,好比DEBUG日誌將不會被記錄--> <!--若是沒有定義LEVEL的值,則缺省爲DEBUG--> <level value="ALL"/> <appender-ref ref="AppLogAppender"/> </root> <!--定義輸出到控制檯命令行中--> <logger name="SysLogger"> <level value="ALL"/> <appender-ref ref="TextAppender" /> </logger> <logger name="AppLogger"> <level value="ALL"/> <appender-ref ref="AppLogAppender" /> </logger> <!--定義輸出到控制檯命令行中--> <appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender"> <layout type="RMFramework.Common.Logger.MyLayout"> <param name="ConversionPattern" value="日誌時間:%d %n日誌級別:%-5p %n用 戶 ID:%Property{UserID} %n用戶姓名:%Property{UserName} %n日誌信息:%Property{Message} %n異常信息:%exception %n%n" /> </layout> </appender> <!--定義輸出到windows事件中--> <appender name="WindowsAppender" type="log4net.Appender.EventLogAppender"> <layout type="RMFramework.Common.Logger.MyLayout"> <param name="ConversionPattern" value="日誌時間:%d %n日誌級別:%-5p %n用 戶 ID:%Property{UserID} %n用戶姓名:%Property{UserName} %n日誌信息:%Property{Message} %n異常信息:%exception %n%n" /> </layout> </appender> <!--定義輸出到文件中 用於記錄系統級--> <appender name="TextAppender" type="log4net.Appender.RollingFileAppender"> <param name="File" value="Logs\\SysLog\\" /> <param name="AppendToFile" value="true" /> <param name="MaxFileSize" value="10240" /> <param name="MaxSizeRollBackups" value="100" /> <param name="StaticLogFileName" value="false" /> <param name="DatePattern" value="yyyyMMdd".log"" /> <param name="RollingStyle" value="Date" /> <layout type="RMFramework.Common.Logger.MyLayout"> <param name="ConversionPattern" value="日誌時間:%d %n日誌級別:%-5p %n用 戶 ID:%Property{UserID} %n用戶姓名:%Property{UserName} %n日誌信息:%Property{Message} %n異常信息:%exception %n%n" /> </layout> </appender> <!--定義輸出文本,用於記錄業務級--> <appender name="AppLogAppender" type="log4net.Appender.RollingFileAppender"> <file value="Logs/AppLog/" /> <appendToFile value="true" /> <rollingStyle value="Date" /> <staticLogFileName value="false" /> <datePattern value="yyyyMMdd".log"" /> <layout type="log4net.Layout.PatternLayout"> <!--輸出格式--> <conversionPattern value="%date %-5level %message%newline" /> </layout> <!--<filter type="log4net.Filter.LoggerMatchFilter"> <loggerToMatch value="SystemLog" /> </filter>--> </appender> <!--定義輸出到數據庫--> <appender name="DataBaseAppender" type="log4net.Appender.AdoNetAppender"> <!--日誌緩存寫入條數--> <bufferSize value="1" /> <!--日誌數據庫鏈接串--> <connectionType value="System.Data.SqlClient.SqlConnection, System.Data, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> <connectionString value="data source=.\SQL2008;initial catalog=Demo;integrated security=false;persist security info=True;User ID=sa;Password=1qaz" /> <!--日誌數據庫腳本--> <commandText value="INSERT INTO LogInfo ([LogDate],[LogLevel],[UserId],[UserName],[Message],[Exception]) VALUES (@LogDate, @LogLevel,@UserId,@UserName, @Message, @Exception)" /> <!--日誌時間LogDate --> <parameter> <parameterName value="@LogDate" /> <dbType value="String" /> <size value="30" /> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%date{yyyy-MM-dd HH:mm:ss}" /> </layout> </parameter> <!--日誌類型LogLevel --> <parameter> <parameterName value="@LogLevel" /> <dbType value="String" /> <size value="10" /> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%level" /> </layout> </parameter> <!--自定義UserId --> <parameter> <parameterName value="@UserId" /> <dbType value="String" /> <size value="20" /> <layout type="RMFramework.Common.Logger.MyLayout"> <conversionPattern value="%Property{UserID}" /> </layout> </parameter> <!--自定義UserName --> <parameter> <parameterName value="@UserName" /> <dbType value="String" /> <size value="50" /> <layout type="RMFramework.Common.Logger.MyLayout"> <conversionPattern value="%Property{UserName}" /> </layout> </parameter> <!--自定義Message --> <parameter> <parameterName value="@Message" /> <dbType value="String" /> <size value="200" /> <layout type="RMFramework.Common.Logger.MyLayout"> <conversionPattern value="%Property{Message}" /> </layout> </parameter> <!--異常信息Exception --> <parameter> <parameterName value="@Exception" /> <dbType value="String" /> <size value="4000" /> <layout type="log4net.Layout.ExceptionLayout" /> </parameter> </appender> </log4net> </configuration>
2.使用前準備工做數據庫
使用nuget引入Log4net; windows
在引入類庫中的AssemblyInfo.cs加入緩存
[assembly: log4net.Config.XmlConfigurator(ConfigFile = "log4net.config", ConfigFileExtension = "config", Watch = true)]
public static readonly log4net.ILog loginfo = log4net.LogManager.GetLogger("AppLogger");
實例化ILog後,便可進行調用。LogManager.GetLogger傳入參數與log4net.config內的Logger要匹配。app
當只有一個配置時,也能夠使用以下代碼。若多個logger配置時,僅讀取第一個輸出配置。ide
LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
完整代碼以下:ui
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using log4net; namespace RMFramework.Common.Logger { public class Log4System { public static readonly log4net.ILog loginfo = log4net.LogManager.GetLogger("AppLogger"); /// <summary> /// 業務日誌記錄 /// </summary> /// <param name="info"></param> public void WriteLog(string info) { if (loginfo.IsInfoEnabled) { loginfo.Info(info); } } /// <summary> /// 報錯日誌記錄 /// </summary> /// <param name="info"></param> /// <param name="ex"></param> public void WriteLog(string info, Exception ex) { if (loginfo.IsErrorEnabled) { loginfo.Error(info, ex); } } /// <summary> /// 調試日誌記錄 /// </summary> /// <param name="info"></param> /// <param name="values"></param> public void WriteLog(string info, params object[] values) { if (loginfo.IsDebugEnabled) { loginfo.Debug(string.Format(info, values)); } } } }
3.2 自定義輸出this
自定義輸出對象,映射到配置文件內的layout標籤spa
支持輸出到控制檯,支持輸出到文本,支持輸出到數據庫。命令行
using log4net; using log4net.Config; using log4net.Core; using log4net.Layout; using log4net.Layout.Pattern; using System; using System.IO; using System.Reflection; namespace RMFramework.Common.Logger { public class LogHelper { /// <summary> /// LoggerName /// </summary> public static string LoggerName = string.Empty; /// <summary> /// 用戶ID /// </summary> public static string UserID = string.Empty; /// <summary> /// 用戶名稱 /// </summary> public static string UserName = string.Empty; private static ILog iLog; private static LogEntity logEntity; /// <summary> /// 接口 /// </summary> private static ILog log { get { var configFile = new FileInfo(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "log4net.config")); if (!configFile.Exists) { throw new Exception("未配置log4net配置文件!"); } // 設置日誌配置文件路徑 XmlConfigurator.Configure(configFile); //string path = AppDomain.CurrentDomain.SetupInformation.ApplicationBase + @"\Log4Net.config";//AppDomain.CurrentDomain.BaseDirectory //XmlConfigurator.Configure(new FileInfo(path)); if (iLog == null) { iLog = LogManager.GetLogger(LoggerName); } else { if (iLog.Logger.Name != LoggerName) { iLog = LogManager.GetLogger(LoggerName); } } return iLog; } } /// <summary> /// 構造消息實體 /// </summary> /// <param name="message"></param> /// <returns></returns> private static LogEntity BuildMessageMode(string message) { if (logEntity == null) { logEntity = new LogEntity(); logEntity.UserID = UserID; logEntity.UserName = UserName; logEntity.Message = message; } else logEntity.Message = message; return logEntity; } /// <summary> /// 調試 /// </summary> /// <param name="message">消息</param> public static void Debug(string message) { if (log.IsDebugEnabled) log.Debug(BuildMessageMode(message)); } /// <summary> /// 調試 /// </summary> /// <param name="message">消息</param> /// <param name="exception">異常</param> public static void Debug(string message, Exception ex) { if (log.IsDebugEnabled) log.Debug(BuildMessageMode(message), ex); } /// <summary> /// 信息 /// </summary> /// <param name="message">消息</param> public static void Info(string message) { if (log.IsInfoEnabled) log.Info(BuildMessageMode(message)); } /// <summary> /// 信息 /// </summary> /// <param name="message">消息</param> /// <param name="exception">異常</param> public static void Info(string message, Exception ex) { if (log.IsInfoEnabled) log.Info(BuildMessageMode(message), ex); } /// <summary> /// 通常錯誤 /// </summary> /// <param name="message">消息</param> public static void Error(string message) { if (log.IsErrorEnabled) log.Error(BuildMessageMode(message)); } /// <summary> /// 通常錯誤 /// </summary> /// <param name="message">消息</param> /// <param name="exception">異常</param> public static void Error(string message, Exception exception) { if (log.IsErrorEnabled) log.Error(BuildMessageMode(message), exception); } /// <summary> /// 警告 /// </summary> /// <param name="message">消息</param> public static void Warn(string message) { if (log.IsWarnEnabled) log.Warn(BuildMessageMode(message)); } /// <summary> /// 警告 /// </summary> /// <param name="message">消息</param> /// <param name="exception">異常</param> public static void Warn(string message, Exception ex) { if (log.IsWarnEnabled) log.Warn(BuildMessageMode(message), ex); } /// <summary> /// 嚴重 /// </summary> /// <param name="message">消息</param> public static void Fatal(string message) { if (log.IsFatalEnabled) log.Fatal(BuildMessageMode(message)); } /// <summary> /// 嚴重 /// </summary> /// <param name="message">消息</param> /// <param name="exception">異常</param> public static void Fatal(string message, Exception ex) { if (log.IsFatalEnabled) log.Fatal(BuildMessageMode(message), ex); } } public class MyLayout : PatternLayout { public MyLayout() { this.AddConverter("Property", typeof(MyPatternConverter)); } } public class LogEntity { public string UserID { get; set; } public string UserName { get; set; } public string Message { get; set; } public Exception Exception { get; set; } } public class MyPatternConverter : PatternLayoutConverter { protected override void Convert(TextWriter writer, LoggingEvent loggingEvent) { if (Option != null) { WriteObject(writer, loggingEvent.Repository, LookupProperty(Option, loggingEvent)); } else { WriteDictionary(writer, loggingEvent.Repository, loggingEvent.GetProperties()); } } /// <summary> /// 經過反射獲取傳入的日誌對象的某個屬性的值 /// </summary> /// <param name="property"></param> /// <returns></returns> private object LookupProperty(string property, log4net.Core.LoggingEvent loggingEvent) { object propertyValue = string.Empty; PropertyInfo propertyInfo = loggingEvent.MessageObject.GetType().GetProperty(property); if (propertyInfo != null) propertyValue = propertyInfo.GetValue(loggingEvent.MessageObject, null); return propertyValue; } } }
3.3 隊列輸出線程
將日誌添加到隊列,隊列輸出到磁盤,效率高。
using log4net; using log4net.Config; using System; using System.Collections.Concurrent; using System.IO; using System.Threading; namespace RMFramework.Common.Logger { public sealed class FlashLogger { /// <summary> /// 記錄消息Queue /// </summary> private readonly ConcurrentQueue<FlashLogMessage> _que; /// <summary> /// 信號 /// </summary> private readonly ManualResetEvent _mre; /// <summary> /// 日誌 /// </summary> private readonly ILog _log; /// <summary> /// 日誌 /// </summary> private static FlashLogger _flashLog = new FlashLogger(); private FlashLogger() { var configFile = new FileInfo(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "log4net.config")); if (!configFile.Exists) { throw new Exception("未配置log4net配置文件!"); } // 設置日誌配置文件路徑 XmlConfigurator.Configure(configFile); _que = new ConcurrentQueue<FlashLogMessage>(); _mre = new ManualResetEvent(false); _log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); } /// <summary> /// 實現單例 /// </summary> /// <returns></returns> public static FlashLogger Instance() { return _flashLog; } /// <summary> /// 另外一個線程記錄日誌,只在程序初始化時調用一次 /// </summary> public void Register() { Thread t = new Thread(new ThreadStart(WriteLog)); t.IsBackground = false; t.Start(); } /// <summary> /// 從隊列中寫日誌至磁盤 /// </summary> private void WriteLog() { while (true) { // 等待信號通知 _mre.WaitOne(); FlashLogMessage msg; // 判斷是否有內容須要如磁盤 從列隊中獲取內容,並刪除列隊中的內容 while (_que.Count > 0 && _que.TryDequeue(out msg)) { // 判斷日誌等級,而後寫日誌 switch (msg.Level) { case FlashLogLevel.Debug: _log.Debug(msg.Message, msg.Exception); break; case FlashLogLevel.Info: _log.Info(msg.Message, msg.Exception); break; case FlashLogLevel.Error: _log.Error(msg.Message, msg.Exception); break; case FlashLogLevel.Warn: _log.Warn(msg.Message, msg.Exception); break; case FlashLogLevel.Fatal: _log.Fatal(msg.Message, msg.Exception); break; } } // 從新設置信號 _mre.Reset(); Thread.Sleep(1); } } /// <summary> /// 寫日誌 /// </summary> /// <param name="message">日誌文本</param> /// <param name="level">等級</param> /// <param name="ex">Exception</param> public void EnqueueMessage(string message, FlashLogLevel level, Exception ex = null) { if ((level == FlashLogLevel.Debug && _log.IsDebugEnabled) || (level == FlashLogLevel.Error && _log.IsErrorEnabled) || (level == FlashLogLevel.Fatal && _log.IsFatalEnabled) || (level == FlashLogLevel.Info && _log.IsInfoEnabled) || (level == FlashLogLevel.Warn && _log.IsWarnEnabled)) { _que.Enqueue(new FlashLogMessage { Message = "[" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss,fff") + "]\r\n" + message, Level = level, Exception = ex }); // 通知線程往磁盤中寫日誌 _mre.Set(); } } public static void Debug(string msg, Exception ex = null) { Instance().EnqueueMessage(msg, FlashLogLevel.Debug, ex); } public static void Error(string msg, Exception ex = null) { Instance().EnqueueMessage(msg, FlashLogLevel.Error, ex); } public static void Fatal(string msg, Exception ex = null) { Instance().EnqueueMessage(msg, FlashLogLevel.Fatal, ex); } public static void Info(string msg, Exception ex = null) { Instance().EnqueueMessage(msg, FlashLogLevel.Info, ex); } public static void Warn(string msg, Exception ex = null) { Instance().EnqueueMessage(msg, FlashLogLevel.Warn, ex); } } /// <summary> /// 日誌等級 /// </summary> public enum FlashLogLevel { Debug, Info, Error, Warn, Fatal } /// <summary> /// 日誌內容 /// </summary> public class FlashLogMessage { public string Message { get; set; } public FlashLogLevel Level { get; set; } public Exception Exception { get; set; } } }