Log4Net配置详解及输出自定义消息类示例

1.简单使用实例

1.1 添加log4net.dll的引用。

  在NuGet程序包中搜索log4net并添加,此次我所用版本为2.0.17。如下图:

image

1.2 添加配置文件

  右键项目,添加新建项,搜索选择应用程序配置文件,命名为log4net.config,步骤如下图:

image

1.2.1 log4net.config简单配置示例

  下面是一个简单的配置示例,详细节点及相应的说明详见 2. 配置文件节点详解

点击查看log4net.config配置
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<log4net>
	<logger name="DefaultLog">
		<!--control log level: ALL|DEBUG|INFO|WARN|ERROR|FATAL|OFF-->
		<!--如果没有定义LEVEL的值,则缺省为DEBUG-->
		<level value="ALL" />
		<appender-ref ref="FileAppenderDefault"></appender-ref>
	</logger>
	
	<!-- appender 定义日志输出方式   将日志以回滚文件的形式写到文件中。-->
	<appender name="FileAppenderDefault" type="log4net.Appender.RollingFileAppender">
		<!--绝对路径-->
		<!--<file value="D:\KangarooLog.txt"></file>-->
		<!--日志输出到exe程序这个相对目录下-->
		<file value="../../Log/DefalutLog" />
		<!--相对路径,在项目的根目录下-->
		<!--以最后一个路径为准,所以上面的绝对路径下不会写日志-->
		<!--<file value="./Log/Kangaroo.txt"></file>-->
		<!--防止多线程时不能写Log,官方说线程非安全-->
		<!--实际使用时,本地测试正常,部署后没有不能写日志的情况-->
		<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
		<!--追加日志内容,true后续输出的日志会追加到之前的日志文件-->
		<appendToFile value="true" />
		<!--可以为:Once|Size|Date|Composite-->
		<!--Composite为Size和Date的组合-->
		<rollingStyle value="Composite" />
		<!--日志最大个数,都是最新的-->
		<!--rollingStyle节点为Date时,该节点不起作用-->
		<!--rollingStyle节点为Size时,只能有value个日志-->
		<!--rollingStyle节点为Composite时,每天有value个日志-->
		<maxSizeRollBackups value="10" />
		<!--当备份文件时,为文件名加的后缀-->
		<!--后缀为*.txt时,例:AX.txt_2008-07-24.PxP  应该是程序上的一个bug-->
		<!--后缀为*.TXT时,例:AX.txt_2008-07-25.TXT-->
		<datePattern value="_yyyy-MM-dd'.log'" />
		<!--每个文件的大小。只在混合方式与文件大小方式下使用。超出大小后在所有文件名后自动增加正整数重新命名,数字最大的最早写入。可用的单位:KB|MB|GB。不要使用小数,否则会一直写入当前日志-->
		<maximumFileSize value="10MB" />
		<!--置为true,当前最新日志文件名永远为file节中的名字-->
		<staticLogFileName value="false" />
		<!--输出级别在INFO和ERROR之间的日志-->
		<!--<filter type="log4net.Filter.LevelRangeFilter">
			<param name="LevelMin" value="INFO" />
			<param name="LevelMax" value="ERROR" />
		</filter>-->
		<!--必须结合起来用,第一个只过滤出WARN,第二个拒绝其它其它日志输出-->
		<filter type="log4net.Filter.LevelMatchFilter">
			<param name="LevelToMatch" value="WARN" />
		</filter>
		<filter type="log4net.Filter.DenyAllFilter" />
		<layout type="log4net.Layout.PatternLayout">
			<conversionPattern value="%n==========
			%n【日志级别】%-5level
			%n【记录时间】%date
			%n【执行时间】[%r]毫秒
			%n【执行Log分类的名称】%logger
			%n【传入信息内容】%message
			%n=========="/>
		</layout>
	</appender>
