使用log4net记录本地日志及自定义扩展输出
简单介绍一下log4net的主要组成,详细说明可以参考https://blog.csdn.net/binnygoal/article/details/79557746
1.Logger
Logger是直接和应用程序交互的组件,是负责日志的记录者,可以同时存在多个Logger,然后由它引用的Appender记录到指定的媒介,并由Layout控制输出格式。
2.Appender
Appender用来定义日志的输出方式,即提供记录介质,如记录到数据库,记录到本地文件,输出到控制台等等。
3.Layout
Layout用于控制Appender的输出格式(格式化记录数据),可以是线性的也可以是XML。
4.Filter
Filter负责记录筛选,可以过滤掉Appender输出的内容。
总体流程:Logger发出记录信息,Appender接到信息,根据内部的Layout配置对记录信息格式化,然后根据Filter对记录信息进行筛选,最后将其记录到指定介质中。
简单配置log4net
第一步:在项目中添加log4net.dll的引用;
第二步:配置log4net.config文件;
<?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" /> </configSections> <log4net> <logger name="ErrorLogger"> <level value="WARN" /> <appender-ref ref="ErrorAppender" /> </logger> <logger name="DebugLogger"> <level value="DEBUG" /> <appender-ref ref="DebugAppender" /> </logger> <logger name="InfoLogger"> <level value="INFO" /> <appender-ref ref="InfoAppender" /> </logger> <appender name="InfoAppender" type="log4net.Appender.RollingFileAppender"> <!--日志文件路径,文件夹不存在则新建 --> <file value="D://THS//logs//" /> <!--是否追加到文件--> <appendToFile value="true" /> <!--记录日志写入文件时,不锁定文本文件,防止多线程时不能写Log,官方说线程非安全--> <lockingModel type="log4net.Appender.FileAppender+MinimalLock" /> <!--最多产生的日志文件数,超过则只保留最新的n+1个--> <maxSizeRollBackups value="1" /> <!--每个文件的大小,只在混合方式与文件大小方式下使用,超出大小后在所有文件名后自动增加正整数重新命名,数字最大的最早写入,没带数字最晚写入--> <maximumFileSize value="256MB" /> <!--是否只写到一个文件中--> <staticLogFileName value="false"/> <!--按照何种方式产生多个日志文件(日期[Date],文件大小[Size],混合[Composite])--> <rollingStyle value="Composite" /> <!--是否保存日志文件的扩展名--> <preserveLogFileNameExtension value="true" /> <!--按日期产生文件夹,文件名固定。注意" 的位置--> <datePattern value="yyyy-MM-dd/"SysInfo.log"" /> <!--记录的格式。一般用log4net.Layout.PatternLayout布局--> <!--此处用继承了log4net.Layout.PatternLayout的自定义布局,LogTest.Log为命名空间。%Property{LineLocation}、%Property{Method}等是自定义的输出--> <layout type="LogTest.Log.Z_PatternLayout"> <conversionPattern value="日志时间:%Property{Time} %n记录方法:[%thread]%Property{Method} Line:%Property{LineLocation} %n日志信息:%Property{Message} %n%n" /> </layout> <!--过滤设置,LevelRangeFilter为使用的过滤器。--> <filter type="log4net.Filter.LevelRangeFilter"> <param name="LevelMin" value="INFO" /> <param name="LevelMax" value="INFO" /> </filter> </appender> <appender name="DebugAppender" type="log4net.Appender.RollingFileAppender"> <file value="D://THS//logs//" /> <appendToFile value="true" /> <lockingModel type="log4net.Appender.FileAppender+MinimalLock" /> <maxSizeRollBackups value="30" /> <staticLogFileName value="false"/> <rollingStyle value="Date" /> <preserveLogFileNameExtension value="true" /> <datePattern value="yyyy-MM-dd/"SysDebug.log"" /> <layout type="LogTest.Log.Z_PatternLayout"> <conversionPattern value="日志时间:%Property{Time} %n记录方法:[%thread]%Property{Method} Line:%Property{LineLocation} %n日志信息:%Property{Message} %n%n" /> </layout> <filter type="log4net.Filter.LevelRangeFilter"> <param name="LevelMin" value="DEBUG" /> <param name="LevelMax" value="DEBUG" /> </filter> </appender> <appender name="ErrorAppender" type="log4net.Appender.RollingFileAppender"> <file value="D://THS//logs//" /> <appendToFile value="true" /> <lockingModel type="log4net.Appender.FileAppender+MinimalLock" /> <maxSizeRollBackups value="30" /> <staticLogFileName value="false"/> <rollingStyle value="Date" /> <preserveLogFileNameExtension value="true" /> <datePattern value="yyyy-MM-dd/"SysError.log"" /> <layout type="LogTest.Log.Z_PatternLayout"> <conversionPattern value="日志时间:%Property{Time} %n日志级别:%-5level %n记录方法:[%thread]%Property{Method} Line:%Property{LineLocation} %n日志信息:%Property{Message} %n异常信息:%exception %n%n" /> </layout> <filter type="log4net.Filter.LevelRangeFilter"> <param name="LevelMin" value="WARN" /> <param name="LevelMax" value="FATAL" /> </filter> </appender> </log4net> </configuration>
注意,注意,注意:修改log4net.config文件属性“复制到输出目录”为“始终复制”,否则不会生成日志文件夹;
第三步:如果是使用自动以配置文件,则在AssemblyInfo.cs中添加关联配置文件代码
// 使用配置文件log4net.config,监视改变 [assembly: log4net.Config.XmlConfigurator(ConfigFile = "log4net.config", Watch = true)]
如果是在默认App.config文件中配置log4net,则可以直接在Program.cs中的Main方法中添加:
log4net.Config.XmlConfigurator.Configure();
第四步:自定义扩展输出,通过继承log4net.Layout.PatternLayout类,使用log4net.Core.LoggingEvent类的方法得到了要输出的LogEntity类的名称,然后通过反射得到各个属性的值,使用PatternLayout类AddConverter方法传入得到的值。注意Appender中的Layout type要引用用到的类的命名空间以及类名。
1.创建LogEntity实体类(自定义输出属性)
namespace LogTest.Log { public class Z_LogEntity { /// <summary> /// 引发日志事件的行号 /// </summary> public string LineLocation { get; set; } /// <summary> /// 应用程序提供给日志事件的消息 /// </summary> public string Message { get; set; } /// <summary> /// 引发日志事件的方法(包括类名和方法名) /// </summary> public string Method { get; set; } /// <summary> /// 引发日志事件的时间(格式化) /// </summary> public string Time { get; set; } } }
2.继承PatternLayoutConvert重写转换器
using log4net.Core; using log4net.Layout.Pattern; using System.IO; using System.Reflection; namespace LogTest.Log { public class Z_PatternConvert : PatternLayoutConverter { protected override void Convert(TextWriter writer, LoggingEvent loggingEvent) { if (Option != null) // 写入指定键的值 WriteObject(writer, loggingEvent.Repository, LookupProperty(Option, loggingEvent)); else // 写入所有关键值对 WriteDictionary(writer, loggingEvent.Repository, loggingEvent.GetProperties()); } /// <summary> /// 通过反射获取传入的日志对象的某个属性 /// </summary> private object LookupProperty(string property, LoggingEvent loggingEvent) { object propertyValue = string.Empty; PropertyInfo propertyInfo = loggingEvent.MessageObject.GetType().GetProperty(property); if (propertyInfo != null) propertyValue = propertyInfo.GetValue(loggingEvent.MessageObject, null); return propertyValue; } } }
3.继承PatternLayout,添加转换器到实例中
using log4net.Layout; namespace LogTest.Log { public class Z_PatternLayout : PatternLayout { public Z_PatternLayout() { this.AddConverter("Property", typeof(Z_PatternConvert)); } } }
第五步:编写LogHelper类
using System; using System.Diagnostics; using System.Runtime.CompilerServices; namespace LogTest.Log { public class LogHelper { /// <summary> /// 静态只读实例化对象DebugLogger /// </summary> public static readonly log4net.ILog DebugLog = log4net.LogManager.GetLogger("DebugLogger"); /// <summary> /// 静态只读实例化对象ErrorLogger /// </summary> public static readonly log4net.ILog ErrorLog = log4net.LogManager.GetLogger("ErrorLogger"); /// <summary> /// 静态只读实例化对象InfoLogger /// </summary> public static readonly log4net.ILog InfoLog = log4net.LogManager.GetLogger("InfoLogger"); private static Z_LogEntity logEntity = new Z_LogEntity(); public static Z_LogEntity BuildLogEntity(string message, [CallerLineNumber] int lineNum = 0, [CallerMemberName] string method = "") { logEntity.Time = DateTime.Now.ToString("HH:mm:ss:fff"); logEntity.Message = message; logEntity.LineLocation = lineNum.ToString(); logEntity.Method = (new StackTrace()).GetFrame(1).GetMethod().ReflectedType.Name + "." + method; return logEntity; } public static void WriteInfo(Z_LogEntity logEntity) { try { if (InfoLog.IsInfoEnabled) { InfoLog.Info(logEntity); } } catch { } } public static void WriteDebug(Z_LogEntity logEntity) { try { if (DebugLog.IsDebugEnabled) { DebugLog.Debug(logEntity); } } catch { } } public static void WriteWarn(Z_LogEntity logEntity) { try { if (ErrorLog.IsWarnEnabled) { ErrorLog.Warn(logEntity); } } catch { } } public static void WriteWarn(Z_LogEntity logEntity, Exception ex) { try { if (ErrorLog.IsWarnEnabled) { ErrorLog.Warn(logEntity, ex); } } catch { } } public static void WriteError(Z_LogEntity logEntity) { try { if (ErrorLog.IsErrorEnabled) { ErrorLog.Error(logEntity); } } catch { } } public static void WriteError(Z_LogEntity logEntity, Exception ex) { try { if (ErrorLog.IsErrorEnabled) { ErrorLog.Error(logEntity, ex); } } catch { } } public static void WriteFatal(Z_LogEntity logEntity) { try { if (ErrorLog.IsFatalEnabled) { ErrorLog.Fatal(logEntity); } } catch { } } public static void WriteFatal(Z_LogEntity logEntity, Exception ex) { try { if (ErrorLog.IsFatalEnabled) { ErrorLog.Fatal(logEntity, ex); } } catch { } } } }
第六步:程序中使用
// 记录Info级别数据 LogHelper.WriteInfo(LogHelper.BuildLogEntity("程序运行记录写入!")); // 记录Debug级别数据 LogHelper.WriteDebug(LogHelper.BuildLogEntity("调试信息数据写入!")); // 记录Warn级别数据 LogHelper.WriteWarn(LogHelper.BuildLogEntity("警告数据写入!"),new Exception("对应的错误信息!")); // 记录Error级别数据 LogHelper.WriteError(LogHelper.BuildLogEntity("一般错误数据写入!"), new Exception("对应的错误信息!")); // 记录Fatal级别数据 LogHelper.WriteFatal(LogHelper.BuildLogEntity("致命错误数据写入!"), new Exception("对应的错误信息!"));
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构