C#

log4net 使用

Posted by Kerwen Blog on November 13, 2023

简单Demo

  1. 新建一个C# framework console project
  2. 通过Nuget添加log4net引用
  3. 新建一个配置文件log4net.config
  4. 修改配置文件,将属性改成 Copy Always, 添加如下配置信息

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    
     <configuration>
       <configSections>
         <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
       </configSections>
       <log4net>
         <root>
           <level value="WARN" />
           <appender-ref ref="LogFileAppender" />
           <appender-ref ref="ConsoleAppender" />
         </root>
    
       <logger name="testApp.Logging">
         <level value="DEBUG"/>
       </logger>
    
       <appender name="LogFileAppender" type="log4net.Appender.FileAppender" >
         <param name="File" value="log_file.txt" />            
         <param name="AppendToFile" value="true" />
    
         <layout type="log4net.Layout.PatternLayout">
           <param name="ConversionPattern" value="%d [%t] %-5p %c - %m%n" />
         </layout>
       </appender>
    
       <appender name="ConsoleAppender"  type="log4net.Appender.ConsoleAppender" >
         <layout type="log4net.Layout.PatternLayout">
           <param name="ConversionPattern"  value="%d [%t] %-5p %c - %m%n" />
         </layout>
       </appender>
     </log4net>
    </configuration>
    
  5. 修改AssemblyInfo.cs,添加如下配置

    1
    
     [assembly: log4net.Config.XmlConfigurator(ConfigFile="log4net.config", Watch = true)]
    
  6. 修改main函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    
     static void Main(string[] args)
     {
         log4net.ILog log = log4net.LogManager.GetLogger("testApp.Logging");
    
         string assemblyFilePath = Assembly.GetExecutingAssembly().Location;
         string assemblyDirPath = Path.GetDirectoryName(assemblyFilePath);
         DirectoryInfo pathInfo = new DirectoryInfo(assemblyDirPath);
         string configFilePath = pathInfo.Parent.Parent.FullName + "//log4net.config";
         log4net.Config.XmlConfigurator.ConfigureAndWatch(new FileInfo(configFilePath));
    
         Thread.CurrentThread.Name = "main";
         log.Debug("initial connection");
         log.Warn("test");
         log.Info(DateTime.Now.ToString() + ": login success");
    
         Console.ReadKey();
     }
    

结构

log4net 有四种主要的组件,分别是Logger(记录器), Repository(库), Appender(附着器)以及 Layout(布局).

Logger

Logger接口

Logger是应用程序需要交互的主要组件,它用来产生日志消息。产生的日志消息并不直接显示,还要预先经过Layout的格式化处理后才会输出。
Logger提供了多种方式来记录一个日志消息,你可以在你的应用程序里创建多个Logger,每个实例化的Logger对象都被log4net框架作为命名实体(named entity)来维护。这意味着为了重用Logger对象,你不必将它在不同的类或对象间传递,只需要用它的名字为参数调用就可以了。
Log4net框架定义了一个叫做LogManager的类,用来管理所有的logger对象。它有一个GetLogger()静态方法,用我们提供的名字参数来检索已经存在的Logger对象。如果框架里不存在该Logger对象,它也会为我们创建一个Logger对象。代码如下所示:

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

通常来说,我们会以类(class)的类型(type)为参数来调用GetLogger(),以便跟踪我们正在进行日志记录的类。传递的类(class)的类型(type)可以用typeof(Classname)方法来获得

日志的级别

有七个日志记录级别,其中五个可以在代码中调用。 它们如下(最高的位于列表顶部):
从高到低分别为:OFF > FATAL > ERROR > WARN > INFO > DEBUG > ALL

Repository

Repository主要用于负责日志对象组织结构的维护。
如果你是个log4net框架的使用者,而非扩展者,那么你几乎不会在你的代码里用到Repository的类。相反的,你需要用到LogManager类来自动管理库和日志对象。

Appender

一个好的日志框架应该能够产生多目的地的输出。比如说输出到控制台或保存到一个日志文件。log4net 能够很好的满足这些要求。它使用一个叫做Appender的组件来定义输出介质。正如名字所示,这些组件把它们附加到Logger日志组件上并将输出传递到输出流中。你可以把多个Appender组件附加到一个日志对象上。

Appender Filters

一个Appender 对象缺省地将所有的日志事件传递到输出流。Appender的过滤器(Appender Filters) 可以按照不同的标准过滤日志事件。