</log4net>
</configuration>
<!--==================================layout节点的配置说明==================================-->
<!--                                                                        Made By YSL      -->
<!--        %m(message):输出的日志消息,如ILog.Debug(…)输出的一条消息                          -->
<!--        %n(new line):换行                                                                   -->
<!--        %d(datetime):输出当前语句运行的时刻                                                 -->
<!--        %r(run time):输出程序从运行到执行到当前语句时消耗的毫秒数                           -->
<!--        %t(thread id):当前语句所在的线程ID                                                  -->
<!--        %p(priority): 日志的当前优先级别,即DEBUG、INFO、WARN…等                           -->
<!--        %c(class):当前日志对象的名称,例如:                                                -->
<!--               模式字符串为:%-10c -%m%n                                                    -->
<!--               代码为:                                                                     -->
<!--        ILog log=LogManager.GetLogger(“Exam.Log”);                                        -->
<!--        log.Debug(“Hello”);                                                               -->
<!--            则输出为下面的形式:                                                            -->
<!--        Exam.Log       - Hello                                                              -->
<!--        %L:输出语句所在的行号                                                              -->
<!--        %F:输出语句所在的文件名                                                            -->
<!--        %-数字:表示该项的最小长度,如果不够,则用空格填充                                  -->

1.2.2 设置log4net.config配置文件属性

  点击log4net.config,将其文件属性设为始终复制,如下图:

image

1.3 在项目中引入该配置文件

  这里有两种方式引入配置文件。

1.3.1 在项目的 AssemblyInfo.cs 中引入配置文件

  首先在项目中新建一个Config文件夹,将之前创建的log4net.config文件放入其中,随后在 AssemblyInfo.cs 中添加如下语句:

image

1.3.2 在项目运行时动态引入配置文件

  使用固定语句引入配置文件,如下所示,其中,configFilePath 为配置文件的绝对路径。

log4net.Config.XmlConfigurator.ConfigureAndWatch(new System.IO.FileInfo(configFilePath));

1.4 创建帮助类使用日志进行记录

  我们首先创建名为 Log4Helper 的类,并使用固定的log4net.LogManager.GetLogger()语句实例化对应的Log对象,然后调用其对应的方法即可写入日志。示例代码如下:

点击查看Log4Helper代码
 public class Log4Helper
 {
     private static readonly log4net.ILog logDefault = log4net.LogManager.GetLogger("DefaultLog");

     /// <summary>
     /// 测试默认配置信息输出(输出范围做了限制)
     /// </summary>
     public static void TestDefaultLog()
     {
         logDefault.Debug("这是条调试信息");
         logDefault.Info("这是条提示信息");
         logDefault.Warn("这是条警告信息");
         logDefault.Error("这是条错误信息");
         logDefault.Fatal("这是条致命错误信息");
     }

     /// <summary>
     /// Log4Net初始化(可读取自定义配置)
     /// </summary>
     /// <param name="configFilePath"></param>
     public static void Log4NetInit(string configFilePath)
     {
         log4net.Config.XmlConfigurator.ConfigureAndWatch(new System.IO.FileInfo(configFilePath));
     }
     /// <summary>
     /// 返回指定名称的日志对象
     /// </summary>
     public static log4net.ILog Log(string appenderName)
     {
         return log4net.LogManager.GetLogger(appenderName);
     }
 }

1.5 在主程序中引用

  最后一步就是在主程序中引用 Log4Helper 中的日志记录方法,如下所示:

private void Application_Startup(object sender, StartupEventArgs e)
 {
     Log4Helper.TestDefaultLog();
 }

  根据上面 log4net.config 配置中 file 节点中的地址,在项目中会生成 Log 文件夹,该文件夹下会生成类似 DefalutLog_2024-07-01.log 的文件。该文件名由 file 节点和 datePattern 节点两部分组合而成,其中,staticLogFileName节点需要被设置为 false ,若为 true ,则当前最新日志文件名永远为 file 节点中的名字,其余日志会根据 datePattern 节点自动添加后缀。

  打开日志文件,会发现里面只有 WARN 警告信息,例如:

==========                     
【日志级别】WARN                      
【记录时间】2024-07-01 00:18:28,828                     
【执行时间】[53]毫秒                     
【执行Log分类的名称】DefaultLog              
【传入信息内容】这是条警告信息             
==========

  这是因为配置中的 filter 过滤节点,详见注释,将其注释再次运行,则会正常显示全部日志信息。

2. 配置文件节点详解

  log4net的主要组成有四部分,分别是 Logger、Appender、Layout、Filter等,详见下方。

2.1 Logger 节点配置详解

  以上文的配置为例解释:

