基於log4net日誌元件的使用
阿新 • • 發佈:2021-11-23
一、前言
系統執行的時候,生產上需要使用檔案來記錄一些系統執行中的日誌,比如系統的對接日誌、錯誤資訊、警告資訊或者特定除錯資訊,通過對記錄的資訊回溯系統執行過程,查詢系統問題,幫助開發者解決線上問題等。這個系統都要使用日誌元件,一般都會有相應的開源元件,比如log4net。
二、使用
1、在專案引用中引入log4net元件,然後對日誌配置檔案進行配置,配置資訊涉及到資料夾位置、檔名稱格式、檔案最大大小、檔案的內容格式、日誌級別等資訊,比如Log4Net.config。
<?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/> </configSections> <log4net> <appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender"> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="[%p %t] [%date{yyyy-MM-dd HH:mm:ss,fff}] %-5l - %m%n"/> </layout> </appender> <appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender"> <!--日誌路徑--> <param name= "File" value= "logs\Info\log_"/> <!--是否是向檔案中追加日誌--> <param name= "AppendToFile" value= "true"/> <!--log保留天數--> <param name= "MaxSizeRollBackups" value= "10"/> <!--日誌檔名是否是固定不變的--> <param name= "StaticLogFileName" value= "false"/> <!--日誌檔名格式為--> <param name= "DatePattern" value= "yyyy-MM-dd".log""/> <!--日誌根據日期滾動--> <param name= "RollingStyle" value= "Date"/> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="記錄時間:%date 執行緒ID:[%thread] 日誌級別: %-5level 類:%logger - 描述:%message %n"/> </layout> <filter type="log4net.Filter.LevelRangeFilter"> <param name="LevelMin" value="Info" /> <param name="LevelMax" value="WARN" /> </filter> </appender> <appender name="RollingFile" type="log4net.Appender.RollingFileAppender"> <param name= "File" value= "logs\Error\Log_"/> <param name= "AppendToFile" value= "true"/> <param name= "MaxSizeRollBackups" value= "10"/> <param name= "StaticLogFileName" value= "false"/> <param name= "DatePattern" value= "yyyy-MM-dd".log""/> <param name= "RollingStyle" value= "Date"/> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="記錄時間:%date 執行緒ID:[%thread] 日誌級別: %-5level 類:%logger - 描述:%message %n"/> </layout> <filter type="log4net.Filter.LevelRangeFilter"> <param name="LevelMin" value="ERROR" /> <param name="LevelMax" value="FATAL" /> </filter> </appender> <root> <!--(高) OFF > FATAL > ERROR > WARN > INFO > DEBUG > ALL (低) --> <level value="ALL" /> <appender-ref ref="RollingLogFileAppender" /> <appender-ref ref="RollingFile" /> </root> </log4net> </configuration>
通過這個日誌檔案定義基本資訊,可以在列印日誌生成想要的日誌檔案。
2、在專案程式碼中使用配置檔案,並且提供寫入日誌的方法,定義一個日誌介面(使用檔案或者資料庫的方式),通過log4net元件來實現檔案形式記錄日誌資訊。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace TQF.Logger.Logger { /// <summary> /// 定義日誌介面類 /// </summary> public interface ILogger { /// <summary> /// 除錯日誌輸出 /// </summary> /// <param name="msg">輸出內容</param> void Debug(string msg); /// <summary> /// 除錯日誌輸出 /// </summary> /// <param name="msg">輸出內容</param> /// <param name="ex">輸出異常</param> void Debug(string msg, Exception ex); /// <summary> /// 資訊日誌輸出 /// </summary> /// <param name="msg">輸出內容</param> void Info(string msg); /// <summary> /// 資訊日誌輸出 /// </summary> /// <param name="msg">輸出內容</param> /// <param name="ex">輸出異常</param> void Info(string msg, Exception ex); /// <summary> /// 警告日誌輸出 /// </summary> /// <param name="msg">輸出內容</param> void Warn(string msg); /// <summary> /// 警告日誌輸出 /// </summary> /// <param name="msg">輸出內容</param> /// <param name="ex">輸出異常</param> void Warn(string msg, Exception ex); /// <summary> /// 錯誤日誌輸出 /// </summary> /// <param name="msg">輸出內容</param> void Error(string msg); /// <summary> /// 錯誤日誌輸出 /// </summary> /// <param name="msg">輸出內容</param> /// <param name="ex">輸出異常</param> void Error(string msg, Exception ex); /// <summary> /// 致命日誌輸出 /// </summary> /// <param name="msg">輸出內容</param> void Fatal(string msg); /// <summary> /// 致命日誌輸出 /// </summary> /// <param name="msg">輸出內容</param> /// <param name="ex">輸出異常</param> void Fatal(string msg, Exception ex); } }
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using log4net; using log4net.Appender; using log4net.Config; namespace TQF.Logger.Logger { /// <summary> /// 日誌介面實現類 /// </summary> public class Log4Helper : ILogger { /// <summary> /// 考慮使用執行緒安全的字典 /// </summary> private Dictionary<string, ILog> LogDic = new Dictionary<string, ILog>(); /// <summary> /// 定義鎖物件 /// </summary> private object _islock = new object(); /// <summary> /// 定義檔名 /// </summary> private string fileName = string.Empty; /// <summary> /// 使用建構函式日誌呼叫初始化 /// </summary> /// <param name="fileSavePath">日誌檔案儲存路徑[若路徑為空,則預設程式根目錄Logger資料夾;]</param> /// <param name="fileName">日誌檔名[若檔名為空,則預設檔名:Default]</param> public Log4Helper(string fileSavePath, string fileName, string logSuffix = ".log") { try { Init(); if (string.IsNullOrEmpty(fileSavePath)) fileSavePath = "Logger"; if (string.IsNullOrEmpty(fileName)) fileName = "Default"; this.fileName = fileName; var repository = LogManager.GetRepository(); var appenders = repository.GetAppenders(); if (appenders.Length == 0) return; var targetApder = appenders.First(p => p.Name == "FileInfoAppender") as RollingFileAppender; targetApder.File = Path.Combine(fileSavePath, this.fileName + logSuffix); targetApder.ActivateOptions(); } catch (Exception ex) { } } /// <summary> /// 快取日誌物件(通過日誌檔名快取日誌物件,減少日誌物件的建立工作) /// </summary> /// <param name="name"></param> /// <returns></returns> private ILog GetLog(string name) { try { if (LogDic == null) { LogDic = new Dictionary<string, ILog>(); } lock (_islock) { if (!LogDic.ContainsKey(name)) { LogDic.Add(name, LogManager.GetLogger(name)); } } return LogDic[name]; } catch { return LogManager.GetLogger("Default"); } } /// <summary> /// 日誌記錄初始化 /// </summary> private void Init() { // 獲取日誌配置檔案 var file = new FileInfo(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Log4net.config")); // 檢測配置檔案是否存在 if (file.Exists) { //載入配置檔案 XmlConfigurator.Configure(file); } else { new Exception("找不到日誌配置檔案!"); } } /// <summary> /// 除錯日誌輸出 /// </summary> /// <param name="msg">內容</param> public void Debug(string msg) { var log = GetLog(this.fileName); if (log == null) { return; } log.Debug(msg); } /// <summary> /// 除錯日誌輸出 /// </summary> /// <param name="msg">輸出內容</param> /// <param name="ex">輸出異常</param> public void Debug(string msg, Exception ex) { var log = GetLog(this.fileName); if (log == null) { return; } log.Debug(msg, ex); } /// <summary> /// 錯誤日誌輸出 /// </summary> /// <param name="msg">輸出內容</param> public void Error(string msg) { var log = GetLog(this.fileName); if (log == null) { return; } log.Error(msg); } /// <summary> /// 錯誤日誌輸出 /// </summary> /// <param name="msg">輸出內容</param> /// <param name="ex">輸出異常</param> public void Error(string msg, Exception ex) { var log = GetLog(this.fileName); if (log == null) { return; } log.Error(msg, ex); } /// <summary> /// 致命日誌輸出 /// </summary> /// <param name="msg">輸出內容</param> public void Fatal(string msg) { var log = GetLog(this.fileName); if (log == null) { return; } log.Fatal(msg); } /// <summary> /// 致命日誌輸出 /// </summary> /// <param name="msg">輸出內容</param> /// <param name="ex">輸出異常</param> public void Fatal(string msg, Exception ex) { var log = GetLog(this.fileName); if (log == null) { return; } log.Fatal(msg, ex); } /// <summary> /// 資訊日誌輸出 /// </summary> /// <param name="msg">輸出內容</param> public void Info(string msg) { var log = GetLog(this.fileName); if (log == null) { return; } log.Info(msg); } /// <summary> /// 資訊日誌輸出 /// </summary> /// <param name="msg">輸出內容</param> /// <param name="ex">輸出異常</param> public void Info(string msg, Exception ex) { var log = GetLog(this.fileName); if (log == null) { return; } log.Info(msg, ex); } /// <summary> /// 警告日誌輸出 /// </summary> /// <param name="msg">輸出內容</param> public void Warn(string msg) { var log = GetLog(this.fileName); if (log == null) { return; } log.Warn(msg); } /// <summary> /// 警告日誌輸出 /// </summary> /// <param name="msg">輸出內容</param> /// <param name="ex">輸出異常</param> public void Warn(string msg, Exception ex) { var log = GetLog(this.fileName); if (log == null) { return; } log.Warn(msg, ex); } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using TQF.Logger.Logger; namespace TQF.Logger { /// <summary> /// 配置檔案如果不設定屬性“複製到輸出目錄”不會在debug檔案中存在 /// </summary> class Program { static void Main(string[] args) { var log4Helper = new Log4Helper(string.Empty, "program"); log4Helper.Info("info"); } } }
通過上述程式碼,定義日誌介面(寫入方法,不同層級的寫入方法)、日誌幫助類實現介面,使用元件讀取配置檔案、獲取日誌物件寫入日誌。
3、上述是按照配置檔案格式定義好的資料夾、檔名稱、檔案內容格式,相對標準化,如果系統要特殊的記錄單個日誌資訊,比如不確定資料夾名稱、不確定的的檔名稱等,就要開發者進行自定義。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Collections.Concurrent; using System.Configuration; using log4net; using log4net.Appender; using log4net.Core; using log4net.Layout; using log4net.Repository; using log4net.Repository.Hierarchy; [assembly: log4net.Config.XmlConfigurator(Watch = true)] namespace DataPullAPI.Utility { /// <summary> /// log4net 自定義日誌類 /// </summary> public static class LogCustomHelper { /// <summary> /// 型別安全的字典物件 /// </summary> private static readonly ConcurrentDictionary<string, ILog> loggerContainer = new ConcurrentDictionary<string, ILog>(); //預設配置 private const int MAX_SIZE_ROLL_BACKUPS = 20; private const string LAYOUT_PATTERN = "%message"; private const string DATE_PATTERN = "yyyyMMdd"; private const string MAXIMUM_FILE_SIZE = "10MB"; private const string LEVEL = "ALL"; /// <summary> /// 獲取日誌物件 /// </summary> /// <param name="loggerName"></param> /// <param name="category"></param> /// <param name="additivity"></param> /// <returns></returns> public static ILog GetCustomLogger(string loggerName, string category = null, bool additivity = false) { return loggerContainer.GetOrAdd(loggerName, delegate (string name) { RollingFileAppender newAppender = GetNewFileApender(loggerName, GetFile(category, loggerName), MAX_SIZE_ROLL_BACKUPS, true, true, MAXIMUM_FILE_SIZE, RollingFileAppender.RollingMode.Composite, DATE_PATTERN, LAYOUT_PATTERN); Hierarchy repository = (Hierarchy)LogManager.GetRepository(); Logger logger = repository.LoggerFactory.CreateLogger(repository, loggerName); logger.Hierarchy = repository; logger.Parent = repository.Root; logger.Level = GetLoggerLevel(LEVEL); logger.Additivity = additivity; logger.AddAppender(newAppender); logger.Repository.Configured = true; return new LogImpl(logger); }); } /// <summary> /// 自定日誌資料夾和檔案路徑 /// </summary> /// <param name="category"></param> /// <param name="loggerName"></param> /// <returns></returns> private static string GetFile(string category, string loggerName) { if (string.IsNullOrEmpty(category)) { return string.Format(@"Logs\{0}\{1}.txt", DateTime.Now.ToString("yyyy-MM-dd"), loggerName); } else { return string.Format(@"Logs\{0}\{1}\{2}.txt", category,DateTime.Now.ToString("yyyy-MM-dd"), loggerName); } } /// <summary> /// 獲取日誌級別 /// </summary> /// <param name="level"></param> /// <returns></returns> private static Level GetLoggerLevel(string level) { if (!string.IsNullOrEmpty(level)) { switch (level.ToLower().Trim()) { case "debug": return Level.Debug; case "info": return Level.Info; case "warn": return Level.Warn; case "error": return Level.Error; case "fatal": return Level.Fatal; } } return Level.Debug; } /// <summary> /// 獲取配置資訊 /// </summary> /// <param name="appenderName"></param> /// <param name="file"></param> /// <param name="maxSizeRollBackups"></param> /// <param name="appendToFile"></param> /// <param name="staticLogFileName"></param> /// <param name="maximumFileSize"></param> /// <param name="rollingMode"></param> /// <param name="datePattern"></param> /// <param name="layoutPattern"></param> /// <returns></returns> private static RollingFileAppender GetNewFileApender(string appenderName, string file, int maxSizeRollBackups, bool appendToFile = true, bool staticLogFileName = false, string maximumFileSize = "2MB", RollingFileAppender.RollingMode rollingMode = RollingFileAppender.RollingMode.Size, string datePattern = "yyyyMMdd\".txt\"", string layoutPattern = "%d [%t] %-5p %c - %m%n") { RollingFileAppender appender = new RollingFileAppender { LockingModel = new FileAppender.MinimalLock(), Name = appenderName, File = file, AppendToFile = appendToFile, MaxSizeRollBackups = maxSizeRollBackups, MaximumFileSize = maximumFileSize, StaticLogFileName = staticLogFileName, RollingStyle = rollingMode, DatePattern = datePattern }; PatternLayout layout = new PatternLayout(layoutPattern); appender.Layout = layout; layout.ActivateOptions(); appender.ActivateOptions(); return appender; } /// <summary> /// 寫入日誌資訊 /// </summary> /// <param name="folderName">資料夾名稱</param> /// <param name="fileName">檔名稱</param> /// <param name="message">日誌資訊</param> public static void Info(string folderName, string fileName,string message) { ILog logger = GetCustomLogger(fileName, folderName); logger.Info(message); } } }
通過在寫入日誌的時候,傳入資料夾和檔名稱來自定義日誌儲存,對於特殊的日誌資訊可以通過該方法達到特定儲存要求。
總結
1、在系統生產環境記錄日誌可以幫助使用者定位系統問題,解決系統問題,對系統執行過程中可能出現異常錯誤進行捕獲記錄,對執行全過程進行回溯檢視等。
2、系統日誌不僅可以使用檔案方式進行儲存,還可以使用資料庫的方式進行儲存。