c# log4net 日志的使用
参考:C# log4net的使用。输出的日志内容添加文件名和行号。_log4net 发布 输出的文件中行号-CSDN博客
1.NuGet增加log4net

2.增加log4net.config文件

log4net.config的内容如下,没有的功能可以搜一搜,设置 log4net.config的属性 复制到输出目录 设为始终复制
<?xml version="1.0" encoding="utf-8"?> <configuration> <configSections> <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/> </configSections> <!--(高) OFF > FATAL > ERROR > WARN > INFO > DEBUG > ALL (低)--> <log4net> <!--调试信息--> <logger name="InfoLog"> <level value="ALL" /> <appender-ref ref="InfoAppender" /> </logger> <!--错误信息--> <logger name="ErrorLog"> <level value="ALL" /> <appender-ref ref="ErrorAppender" /> </logger> <!--Info日志附加介质--> <appender name="InfoAppender" type="log4net.Appender.RollingFileAppender"> <!--日志路径--> <param name= "File" value= "Logs\"/> <!--是否是向文件中追加日志--> <param name= "AppendToFile" value= "true"/> <!--log保留天数--> <param name= "MaxSizeRollBackups" value= "100"/> <!--写到一个文件--> <staticLogFileName value="false"/> <!--单个文件大小。单位:KB|MB|GB--> <maximumFileSize value="200MB"/> <!--最多保留的文件数,设为"-1"则不限--> <maxSizeRollBackups value="-1"/> <!--日志文件名格式--> <param name= "DatePattern" value= "yyyyMM\\yyyyMMdd'_InfoLog.log'"/> <!--不以独占方式记录日志,仅在记录每个日志的最短时间内锁定,因为部署到服务器上遇到了文件被占用无法下载日志--> <lockingModel type="log4net.Appender.FileAppender+MinimalLock" /> <!--日志根据日期滚动--> <param name= "RollingStyle" value= "Date"/> <!--日志文本格式--> <layout type="log4net.Layout.PatternLayout"> <!--%d 时间,等价于 date--> <!--%t 线程--> <!--%-5p 日志级别--> <!--%C 出错类,等价于 class,可以使用:%class{1},如果给出了精度说明符, 则只会打印类名中最右边的组件的相应数量。默认情况下,类名以完全限定形式输出。--> <!--%L 出错行--> <!--%M 方法名,等价于 method--> <!--%m 日志信息,等价于 message--> <!--%n 换行--> <!--这种情况输出的类名方法名输出的是封装的类名<LogHelper>方法名每有意义--> <!--<param name="ConversionPattern" value="**********%d 线程[%t] 日志级别[%p] **********%n类名:%C 方法名:%M - 第 [%L] 行%n消息:%m%n%n" />--> <param name="ConversionPattern" value="**********%d 线程[%t] 日志级别[%p]**********%n消息:%m%n%n"/> </layout> </appender> <!--Error日志附加介质--> <appender name="ErrorAppender" type="log4net.Appender.RollingFileAppender"> <!--日志路径--> <param name= "File" value= "Logs\"/> <!--是否是向文件中追加日志--> <param name= "AppendToFile" value= "true"/> <!--log保留天数--> <param name= "MaxSizeRollBackups" value= "100"/> <!--日志文件名是否是固定不变的--> <param name= "StaticLogFileName" value= "false"/> <!--日志文件名格式--> <param name= "DatePattern" value= "yyyyMM\\yyyyMMdd'_ErrorLog.log'"/> <!--日志根据日期滚动--> <param name= "RollingStyle" value= "Date"/> <param name="MaxFileSize" value="1"/> <!--日志文本格式--> <layout type="log4net.Layout.PatternLayout"> <!--%d 时间,等价于 date--> <!--%t 线程--> <!--%-5p 日志级别--> <!--%C 出错类,等价于 class,可以使用:%class{1},如果给出了精度说明符, 则只会打印类名中最右边的组件的相应数量。默认情况下,类名以完全限定形式输出。--> <!--%L 出错行--> <!--%M 方法名,等价于 method--> <!--%m 日志信息,等价于 message--> <!--%n 换行--> <!--这种情况输出的类名方法名输出的是封装的类名<LogHelper>方法名每有意义--> <!--<param name="ConversionPattern" value="**********%d 线程[%t] 日志级别[%p] **********%n类名:%C 方法名:%M - 第 [%L] 行%n消息:%m%n%n" />--> <param name="ConversionPattern" value="**********%d 线程[%t] 日志级别[%p]**********%n消息:%m%n%n"/> </layout> </appender> </log4net> </configuration>
3.创建LogHelper.cs类
using System.Diagnostics; using System.IO; using log4net; [assembly: log4net.Config.XmlConfigurator(ConfigFile = "Log4net\\log4net.config", Watch = true)] namespace TestLog4net.Log4net { /// <summary> /// 日志助手类 /// </summary> internal class LogHelper { private static ILog InfoLog = LogManager.GetLogger("InfoLog"); private static ILog ErrorLog = LogManager.GetLogger("ErrorLog"); //Debug Info Warn Error方法 在日志里体现在日志级别上 /// <summary> /// 写日志 /// </summary> /// <param name="message">日志信息</param>
public static void WriteInfoLog(string message)
{
InfoLog.Info(AppendClassLine(message));
}
/// <summary> /// 写错误日志 /// </summary> /// <param name="message">日志信息</param> public static void WriteErrorLog(string message) { ErrorLog.Error(AppendClassLine(message)); } /// <summary> /// 给日志信息附加所在文件名和行 /// </summary> /// <param name="msg">日志信息</param> /// <returns>附加后的日志信息</returns> static string AppendClassLine(string msg) { string logStr = msg; try { //测试了一下这个new StackTrace(true) 10w次耗时1.3秒,性能可以接收啊 //这个原理是读pdb文件,也许大项目读这个文件会更耗时 StackTrace st = new StackTrace(true); StackFrame sf = st.GetFrame(2); logStr = $"{msg} [{Path.GetFileName(sf.GetFileName())}:{sf.GetFileLineNumber().ToString()}]"; } catch { logStr = $"{msg} [没找到文件名和所在行]"; } return logStr; } } }
这里的new StackTrace(true)性能测试代码如下
using System; using System.Diagnostics; namespace ConsoleApp1 { internal class Program { static void Main(string[] args) { const int iterations = 100000; // 测试 new StackTrace(false) 的性能 var stopwatch1 = Stopwatch.StartNew(); for (int i = 0; i < iterations; i++) { var stackTrace = new StackTrace(false); } stopwatch1.Stop(); Console.WriteLine($"new StackTrace(false) 耗时: {stopwatch1.ElapsedMilliseconds} 毫秒"); // 测试 new StackTrace(true) 的性能 var stopwatch2 = Stopwatch.StartNew(); for (int i = 0; i < iterations; i++) { var stackTrace = new StackTrace(true); } stopwatch2.Stop(); Console.WriteLine($"new StackTrace(true) 耗时: {stopwatch2.ElapsedMilliseconds} 毫秒"); } } }
4.日志调用
using TestLog4net.Log4net; namespace TestLog4net { internal class Program { static void Main(string[] args) { LogHelper.WriteInfoLog("info日志"); LogHelper.WriteErrorLog("error日志"); } } }
5.日志效果

如果要动态指定日志文件的名称怎么办呢,应该代码配置appender 如下
using System; using System.Diagnostics; using System.IO; using System.Windows.Forms; using log4net; using log4net.Appender; using log4net.Core; using log4net.Layout; using log4net.Repository.Hierarchy; namespace SKDBurninSystem.log4net { internal class LogHelper { private readonly ILog InfoLog; public LogHelper(string InfoFileName) { InfoLog = GetLoggerByName(InfoFileName); } /// <summary> /// 根据日志文件名获取日志记录器 /// </summary> /// <param name="LogName"></param> /// <returns></returns> private ILog GetLoggerByName(string LogName) { try { //检查日志记录器是否存在 if (LogManager.Exists(LogName) == null) { // 定义日志输出的模式布局 PatternLayout patternLayout = new PatternLayout(); // 设置日志输出的转换模式,这里定义为日期 - 消息,每条日志记录后换行 patternLayout.ConversionPattern = "**********%d 线程[%t] 日志级别[%p]**********%n消息:%m%n%n"; // 激活模式布局的配置选项,使配置生效 patternLayout.ActivateOptions(); // 配置 RollingFileAppender 对象,用于将日志滚动写入文件 RollingFileAppender appender = new RollingFileAppender(); //文件名 appender.Name = LogName; //日志追加写 appender.AppendToFile = true; //日志加上路径,文件名也可以加路径 appender.File = $"Logs\\{LogName}\\{DateTime.Now:yyyyMMdd}.log"; appender.DatePattern = "yyyyMMdd.log"; //日志文件名在滚动时保持静态 appender.StaticLogFileName = true; //保留日志文件的扩展名 appender.PreserveLogFileNameExtension = false; // 设置文件锁定模式为最小化锁定,减少文件锁定时间,提高并发性能 appender.LockingModel = new FileAppender.MinimalLock(); // 为 appender 设置日志布局,这里使用传入的 patternLayout appender.Layout = patternLayout; // 设置日志文件备份的最大数量,这里最多保留 5 个备份文件 appender.MaxSizeRollBackups = 5; // 设置单个日志文件的最大大小为 200MB,达到这个大小后会进行滚动操作 appender.MaximumFileSize = "200MB"; // 设置滚动模式为复合模式,结合多种滚动条件进行滚动 appender.RollingStyle = RollingFileAppender.RollingMode.Date; // 激活 appender 的配置选项,使配置生效 appender.ActivateOptions(); // 获取 LogManager 的仓库,并将其转换为 Hierarchy 类型 Hierarchy hierarchy = (Hierarchy)LogManager.GetRepository(); // 获取指定名称的日志记录器,使用指定的 LoggerFactory var loger = hierarchy.GetLogger(LogName, hierarchy.LoggerFactory); //!!! 此处写法是重点,不容更改 // 设置日志记录器的层次结构 loger.Hierarchy = hierarchy; // 为日志记录器添加 appender,使其使用上面配置的文件追加器 loger.AddAppender(appender); // 设置日志记录器的日志级别为所有级别,即记录所有类型的日志 loger.Level = Level.All; // 基本配置器进行配置,使日志系统生效 //BasicConfigurator.Configure();//!!! 此处写法是重点,不容更改 hierarchy.Configured = true; } // 获取指定名称的日志记录器 var logger = LogManager.GetLogger(LogName); return logger; } catch (Exception ex) { MessageBox.Show($"{LogName}log4net初始化时失败" + ex.Message); return null; } } /// <summary> /// 给日志信息附加所在文件名和行 /// </summary> /// <param name="msg">日志信息</param> /// <returns>附加后的日志信息</returns> private string AppendClassLine(string msg) { string logStr = msg; try { //测试了一下这个new StackTrace(true) 10w次耗时1.3秒,性能可以接收啊 //这个原理是读pdb文件,也许大项目读这个文件会更耗时,要保证*.pdb文件在时才能读到 StackTrace st = new StackTrace(true); StackFrame sf = st.GetFrame(2); logStr = $"{msg} [{Path.GetFileName(sf.GetFileName())}:{sf.GetFileLineNumber().ToString()}]"; } catch { logStr = $"{msg} [没找到文件名和所在行]"; } return logStr; } public void WriteInfoLog(string message) { InfoLog.Info(AppendClassLine(message)); } public void WriteErrorLog(string message) { InfoLog.Error(AppendClassLine(message)); } } }
调用如下
internal class Program { static void Main(string[] args) { Test1 t1 = new Test1(); } } public class Test1 { private static readonly LogHelper _log = new LogHelper(Path.Combine("TTTT", "Info_2025-04-16")); public Test1() { new Thread(Execute) { Name = "TTTT" }.Start(); } private void Execute() { for (int i = 0; i < 3; i++) { _log.WriteInfoLog("Test1 info"); Thread.Sleep(1000); } } }
生成的日志如下

这里日志的内容是上次测试的内容,这次是修改文件名的问题,日志内容就用了上次的有出入,请自行测试

有可能会生成一些奇怪名字的日志文件,但内容却是空的
有可能是log4net.config这个文件影响了,用代码就用不到这个文件了,但测试代码是从有log4net.config文件改成代码形式的,删除这个文件试试,从项目里改那个”复制到输出目录“
这个后缀名与按天更新有关,我就是想要自定义log名字又要按日期更新,先这样吧,不要后缀名了
这里用了一个方法是名称用作文件夹名称,文件名还是YYYYMMDD.log,这样文件夹名用来表示意义,文件名还可以按日期自动更新

浙公网安备 33010602011771号