<logger name="DefaultLog">
	<!--control log level: ALL|DEBUG|INFO|WARN|ERROR|FATAL|OFF-->
	<!--如果没有定义LEVEL的值,则缺省为DEBUG-->
	<level value="ALL" />
	<appender-ref ref="FileAppenderDefault"></appender-ref>
</logger>

  level 定义记录的日志级别,就是说,你要记录哪个级别以上的日志,级别由低到高依次是:

ALL|DEBUG|INFO|WARN|ERROR|FATAL|OFF

  如果你 level 定义 INFO,那么低于 INFO 级别以下的信息,将不会记入日志,啥意思呢?
  就是说,就算你在程序里,用 log.Debug() 来写入一个日志信息,可是你在配置中指定 level 为 INFO,由于 DEBUG 级别低于 INFO,所以,不会被记入日志。这样的处理非常灵活。

  在具体写日志时,一般可以这样理解日志等级:

FATAL(致命错误):记录系统中出现的能使用系统完全失去功能,服务停止,系统崩溃等使系统无法继续运行下去的错误。例如,数据库无法连接,系统出现死循环。

ERROR(一般错误):记录系统中出现的导致系统不稳定,部分功能出现混乱或部分功能失效一类的错误。例如,数据字段为空,数据操作不可完成,操作出现异常等。

WARN(警告):记录系统中不影响系统继续运行,但不符合系统运行正常条件,有可能引起系统错误的信息。例如,记录内容为空,数据内容不正确等。

INFO(一般信息):记录系统运行中应该让用户知道的基本信息。例如,服务开始运行,功能已经开户等。

DEBUG (调试信息):记录系统用于调试的一切信息,内容或者是一些关键数据内容的输出。

  appender-ref,要引用的 appender 的名字,由 Layout 控制输出格式。

  最后还要说一个LogManager类,它用来管理所有的Logger。它的GetLogger静态方法,可以获得配置文件中相应的Logger:

log4net.ILog log = log4net.LogManager.GetLogger("logger-name");

2.2 Appender 节点配置详解

  以上文的 FileAppenderDefault 节点为例:

点击查看 FileAppenderDefault 节点配置
	<!-- appender 定义日志输出方式   将日志以回滚文件的形式写到文件中。-->
	<appender name="FileAppenderDefault" type="log4net.Appender.RollingFileAppender">
		<!--绝对路径-->
		<!--<file value="D:\KangarooLog.txt"></file>-->
		<!--日志输出到exe程序这个相对目录下-->
		<file value="../../Log/DefalutLog" />
		<!--相对路径,在项目的根目录下-->
		<!--以最后一个路径为准,所以上面的绝对路径下不会写日志-->
		<!--<file value="./Log/Kangaroo.txt"></file>-->
		<!--防止多线程时不能写Log,官方说线程非安全-->
		<!--实际使用时,本地测试正常,部署后没有不能写日志的情况-->
		<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
		<!--追加日志内容,true后续输出的日志会追加到之前的日志文件-->
		<appendToFile value="true" />
		<!--可以为:Once|Size|Date|Composite-->
		<!--Composite为Size和Date的组合-->
		<rollingStyle value="Composite" />
		<!--日志最大个数,都是最新的-->
		<!--rollingStyle节点为Date时,该节点不起作用-->
		<!--rollingStyle节点为Size时,只能有value个日志-->
		<!--rollingStyle节点为Composite时,每天有value个日志-->
		<maxSizeRollBackups value="10" />
		<!--当备份文件时,为文件名加的后缀-->
		<!--后缀为*.txt时,例:AX.txt_2008-07-24.PxP  应该是程序上的一个bug-->
		<!--后缀为*.TXT时,例:AX.txt_2008-07-25.TXT-->
		<datePattern value="_yyyy-MM-dd'.log'" />
		<!--每个文件的大小。只在混合方式与文件大小方式下使用。超出大小后在所有文件名后自动增加正整数重新命名,数字最大的最早写入。可用的单位:KB|MB|GB。不要使用小数,否则会一直写入当前日志-->
		<maximumFileSize value="10MB" />
		<!--置为true,当前最新日志文件名永远为file节中的名字-->
		<staticLogFileName value="false" />
		<!--输出级别在INFO和ERROR之间的日志-->
		<!--<filter type="log4net.Filter.LevelRangeFilter">
			<param name="LevelMin" value="INFO" />
			<param name="LevelMax" value="ERROR" />
		</filter>-->
		<!--必须结合起来用,第一个只过滤出WARN,第二个拒绝其它其它日志输出-->
		<filter type="log4net.Filter.LevelMatchFilter">
			<param name="LevelToMatch" value="WARN" />
		</filter>
		<filter type="log4net.Filter.DenyAllFilter" />
		<layout type="log4net.Layout.PatternLayout">
			<conversionPattern value="%n==========
			%n【日志级别】%-5level
			%n【记录时间】%date
			%n【执行时间】[%r]毫秒
			%n【执行Log分类的名称】%logger
			%n【传入信息内容】%message
			%n=========="/>
		</layout>
	</appender>

  每个节点均写有注释,这里额外介绍下 appender 的输出方式( type 属性),如下所示:

