log4Net 高效能寫入和CSV格式的執行個體詳解

來源:互聯網
上載者:User

最近在使用log4net,在使用之前我們必須知道檔案流是如何操作的,否則就是盲人摸向。。。,在FileAppender.cs檔案裡面有LockingModelBase來控制流程的鎖,預設有3個子類

ExclusiveLock:預設的,Hold an exclusive lock on the output file,Open the file once for writing and hold it open until CloseFile is called. Maintains an exclusive lock on the file during this time.

MinimalLock:Acquires the file lock for each write,Opens the file once for each AcquireLock / ReleaseLock cycle, thus holding the lock for the minimal amount of time.This method of locking is considerably slower than FileAppender.ExclusiveLock but allows other processes to move/delete the log file whilst logging continues.

InterProcessLock:Provides cross-process file locking.使用Mutex來實現多進程

這裡意思是MinimalLock比ExclusiveLock慢一點,因為它每次都會開啟關閉檔案流。

不過有2個類感覺比較重要PatternString.cs


和PatternLayout.cs




如果log檔案在一個公用的目錄,建議大家log檔案加上電腦名稱、應用程式名稱、進程ID(如web 有多個工作者) 如:

<file type="log4net.Util.PatternString" value="\\192.168.0.1\logs\%env{COMPUTERNAME}\%appsetting{ApplicationName}\%processid\Log\" />

但是這裡的log記錄預設都是採用同步方式的,但是我個人更趨向用非同步多線程的思路來寫log,首先log的資訊記錄在記憶體ConcurrentQueue裡面,然後在通過一個後台線程把ConcurrentQueue裡面的東西記錄到檔案流裡面。至於效能高出多少我想就不用多說了吧,寫記憶體肯定比寫流快啊

具體實現code如下:

