因公司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; if (custLevel == null) { throw new InvalidCastException("Invalid Cast Tuple "); } 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即可)
代 ${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" />
代码中输出PROCESS日志:
////// 基于NLOG框架的日志工具类 /// 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的扩展)文件代码内容如下:
////// Log4Net扩展(扩展一个Process的LOG LEVEL,以便定时输出进程运行日志) /// Author:Zuowenjun /// Date:2018-4-25 /// 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); } } ////// 输出当前进程名转换器 /// 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配置如下:
代码使用如下:
public static ILog Logger = LogManager.GetLogger("KyAutoTimingExecSystem"); Logger.Process(msg);
好了,就介绍到这里,没有什么高大上的技术分享,只是一点对框架扩展的总结,举一反三,可以扩展其它方面。