点击查看 appender 输出方式
	AdoNetAppender              将日志记录到数据库中。可以采用SQL和存储过程两种方式。
	AnsiColorTerminalAppender   将日志高亮输出到ANSI终端。
	AspNetTraceAppender         能用asp.net中Trace的方式查看记录的日志。
	BufferingForwardingAppender 在输出到子Appenders之前先缓存日志事件。
	ConsoleAppender             将日志输出到应用程序控制台。
	EventLogAppender            将日志写到Windows Event Log。
	FileAppender                将日志输出到文件。
	ForwardingAppender          发送日志事件到子Appenders。
	LocalSyslogAppender         将日志写到local syslog service (仅用于UNIX环境下)。
	MemoryAppender              将日志存到内存缓冲区。
	NetSendAppender             将日志输出到Windows Messenger service.这些日志信息将在用户终端的对话框中显示。
	OutputDebugStringAppender   将日志输出到Debuger,如果程序没有Debuger,就输出到系统Debuger。如果系统Debuger也不可用,将忽略消息。
	RemoteSyslogAppender        通过UDP网络协议将日志写到Remote syslog service。
	RemotingAppender            通过.NET Remoting将日志写到远程接收端。
	RollingFileAppender         将日志以回滚文件的形式写到文件中。
	SmtpAppender                将日志写到邮件中。
	SmtpPickupDirAppender       将消息以文件的方式放入一个目录中,像IIS SMTP agent这样的SMTP代理就可以阅读或发送它们。
	TelnetAppender              客户端通过Telnet来接受日志事件。
	TraceAppender               将日志写到.NET trace 系统。
	UdpAppender                 将日志以无连接UDP数据报的形式送到远程宿主或用UdpClient的形式广播。

2.3 Filter 节点配置详解

  filter只能作为 appender 的子元素,type 属性表示 Filter 的类型。常用子元素 param 数量0个或多个,作用设置一些参数。具体例子详见上文 2.2 Appender 节点配置详解

  额外补充下 filter 的类型说明:

	DenyAllFilter       阻止所有的日志事件被记录
	LevelMatchFilter    只有指定等级的日志事件才被记录
	LevelRangeFilter    日志等级在指定范围内的事件才被记录
	LoggerMatchFilter   Logger名称匹配,才记录
	PropertyFilter      消息匹配指定的属性值时才被记录
	StringMathFilter    消息匹配指定的字符串才被记录

2.4 Layout 节点配置详解

  layout 节点只能作为 appender 的子元素。type 属性表示 Layout 的类型。具体例子详见上文 2.2 Appender 节点配置详解

  额外补充 layout 节点的 type 属性取值:

	ExceptionLayout         只呈现日志事件中异常的文本信息
	PatternLayout           可以通过类型字符串来配置的布局
	RawPropertyLayout       从日志事件中提取属性值
	RawTimeStampLayout      从日志事件中提取日期
	RawUtcTimeStampLayout   从日志事件中提取UTC日期
	SimpleLayout            很简单的布局
	XmlLayout               把日志事件格式化为XML元素的布局

  这其中我们主要使用的还是PatternLayout 类型,而在 ConversionPattern 节点中,我们可以进一步的配置日志输出格式,以 PatterLayout 的格式化字符串输出为例:

