Nlog、Log4Net 的一個小擴展(增長自定義LEVEL)

因公司ELK監控分析日誌的須要,須要區分進程運行狀態日誌以及錯誤日誌,以便可以根據日誌級別(level)進行不一樣策略的預警,而現有的Nlog、Log4Net都沒有Process這樣的level,故針對這兩個日誌框架作了一些擴展,實現了自定義PROCESS LEVEL,因代碼很少,故直接貼代碼,有疑問的或好的建議能夠下方評論留言交流,謝謝!html

NlogExtend.cs:(Nlog的擴展)文件代碼內容以下:app

public static class NlogExtend
    {
        public static void Process(this ILogger logger, string message = "RUNNING")
        {
            var logEventInfo = new LogEventInfo(LogLevel.Trace, logger.Name, message);
            logEventInfo.Properties["custLevel"] = Tuple.Create(9, "Process");
            logger.Log(logEventInfo);
        }
    }

    [LayoutRenderer("levelx")]
    public class LevelExLayoutRenderer : LevelLayoutRenderer
    {

        protected override void Append(StringBuilder builder, LogEventInfo logEvent)
        {
            if (logEvent.Level == LogLevel.Trace && logEvent.Properties.ContainsKey("custLevel"))
            {
                var custLevel = logEvent.Properties["custLevel"] as Tuple<int, string>;
                if (custLevel == null)
                {
                    throw new InvalidCastException("Invalid Cast Tuple<int, string>");
                }

                switch (this.Format)
                {
                    case LevelFormat.Name:
                        builder.Append(custLevel.Item2);
                        break;
                    case LevelFormat.FirstCharacter:
                        builder.Append(custLevel.Item2[0]);
                        break;
                    case LevelFormat.Ordinal:
                        builder.Append(custLevel.Item1);
                        break;
                }
            }
            else
            {
                base.Append(builder, logEvent);
            }
        }
    }

 因Nlog的LogLevel不容許直接經過繼承或New 構造函數來建立一個新的Level,故只能採起把自定義的level寫入到LogEventInfo的擴展屬性中,而後再自定義一個繼承自LevelLayoutRenderer的佈局生成器類,就OK了框架

用法以下:async

config文件:(用levelx取代原來的level便可)ide

<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <targets async="true">
      <target name="file" xsi:type="File" fileName="${basedir}/Logs/${shortdate}.log" layout="[${date:format=yyyy-MM-dd HH\:mm\:ss.fff}]  ${levelx:uppercase=true}  ${processname}  Thread-${threadid}  ${processname}  ${message}  Stack:${stacktrace}" archiveFileName="${basedir}/Logs/Archives/log.{#}.log" archiveEvery="Day" archiveNumbering="DateAndSequence" archiveAboveSize="10485760" archiveDateFormat="yyyyMMdd" maxArchiveFiles="30" concurrentWrites="true" keepFileOpen="false" />
    </targets>
    <rules>
      <logger name="*" minlevel="Trace" writeTo="file" />
    </rules>
  </nlog>代

代碼中輸出PROCESS日誌:函數

/// <summary>
    /// 基於NLOG框架的日誌工具類
    /// </summary>
    public static class LogUtil
    {
        private readonly static ILogger logger = null;

        static LogUtil()
        {
            NLog.Config.ConfigurationItemFactory.Default.LayoutRenderers.RegisterDefinition("levelx", typeof(WMSNWPP.LevelExLayoutRenderer));//註冊自定義的佈局生成器,這樣config文件中的levelx才能生效
            logger = LogManager.GetCurrentClassLogger();
        }

        public static void Info(string msg)
        {
            try
            {
                logger.Info(msg);
            }
            catch
            { }
        }

        public static void Error(Exception ex)
        {
            try
            {
                logger.Error(ex);
            }
            catch
            { }
        }


        public static void Error(string msg)
        {
            try
            {
                Log(LogLevel.Error, msg);
            }
            catch
            { }
        }

        public static void Warn(string msg)
        {
            try
            {
                logger.Warn(msg);
            }
            catch
            { }
        }

        public static void Log(LogLevel level, string msg)
        {
            try
            {
                logger.Log(level, msg);
            }
            catch
            { }
        }

        public static void Process(string message = "RUNNING")
        {
            try
            {
                logger.Process(message);
            }
            catch
            { }
        }

    }




