1. 程式人生 > 其它 >基於log4net日誌元件的使用

基於log4net日誌元件的使用

一、前言

  系統執行的時候,生產上需要使用檔案來記錄一些系統執行中的日誌,比如系統的對接日誌、錯誤資訊、警告資訊或者特定除錯資訊,通過對記錄的資訊回溯系統執行過程,查詢系統問題,幫助開發者解決線上問題等。這個系統都要使用日誌元件,一般都會有相應的開源元件,比如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&quot;.log&quot;"/> <!--日誌根據日期滾動--> <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&quot;.log&quot;"/> <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、系統日誌不僅可以使用檔案方式進行儲存,還可以使用資料庫的方式進行儲存。