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,这样文件夹名用来表示意义,文件名还可以按日期自动更新

 

posted @ 2025-02-14 09:37  ckrgd  阅读(367)  评论(0)    收藏  举报