//調用寫Process進程日誌
LogUtil.Process();

 說完了Nlog,再來講一下Log4Net的擴展實現。工具

Log4NetExtend.cs:(Nlog的擴展)文件代碼內容以下:佈局

/// <summary>
    /// Log4Net擴展(擴展一個Process的LOG LEVEL,以便定時輸出進程運行日誌)
    /// Author:Zuowenjun
    /// Date:2018-4-25
    /// </summary>
    public static class Log4NetExtend
    {
        public static readonly log4net.Core.Level ProcessLevel = new log4net.Core.Level(10001, "PROCESS");

        private static void AddProcessLevel(log4net.ILog log)
        {
            if (!log.Logger.Repository.LevelMap.AllLevels.Contains(ProcessLevel))
            {
                log.Logger.Repository.LevelMap.Add(ProcessLevel);
            }
        }

        public static void Process(this log4net.ILog log, string message)
        {
            AddProcessLevel(log);
            log.Logger.Log(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType,
                ProcessLevel, message, null);
        }

        public static void ProcessFormat(this log4net.ILog log, string message, params object[] args)
        {
            AddProcessLevel(log);
            string formattedMessage = string.Format(message, args);
            log.Logger.Log(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType,
                ProcessLevel, formattedMessage, null);
        }
    }

    /// <summary>
    /// 輸出當前進程名轉換器
    /// </summary>
    public class ProcessPatternConvert : PatternLayoutConverter
    {
        private readonly static string processName = System.Diagnostics.Process.GetCurrentProcess().ProcessName;
        protected override void Convert(System.IO.TextWriter writer, log4net.Core.LoggingEvent loggingEvent)
        {
            writer.Write(processName);//輸出當前進程名
        }
    }

    public class CustomPatternLayout : log4net.Layout.PatternLayout
    {
        public CustomPatternLayout()
        {
            this.AddConverter("proc", typeof(ProcessPatternConvert));
        }
    }

 log4net相對於Nlog好一點,能夠經過New Level的構造函數自定義一個新的level實例,但也很差的地方,就是沒有默認的顯示進程名字,故須要自定義ProcessPatternConvert、CustomPatternLayout,經過CustomPatternLayout的構造函數把ProcessPatternConvert佈局轉換器類添加到佈局轉換器集合,這樣config中就能夠配置proc,以便輸出進程名,config配置以下:ui

<log4net>
    <appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
      <!--日誌路徑-->
      <param name="File" value="Logs\" />
      <!--是不是向文件中追加日誌-->
      <param name="AppendToFile" value="true" />
      <!--log保留天數-->
      <param name="MaxSizeRollBackups" value="30" />
      <!--日誌文件名是不是固定不變的-->
      <param name="StaticLogFileName" value="false" />
      <!--日誌文件名格式爲:2008-08-31.log-->
      <param name="DatePattern" value="yyyyMMdd".log"" />
      <!--當日志文件達到10MB,則產生新的日誌文件-->
      <maximumFileSize value="10MB"/>
      <!--日誌根據日期\文件大小滾動-->
      <param name="RollingStyle" value="Composite" />
      <!--最小鎖定模型以容許多個進程能夠寫入同一個文件-->
      <param name="lockingModel" type="log4net.Appender.FileAppender+MinimalLock" />
      <layout type="KyAutoTimingExecSystem.CommonLib.CustomPatternLayout">
        <param name="Header" value="[Begin]
" />
        <param name="Footer" value="[End]
" />
        <param name="ConversionPattern" value="[%d]  %5p  %proc Thread-%t  %m%n  Stack:%stacktrace{10}%n" />
      </layout>
    </appender>
    <root>
      <level value="ALL" />
      <appender-ref ref="RollingLogFileAppender" />
    </root>
  </log4net>

 代碼使用以下:this

public static ILog Logger = LogManager.GetLogger("KyAutoTimingExecSystem");
Logger.Process(msg);

 好了,就介紹到這裏,沒有什麼高大上的技術分享,只是一點對框架擴展的總結,觸類旁通,能夠擴展其它方面。  

相關文章
相關標籤/搜索