点击查看 PatterLayout 的格式化字符串
    %m、%message         输出的日志消息
    %d、%datetime        输出当前语句运行的时刻,格式%date{yyyy-MM-dd HH:mm:ss,fff}
    %r、%timestamp       输出程序从运行到执行到当前语句时消耗的毫秒数
    %p、%level           日志的当前优先级别
    %c、%logger          当前日志对象的名称
    %L、%line            输出语句所在的行号
    %F、%file            输出语句所在的文件名,警告:只在调试的时候有效,调用本地信息会影响性能
    %a、%appdomain       引发日志事件的应用程序域的名称。
    %C、%class、%type    引发日志请求的类的全名,警告:会影响性能
    %exception           异常信息
    %u、%identity        当前活动用户的名字,我测试的时候%identity返回都是空的。警告:会影响性能
    %l、%location        引发日志事件的名空间、类名、方法、行号。警告:会影响性能,依赖pdb文件
    %M、%method          发生日志请求的方法名,警告:会影响性能
    %n、%newline         换行符
    %x、%ndc             NDC(nested diagnostic context)
    %X、%mdc、%P、%properties  等介于 %property
    %property           输出{log4net:Identity=, log4net:UserName=, log4net:HostName=} 
    %t、%thread         引发日志事件的线程,如果没有线程名就使用线程号。
    %w、%username       当前用户的WindowsIdentity,类似:HostName/Username。警告:会影响性能
    %utcdate            发生日志事件的UTC时间。例如:%utcdate{HH:mm:ss,fff}
    %%                  输出一个百分号

  额外补充下 PatterLayout 格式修饰符:

格式修饰符 对齐 最小宽 最大宽 说明
%20logger 右对齐 20 如果logger名不足20个字符,就在左边补空格
%-20logger 左对齐 20 如果logger名不足20个字符,就在右边补空格
%.30logger 左对齐 30 超过30个字符将截断
%20.30logger 右对齐 20 30 logger名要在20到30之间,少了在左边补空格,多了截断
%-20.30logger 左对齐 20 30 logger名要在20到30之间,少了在右边补空格,多了截断

3. 如何输出自定义类

  自定义扩展输出,通过继承 log4net.Layout.PatternLayout 和 log4net.Layout.Pattern.PatternLayoutConverter 类,使用 log4net.Core.LoggingEvent 类的方法得到了要输出的 LogEntity 类的名称。

  然后通过反射得到各个属性的值,使用 PatternLayout 类 AddConverter 方法传入得到的值,在 PatternLayoutConverter 中对其进行处理。注意配置文件 Appender 中的 Layout type 用到的类的命名空间以及类名,要同步更改为自定义类的。详见示例:

点击查看自定义拓展类的代码
namespace WPFPractice
{
    public class CustomLayout : log4net.Layout.PatternLayout
    {
        public CustomLayout()
        {
            this.AddConverter("Custom", typeof(CustomConvert));
        }
    }
    public class CustomConvert : log4net.Layout.Pattern.PatternLayoutConverter
    {
        protected override void Convert(System.IO.TextWriter writer, log4net.Core.LoggingEvent loggingEvent)
        {
            if (!string.IsNullOrEmpty(Option))
            {
                object obj = loggingEvent.MessageObject;
                if (obj != null)
                {
                    PropertyInfo info = obj.GetType().GetProperty(Option);
                    if (info != null)
                    {
                        object cusMsg = info.GetValue(obj, null);
                        writer.Write(cusMsg);
                    }
                }
            }
        }
    }
}

  与之对应的,我们的配置文件也添加以下代码:

点击查看配置文件新增代码
	<logger name="CustomLog">
		<!--control log level: ALL|DEBUG|INFO|WARN|ERROR|FATAL|OFF-->
		<!--如果没有定义LEVEL的值,则缺省为DEBUG-->
		<level value="ALL" />
		<appender-ref ref="FileAppenderCustom"></appender-ref>
	</logger>
	
	<appender name="FileAppenderCustom" type="log4net.Appender.RollingFileAppender">
		<file value="../../Log/CustomLog" />
		<appendToFile value="true" />
		<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
		<rollingStyle value="Composite" />
		<maxSizeRollBackups value="5" />
		<datePattern value="_yyyy-MM-dd'.log'" />
		<maximumFileSize value="10MB" />
		<staticLogFileName value="false" />
		<layout type="WPFPractice.CustomLayout">
			<conversionPattern value="%n==========
			%n【日志级别】%-5level
			%n【记录时间】%date
			%n【执行时间】[%r]毫秒
			%n【执行线程ID】[%thread]
			%n【执行Log分类的名称】%logger
			%n【耗材类型名】%Custom{LabTypeName}
			%n【耗材名】%Custom{LabName}
			%n【耗材编号】%Custom{LabNumber}
			%n【是否吸头】%Custom{IsTip}
			%n【传入信息内容/类型】%message
			%n==========" />
		</layout>
	</appender>

  接下来我们新建一个类 LabwareModel,并在帮助类 Log4Helper 中添加调用 CustomLog 的方法,来测试能否输出自定义类 LabwareModel 中的内容。