记录日志等级为“FATAL”和“ERROR”的日志信息:

1
2
3
4
5
6
7
  <filter type="log4net.Filter.LevelMatchFilter">
    <levelToMatch value="FATAL"/>
  </filter>
  <filter type="log4net.Filter.LevelMatchFilter">
    <levelToMatch value="ERROR"/>
  </filter>
  <filter type="log4net.Filter.DenyAllFilter"/>

记录日志等级范围从“ERROR”到“INFO”的日志信息:

1
2
3
4
5
<filter type="log4net.Filter.LevelRangeFilter">
  <levelMax value="ERROR"/>
  <levelMin value="INFO"/>
</filter>
<filter type="log4net.Filter.DenyAllFilter"/><!--不加这个过滤器也可以-->

Layout

Layout 组件用于向用户显示最后经过格式化的输出信息。输出信息可以以多种格式显示,主要依赖于我们采用的Layout组件类型。可以是线性的或一个XML文件。Layout组件和一个Appender组件一起工作。API帮助手册中有关于不同Layout组件的列表。一个Appender对象,只能对应一个Layout对象。

配置文件

设置 log4net的标准方法是使用app.config 文件或 web.config文件。 为了使其与 log4net 正常工作,需要将一些信息放入配置文件中。 这些部分将告诉 log4net 如何配置自身。 无需重新编译应用程序即可更改设置

Root

Root用来记录所有顶级记录器引用。 这些记录器从root继承信息。root节点还包含一个level属性,用来记录log的最低级别。 由于所有内容都继承自root,因此任何子记录器都不会记录低于此lelve的日志信息。 这是快速控制应用程序中的日志记录级别的简单方法。
下面的例子中,Debug信息将不会被记录。

1
2
3
4
5
<root>
  <level value="INFO"/>
  <appender-ref ref="FileAppender"/>
  <appender-ref ref="ConsoleAppender" />
</root>

Additional Loggers

log4net 允许指定除root之外的其他记录器引用。

1
2
3
4
<logger name="Log4NetTest.OtherClass">
  <level value="DEBUG"/>
  <appender-ref ref="ConsoleAppender"/>
</logger>

请注意,logger的name是包含命名空间的类的全名。

ConfigSections

在配置文件中,除了 log4net 配置信息之外,还可能放了其他信息,因此需要指定一个部分来标识 log4net 配置的存放位置。 下面是一个示例,指定配置信息将存储在 XML 标签log4net下:

1
2
3
4
5
6
7
<configSections>
  <section name="log4net" 
    type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
</configSections>
<log4net>
  ...
</log4net>

Appender

Appender指定log的位置、记录方式以及在什么情况下记录信息。 下面是一个Appender的示例:

1
<appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">

Layout

每个appender必须有一个Layout。 根据所使用的appender的类型,这可能会有所不同,但基本原理是相同的。 需要指定一个数据写入方式的类型。 有多种选项,但建议使用的选项是pattern类型。 这将允许指定如何将数据写入数据存储库。 如果指定pattern,则需要一个指定转换模式的子标签conversionPattern。 这是将数据写入数据存储库的模式。

1
2
3
4
<layout type="log4net.Layout.PatternLayout">
  <conversionPattern value="%date [%thread] %-5level %logger [%ndc] 
- %message%newline"/>
</layout>

Conversion Patterns

1
2
3
4
5
6
7
8
9
10
%m(message):输出的日志消息;
%n(newline):换行;
%d(datetime):输出当前语句运行的时刻;
%r(runtime):输出程序从运行到执行到当前语句时消耗的毫秒数;
%t(threadid):当前语句所在的线程ID ;
%p(priority): 日志的当前日志级别;
%c(class):当前日志对象的名称;
%L:输出语句所在的行号;
%F:输出语句所在的文件名;
%-10:表示最小长度为10,如果不够,则用空格填充;

Filters

filter是 appender 的另一个重要部分。 使用过滤器,您可以指定要记录的级别,甚至可以在消息中查找关键字。 过滤器可以混合和匹配,但这样做时需要小心。

StringMatchFilter

