因公司ELK監控分析日誌的需要,需要區分進程運行狀態日誌以及錯誤日誌,以便能夠根據日誌級別(level)進行不同策略的預警,而現有的Nlog、Log4Net都沒有Process這樣的level,故針對這兩個日誌框架做了一些擴展,實現了自定義PROCESS LEVEL,因代碼不多,故直接貼代碼,有疑問 ...
因公司ELK監控分析日誌的需要,需要區分進程運行狀態日誌以及錯誤日誌,以便能夠根據日誌級別(level)進行不同策略的預警,而現有的Nlog、Log4Net都沒有Process這樣的level,故針對這兩個日誌框架做了一些擴展,實現了自定義PROCESS LEVEL,因代碼不多,故直接貼代碼,有疑問的或好的建議可以下方評論留言交流,謝謝!
NlogExtend.cs:(Nlog的擴展)文件代碼內容如下:
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了
用法如下:
config文件:(用levelx取代原來的level即可)
<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配置如下:
<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>
代碼使用如下:
public static ILog Logger = LogManager.GetLogger("KyAutoTimingExecSystem"); Logger.Process(msg);
好了,就介紹到這裡,沒有什麼高大上的技術分享,只是一點對框架擴展的總結,舉一反三,可以擴展其它方面。