点击查看自定义类 LabwareModel
public class LabwareModel
{
    public string LabTypeName { get; set; } = "采样管";
    public string LabName { get; set; } = "Custom_2000ul";
    public double LabNumber { get; set; } = 200;
    public int IsTip { get; set; } = 1;
}

  帮助类 Log4Helper 如下:

点击查看帮助类 Log4Helper
 private static readonly log4net.ILog logDefault = log4net.LogManager.GetLogger("DefaultLog");

 /// <summary>
 /// 生成默认实例
 /// </summary>
 /// <returns></returns>
 public static LabwareModel GetLabwareModel() 
 {
     LabwareModel labwareModel = new LabwareModel();
     return labwareModel;
 }

 /// <summary>
 /// 测试自定义配置信息输出
 /// </summary>
 public static void TestCustomLog()
 {
     var labware = GetLabwareModel();
     logCustom.Debug("这是条调试信息");
     logCustom.Info("这是条提示信息");
     logCustom.Warn("这是条警告信息");
     logCustom.Error(labware);
     logCustom.Fatal("这是条致命错误信息");
 }

  在主程序中调用进行测试:

private void Application_Startup(object sender, StartupEventArgs e)
{
    Log4Helper.TestCustomLog();
    Log4Helper.TestDefaultLog();
}

  根据上面 FileAppenderCustom 配置中 file 节点中的地址,在项目中会生成 Log 文件夹,该文件夹下会生成类似 CustomLog_2024-07-01.log 的文件。打开文件会看到输出内容如下:

点击查看日志输出内容
==========           
【日志级别】DEBUG                  
【记录时间】2024-07-01 00:18:28,812                  
【执行时间】[37]毫秒           
【执行线程ID】[1]           
【执行Log分类的名称】CustomLog           
【耗材类型名】           
【耗材名】           
【耗材编号】           
【是否吸头】           
【传入信息内容/类型】这是条调试信息           
==========
==========           
【日志级别】INFO                   
【记录时间】2024-07-01 00:18:28,823                  
【执行时间】[48]毫秒           
【执行线程ID】[1]           
【执行Log分类的名称】CustomLog           
【耗材类型名】           
【耗材名】           
【耗材编号】           
【是否吸头】           
【传入信息内容/类型】这是条提示信息           
==========
==========           
【日志级别】WARN                   
【记录时间】2024-07-01 00:18:28,825                  
【执行时间】[50]毫秒           
【执行线程ID】[1]           
【执行Log分类的名称】CustomLog           
【耗材类型名】           
【耗材名】           
【耗材编号】           
【是否吸头】           
【传入信息内容/类型】这是条警告信息           
==========
==========           
【日志级别】ERROR                  
【记录时间】2024-07-01 00:18:28,826                  
【执行时间】[51]毫秒           
【执行线程ID】[1]           
【执行Log分类的名称】CustomLog           
【耗材类型名】采样管           
【耗材名】Custom_2000ul           
【耗材编号】200           
【是否吸头】1           
【传入信息内容/类型】WPFPractice.LabwareModel           
==========
==========           
【日志级别】FATAL                  
【记录时间】2024-07-01 00:18:28,828                  
【执行时间】[53]毫秒           
【执行线程ID】[1]           
【执行Log分类的名称】CustomLog           
【耗材类型名】           
【耗材名】           
【耗材编号】           
【是否吸头】           
【传入信息内容/类型】这是条致命错误信息           
==========

  以上就是关于 Log4Net 配置详解。

posted @ 2024-07-04 13:45  letmebaby  阅读(671)  评论(0编辑  收藏  举报