字符串匹配过滤器会查找log信息中的特定字符串。 可以指定多个字符串匹配过滤器。 它们的工作方式类似于查询中的 OR 语句。 过滤器将查找第一个字符串,然后查找第二个字符串,依此类推,直到找到匹配项。 然而,这里要注意的重要一点是,未找到与指定字符串的匹配并不排除该条目(因为它可能会继续到下一个字符串匹配过滤器)。 然而,这意味着您可能会遇到找不到匹配项的情况。 在这种情况下,默认操作是记录该条目。 因此,在字符串匹配过滤器集的末尾,有必要包含一个拒绝所有过滤器,以在未进行匹配的情况下拒绝记录条目。

1
2
3
4
<filter type="log4net.Filter.StringMatchFilter">
  <stringToMatch value="test" />
</filter>
<filter type="log4net.Filter.DenyAllFilter"/>

LevelRangeFilter

LevelRangeFilter告诉系统仅记录指定范围内的log。 在下面的示例中,级别为 INFO、WARN、ERROR 或 FATAL 的事件将被记录,但 DEBUG 事件将被忽略。 不需要在此条目后添加DenyAll过滤器,因为拒绝是隐含的。

1
2
3
4
<filter type="log4net.Filter.LevelRangeFilter">
  <levelMin value="INFO" />
  <levelMax value="FATAL" />
</filter>

LevelMatchFilter

LevelMatchFilter指定一个且仅一个要捕获的级别。 但是,它没有内置拒绝功能,因此需要在列出此过滤器后添加DenyAll过滤器。

1
2
3
4
<filter type="log4net.Filter.LevelMatchFilter">
  <levelToMatch value="ERROR"/>
</filter>
<filter type="log4net.Filter.DenyAllFilter"/>

DenyAllFilter

如果忘记了这个filter,可能会导致程序无法按预期工作。 此filter的唯一目的是指定不应创建任何log条目。 如果这是唯一的filter,则log4net不会记录任何内容。 然而,它的真正目的是指定不再记录任何内容(记住,任何已经匹配的内容都已被记录)。

1
<filter type="log4net.Filter.DenyAllFilter" />

Appender demo

Console Appender

1
2
3
4
5
6
7
8
9
<appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
  <layout type="log4net.Layout.PatternLayout">
    <conversionPattern value="%date{ABSOLUTE} [%thread] %level %logger - %message%newline"/>
  </layout>
  <filter type="log4net.Filter.StringMatchFilter">
    <stringToMatch value="test" />
  </filter>
  <filter type="log4net.Filter.DenyAllFilter" />
</appender>

File Appender

File Appender将log写入文本文件。 这里需要注意的最大区别是必须指定文本文件的名称,指定我们应该 附加到文件(而不是覆盖它),并且指定 FileAppender 应使用最小锁定,这样文件可供多个附加程序使用。

1
2
3
4
5
6
7
8
9
10
11
12
<appender name="FileAppender" type="log4net.Appender.FileAppender">
  <file value="mylogfile.txt" />
  <appendToFile value="true" />
  <lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
  <layout type="log4net.Layout.PatternLayout">
    <conversionPattern value="%date [%thread] %level %logger - %message%newline" />
  </layout>
  <filter type="log4net.Filter.LevelRangeFilter">
    <levelMin value="INFO" />
    <levelMax value="FATAL" />
  </filter>
</appender>

Rolling File Appender

应尽可能使用Rolling File Appender来代替File Appender。 滚动文件追加器的目的是执行与文件追加器相同的功能,但具有附加选项,即在启动新的日志文件之前仅存储一定量的数据。 这样,就不必担心日志文件大小会随着时间的推移而增长的太大。 在此示例中,指定日志文件的上限应为 10MB,并且在开始删除它们之前最多保留 5 个存档文件(首先删除最旧的文件)。 存档将以与文件相同的名称命名,但带有一个点和数字。 staticLogFileName 条目确保当前日志文件始终以文件标记中指定的名称(mylogfile.txt)命名。

1
2
3
4
5
6
7
8
9
10
11
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
  <file value="mylogfile.txt" />
  <appendToFile value="true" />
  <rollingStyle value="Size" />
  <maxSizeRollBackups value="5" />
  <maximumFileSize value="10MB" />
  <staticLogFileName value="true" />
  <layout type="log4net.Layout.PatternLayout">
    <conversionPattern value="%date [%thread] %level %logger - %message%newline" />
  </layout>
</appender>

rollingStyle有四种模式:

Name Description
Once Roll files once per program execution
Size Roll files based only on the size of the file
Date Roll files based only on the date
Composite Roll files based on both the size and date of the file

reference