[assembly: log4net.Config.XmlConfigurator(Watch = true, ConfigFile = "log4net.config")]namespace ConsoleApp{    using log4net;    using System;    using System.Collections.Concurrent;    using System.Threading;    using System.Threading.Tasks;    public sealed class QueueLogger    {        /// <summary>        /// 記錄訊息Queue        /// </summary>        private readonly ConcurrentQueue<QueueLogMessage> _que;        /// <summary>        /// 訊號        /// </summary>        private readonly ManualResetEvent _mre;        /// <summary>        /// 日誌        /// </summary>        private readonly ILog _log;        /// <summary>        /// 日誌        /// </summary>        private static QueueLogger flashLog = new QueueLogger();        private QueueLogger()        {            // 設定日誌設定檔路徑            //XmlConfigurator.Configure(new FileInfo(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "log4net.config")));            _que = new ConcurrentQueue<QueueLogMessage>();            _mre = new ManualResetEvent(false);            _log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);            Task.Run(() => { WriteLog(); });        }        /// <summary>        /// 從隊列中寫日誌至磁碟        /// </summary>        private void WriteLog()        {            while (true)            {                // 等待訊號通知                _mre.WaitOne();                QueueLogMessage msg;                // 判斷是否有內容需要如磁碟 從列隊中擷取內容,並刪除列隊中的內容                while (_que.Count > 0 && _que.TryDequeue(out msg))                {                    // 判斷日誌等級,然後寫日誌                    switch (msg.Level)                    {                        case QueueLogLevel.Debug:                            _log.Debug(msg.Message, msg.Exception);                            break;                        case QueueLogLevel.Info:                            _log.Info(msg.Message, msg.Exception);                            break;                        case QueueLogLevel.Error:                            _log.Error(msg.Message, msg.Exception);                            break;                        case QueueLogLevel.Warn:                            _log.Warn(msg.Message, msg.Exception);                            break;                        case QueueLogLevel.Fatal:                            _log.Fatal(msg.Message, msg.Exception);                            break;                    }                }                // 重新設定訊號                _mre.Reset();            }        }        /// <summary>        /// 寫日誌        /// </summary>        /// <param name="message">日誌文本</param>        /// <param name="level">等級</param>        /// <param name="ex">Exception</param>        public void EnqueueMessage(string message, QueueLogLevel level, Exception ex = null)        {            if ((level == QueueLogLevel.Debug && _log.IsDebugEnabled)             || (level == QueueLogLevel.Error && _log.IsErrorEnabled)             || (level == QueueLogLevel.Fatal && _log.IsFatalEnabled)             || (level == QueueLogLevel.Info && _log.IsInfoEnabled)             || (level == QueueLogLevel.Warn && _log.IsWarnEnabled))            {                _que.Enqueue(new QueueLogMessage                {                    // Message = "[" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss,fff") + "]\r\n" + message,                    Message = message,                    Level = level,                    Exception = ex                });                // 通知線程往磁碟中寫日誌                _mre.Set();            }        }        public static void Debug(string msg, Exception ex = null)        {            flashLog.EnqueueMessage(msg, QueueLogLevel.Debug, ex);        }        public static void Error(string msg, Exception ex = null)        {            flashLog.EnqueueMessage(msg, QueueLogLevel.Error, ex);        }        public static void Fatal(string msg, Exception ex = null)        {            flashLog.EnqueueMessage(msg, QueueLogLevel.Fatal, ex);        }        public static void Info(string msg, Exception ex = null)        {            flashLog.EnqueueMessage(msg, QueueLogLevel.Info, ex);        }        public static void Warn(string msg, Exception ex = null)        {            flashLog.EnqueueMessage(msg, QueueLogLevel.Warn, ex);        }    }    /// <summary>    /// 日誌等級    /// </summary>    public enum QueueLogLevel    {        Debug,        Info,        Error,        Warn,        Fatal    }    /// <summary>    /// 日誌內容    /// </summary>    public class QueueLogMessage    {        public string Message { get; set; }        public QueueLogLevel Level { get; set; }        public Exception Exception { get; set; }    }}

至於CSV格式有2中方法 實現,一是自訂PatternLayout類:

namespace log4net{    using Layout;    using System.IO;    using System.Text;    using Util;    using Core;    public class CSVPatternLayout : PatternLayout    {        public override void ActivateOptions()        {            AddConverter("newfield", typeof(CSVNewFiledConverter));            AddConverter("endrow", typeof(CSVEndRowConverter));            base.ActivateOptions();        }        public override void Format(TextWriter writer, LoggingEvent loggingEvent)        {            var csvWriter = new CSVTextWriter(writer);            csvWriter.WriteQuote();            base.Format(csvWriter, loggingEvent);        }    }    public class CSVTextWriter : TextWriter    {        private readonly TextWriter textWriter;        public CSVTextWriter(TextWriter txtWriter)        {            textWriter = txtWriter;        }        public override void Write(char value)        {            // base.Write(value);            textWriter.Write(value);            //if (value == '"')            //{            //}        }        public void WriteQuote()        {            textWriter.Write('"');        }        public override Encoding Encoding        {            get            {                return textWriter.Encoding;            }        }    }    public class CSVNewFiledConverter : PatternConverter    {        protected override void Convert(TextWriter writer, object state)        {            var csvWriter = writer as CSVTextWriter;            csvWriter?.WriteQuote();            writer.Write(",");            csvWriter?.WriteQuote();        }    }    public class CSVEndRowConverter : PatternConverter    {        protected override void Convert(TextWriter writer, object state)        {            var csvWriter = writer as CSVTextWriter;            csvWriter?.WriteQuote();            writer.WriteLine();        }    }}

設定檔中需要加上逗號

<layout type="log4net.CSVPatternLayout,ConsoleApp"><header value="Time,Thread,Level,Logger,Message,Exception&#13;&#10;" /><conversionPattern value="%date{yyyy-MM-dd HH:mm:ss}%newfield%thread%newfield%level%newfield%logger%newfield%message%newfield%exception%endrow" /></layout>

這裡&#13;&#10;是\r\n,%newfield是一個逗號,%endrow是逗號+換行

看到這裡其實我們可以自己拼接CSV的內容,也就是說只要有,\r\n就可以了

<layout type="log4net.Layout.PatternLayout"><header value="Time,Message,Type,&#13;&#10;" /><param name="ConversionPattern" value="&quot;%date{yyyy-MM-dd HH:mm:ss}&quot;,&quot;%message%&quot;&#13;&#10;"/></layout>

調用code:

StringBuilder sb = new StringBuilder();sb.Append("test");sb.Append("\",\"");sb.Append("debug");QueueLogger.Debug(sb.ToString());

寫入的資訊是test","debug,在加上ConversionPattern裡面的配置就是"test","debug".整個配置如下:

<?xml version="1.0" encoding="utf-8"?><configuration>  <configSections>    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />  </configSections>  <log4net xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">    <appender name="InfoLog" type="log4net.Appender.RollingFileAppender">      <param name="File" value="Log\Info\Info" />      <param name="AppendToFile" value="True" />      <appendToFile value="true" />      <maxSizeRollBackups value="100" />      <maximumFileSize value="10MB" />      <staticLogFileName value="false" />      <rollingStyle value="Composite" />      <datePattern value="yyyyMMdd'.csv'" />      <lockingModel type="log4net.Appender.FileAppender+MinimalLock" />      <layout type="log4net.CSVPatternLayout,ConsoleApp">        <header value="Time,Thread,Level,Logger,Message,Exception&#13;&#10;" />        <conversionPattern  value="%date{yyyy-MM-dd HH:mm:ss}%newfield%thread%newfield%level%newfield%logger%newfield%message%newfield%exception%endrow" />      </layout>      <filter type="log4net.Filter.LevelRangeFilter">        <param name="LevelMin" value="INFO" />        <param name="LevelMax" value="INFO" />      </filter>    </appender>    <appender name="DebugLog" type="log4net.Appender.RollingFileAppender">      <file type="log4net.Util.PatternString" value="Log\Debug\Debug" />      <appendToFile value="true" />      <maxSizeRollBackups value="100" />      <maximumFileSize value="10MB" />      <staticLogFileName value="false" />      <rollingStyle value="Composite" />      <datePattern value="yyyyMMdd'.csv'" />      <lockingModel type="log4net.Appender.FileAppender+ExclusiveLock" />      <layout type="log4net.Layout.PatternLayout">        <header value="Time,Message,Type,&#13;&#10;" />        <param name="ConversionPattern" value="&quot;%date{yyyy-MM-dd HH:mm:ss}&quot;,&quot;%message%&quot;&#13;&#10;"/>      </layout>      <filter type="log4net.Filter.LevelRangeFilter">        <param name="LevelMin" value="DEBUG" />        <param name="LevelMax" value="DEBUG" />      </filter>    </appender>    <appender name="ColoredConsoleAppender" type="log4net.Appender.ColoredConsoleAppender">      <mapping>        <level value="ERROR" />        <foreColor value="Red" />      </mapping>      <mapping>        <level value="INFO" />        <foreColor value="Green" />      </mapping>      <layout type="log4net.Layout.PatternLayout">        <conversionPattern value="# %date{HH:mm:ss} [%thread] %-5level %logger #%newline%message%newline" />      </layout>      <filter type="log4net.Filter.LevelRangeFilter">        <param name="LevelMin" value="DEBUG" />        <param name="LevelMax" value="FATAL" />      </filter>    </appender>    <root>      <!-- OFF < FATAL < ERROR < WARN < INFO < DEBUG < ALL -->      <level value="ALL" />      <appender-ref ref="InfoLog" />      <appender-ref ref="DebugLog" />      <!--         <appender-ref ref="ColoredConsoleAppender" />      -->    </root>  </log4net></configuration>

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.