原文地址
向应用程序代码插入日志请求需要大量的规划和努力。观察表明,约 4% 的代码是专门用来记录日志的。因此,即使中等规模的应用程序也会嵌入成千上万的日志语句。鉴于日志语句在应用程序中的数量,在无需手动修改的情况下,管理这些日志语句,就成为当务之急。
log4net 环境是完全可用编程配置的。然而,利用配置文件来配置 log4net 更灵活。目前,配置文件主要采用 xml 文件。
下面在 MyApp 应用程序中使用 log4net。
位于本文 Demo 中的 MyApp 演示。
using Com.Foo;
// Import log4net classes.
using log4net;
using log4net.Config;
public class MyApp
{
// Define a static logger variable so that it references the
// Logger instance named "MyApp".
PRivate static readonly ILog log = LogManager.GetLogger(typeof(MyApp));
static void Main(string[] args)
{
// Set up a simple configuration that logs on the console.
BasicConfigurator.Configure();
log.Info("Entering application.");
Bar bar = new Bar();
bar.DoIt();
log.Info("Exiting application.");
}
}
导入 log4net 相关类。然后,定义一个静态的 logger 变量,用 log4net 静态方法和 MyApp 类名来初始化。
MyApp 使用位于另一个命令空间中的 Bar 类:
// Import log4net classes.
using log4net;
namespace Com.Foo
{
public class Bar
{
private static readonly ILog log = LogManager.GetLogger(typeof(Bar));
public void DoIt()
{
log.Debug("Did it again!");
}
}
}
调用 BasicConfigurator.Configure() 方法会创建了一个最简单的 log4net,向 root 记录器添加一个 ConsoleAppender——控制台追加器。用log4net.Layout.PatternLayout 格式化输出,设置格式为“%-4timestamp [%thread] %-5level %logger %ndc - %message%newline”。
默认时,root 记录器被分配为 Level.DEBUG。
MyApp 输出为:
0 [main] INFO MyApp - Entering application.
36 [main] DEBUG Com.Foo.Bar - Did it again!
51 [main] INFO MyApp - Exiting application.
另外,log4net 中的“孩子”记录器只会连接到他们已存在的“祖先”。具体地讲,名为 Com.Foo.Bar 记录器被直接连接到 root 记录器,从而规避不存在的 Com 或 Com.Foo 记录器。这显著提高了性能,并减少 log4net 的内存占用。
MyApp 类通过调用 BasicConfigurator.Configure() 方法配置 log4net,而无需在配置文件进行任何配置。其他类只需引用 log4net 命名空间,检索期望的记录器就可以。
上面例子总是输出相同的日志信息,但很容易修改 MyApp 的配置,在运行时控制日志的输出。下面稍微对程序进行修改。
位于本文 Demo 中的 MyApp2 演示。
using Com.Foo;
// Import log4net classes.
using log4net;
using log4net.Config;
public class MyApp
{
private static readonly ILog log = LogManager.GetLogger(typeof(MyApp));
static void Main(string[] args)
{
// BasicConfigurator replaced with XmlConfigurator.
XmlConfigurator.Configure(new System.IO.FileInfo(args[0]));
log.Info("Entering application.");
Bar bar = new Bar();
bar.DoIt();
log.Info("Exiting application.");
}
}
XmlConfigurator.Configure() 方法可以解析指定的配置文件,并相应地进行设置。上面示例是在命令行参数指定配置文件的路径。
下面配置文件的输出结果跟使用 BasicConfigurator 的结果一样。
<log4net>
<!-- A1 is set to be a ConsoleAppender -->
<appender name="A1" type="log4net.Appender.ConsoleAppender">
<!-- A1 uses PatternLayout -->
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%-4timestamp [%thread] %-5level %logger %ndc - %message%newline" />
</layout>
</appender>
<!-- Set root logger level to DEBUG and its only appender to A1 -->
<root>
<level value="DEBUG" />
<appender-ref ref="A1" />
</root>
</log4net>
假设,我们不想看到任何关于 Com.Foo 包的输出。下面配置文件是实现这个目标的一种方法。
位于本文 Demo 中的 MyApp3 演示。
<log4net>
<!-- A1 is set to be a ConsoleAppender -->
<appender name="A1" type="log4net.Appender.ConsoleAppender">
<!-- A1 uses PatternLayout -->
<layout type="log4net.Layout.PatternLayout">
<!-- Print the date in ISO 8601 format -->
<conversionPattern value="%date [%thread] %-5level %logger %ndc - %message%newline" />
</layout>
</appender>
<!-- Set root logger level to DEBUG and its only appender to A1 -->
<root>
<level value="DEBUG" />
<appender-ref ref="A1" />
</root>
<!-- Print only messages of level WARN or above in the package Com.Foo -->
<logger name="Com.Foo">
<level value="WARN" />
</logger>
</log4net>
用上面配置文件来配置 MyApp 将显示如下结果:
2000-09-07 14:07:41,508 [main] INFO MyApp - Entering application.
2000-09-07 14:07:41,529 [main] INFO MyApp - Exiting application.
由于记录器 Com.Foo.Bar 在任何地方中没有分配级别,因此,它会从 Com.Foo 继承,而 Com.Foo 在配置文件中被设置成 WARN 级别。因此,Bar.DoIt 方法中的记录语句具有 DEBUG 级别,而 DEBUG 级别比 WARN 要低。因此,DoIt() 方法的日志请求被抑制。
log4net 是按级别输出日志的,只输出从你指定级别高的日志开始。
下面的配置文件使用多个追加器(appenders)。
位于本文 Demo 中的 MyApp4 演示。
<log4net>
<appender name="Console" type="log4net.Appender.ConsoleAppender">
<layout type="log4net.Layout.PatternLayout">
<!-- Pattern to output the caller's file name and line number -->
<conversionPattern value="%5level [%thread] (%file:%line) - %message%newline" />
</layout>
</appender>
<appender name="RollingFile" type="log4net.Appender.RollingFileAppender">
<file value="example.log" />
<appendToFile value="true" />
<maximumFileSize value="100KB" />
<maxSizeRollBackups value="2" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%level %thread %logger - %message%newline" />
</layout>
</appender>
<root>
<level value="DEBUG" />
<appender-ref ref="Console" />
<appender-ref ref="RollingFile" />
</root>
</log4net>
用上面配置文件将输出如下信息到控制台。
INFO [main] (MyApp.cs:16) - Entering application.
DEBUG [main] (Bar.cs:12) - Doing it again!
INFO [main] (MyApp.cs:19) - Exiting application.
另外,root 记录器的第二个附加目的地,输出还会被定向到 example.log 文件。当文件达到100KB时,该文件将被转存。转存时,example.log 将自动移动到 example.log.1。
你可以将 100KB 设置得小点,能尽快看到结果。
注意,获得上面那些日志的不同行为,不需要重新编译代码。你可以很容易地登录一个电子邮件地址,重定向所有 Com.Foo 输出到 NT 事件记录器,或转发事件记录到远程 log4net 服务器,这将根据本地服务器策略日志。
关于使用 XmlConfigurator 配置追加器的更多例子,查看 Example Appender Configuration 文档。
log4net 的配置可以通过装配级别(assembly-level)的属性来配置,而不用编程方式。
log4net.Config.XmlConfiguratorAttribute 让 XmlConfigurator 通过下面属性配置:
如果指定了该属性,XmlConfigurator 会使用配置文件名。该配置文件路径是相对应用程序基目录(AppDomain.CurrentDomain.BaseDirectory)。
该属性不能跟下面的 ConfigFileExtension 属性一起使用。
如果指定了该属性,它就是配置文件的扩展名。装配文件(程序集文件)名作为基文件名,后面加 log4net 配置文件的后缀名。例如,如果装配是从 TestApp.exe 加载的,并且,ConfigFileExtension 属性设置为 log4net,那么,配置文件的文件名就为 TestApp.exe.log4net。这相当于设置 ConfigFile 属性为 TestApp.exe.log4net。
配置文件的路径是应用程序基目录(AppDomain.CurrentDomain.BaseDirectory)。
该属性不能与 ConfigFile 属性一起使用。
如果该标记指定为 true,那么,框架将监控配置文件,并且当配置文件被修改时,重新加载。
如果既 ConfigFile 和 ConfigFileExtension 属性都没有指定,那么应用程序配置文件(如 TestApp.exe.config)作为 log4net 的配置文件。
用法:
// Configure log4net using the .config file
[assembly: log4net.Config.XmlConfigurator(Watch=true)]
// This will cause log4net to look for a configuration file
// called TestApp.exe.config in the application base
// directory (i.e. the directory containing TestApp.exe)
// The config file will be watched for changes.
// Configure log4net using the .log4net file
[assembly: log4net.Config.XmlConfigurator(ConfigFileExtension="log4net",Watch=true)]
// This will cause log4net to look for a configuration file
// called TestApp.exe.log4net in the application base
// directory (i.e. the directory containing TestApp.exe)
// The config file will be watched for changes.
上面的装配属性只能在装配文件(程序集文件)中出现一次。若出现多次,Build 时,会报错。
Using attributes can be a clearer method for defining where the application's configuration will be loaded from. However it is worth noting that attributes are purely passive. They are information only. Therefore if you use configuration attributes you must invoke log4net to allow it to read the attributes. A simple call to LogManager.GetLogger will cause the attributes on the calling assembly to be read and processed. Therefore it is imperative to make a logging call as early as possible during the application start-up, and certainly before any external assemblies have been loaded and invoked.
如果你使用 attributes 来配置 log4net,你应用程序配置文件的 AppSettings 小节有两个配置可以用于覆盖你在装配文件中给出的值。
即使装配文件属性为
[assembly: log4net.Config.XmlConfigurator(Watch=false)]
<appSettings>
<add key="log4net.Config" value="log4net.config"/>
<add key="log4net.Config.Watch" value="True"/>
</appSettings>
使用文件来指定 log4net 配置。有两种方式读取文件:
其实,log4net 即可以读取应用程序 .config 配置文件,也可以直接读取它自己的配置文件。之所以这么做,完全是因为,应用程序访问文件系统时存在权限问题。估计你也遇到和设置过,比如,当在 Web 应用程序中,涉及操作文件系统时,比如写文件,访问所在文件的目录就会遇到权限问题。
如果配置的数据是在应用程序配置文件中,如 MyApp.exe.config 或 Web.config,那么 System.Configuration API 是唯一可用的。因为,System.Configuration API 不支持重新加载配置文件,配置的设置不能通过 log4net.Config.XmlConfigurator.ConfigureAndWatch 方法来监控。使用 System.Configuration API 来读取配置数据的主要优点是,它比直接访问配置文件需要的权限更小。
使用 System.Configuration API 配置应用程序的唯一方法,是调用 log4net.Config.XmlConfigurator.Configure() 或 log4net.Config.XmlConfigurator.Configure(ILoggerRepository) 方法。
为了在 .config 文件嵌入配置数据,小节的名称必须能够被 .NET 配置文件分析器识别,这是通过使用 configSections 元素。小节必须指定 log4net.Config.Log4NetConfigurationSectionHandler,用于解析配置的小节。小节的类型必须是完全的装配限定名(fully assembly qualified name),因为,这是通过 .NET 配置文件分析器加载的,而不是 log4net。
对于 log4net 必须指定正确的装配名。下面示例是一个简单的配置,该文件指定了正确的节处理程序用于 log4net。
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
</configSections>
<log4net>
<appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender" >
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger [%ndc] - %message%newline" />
</layout>
</appender>
<root>
<level value="INFO" />
<appender-ref ref="ConsoleAppender" />
</root>
</log4net>
</configuration>
configSetions 元素必须作为 configuration 元素的第一个子元素。否则,运行应用程序时,会报错。
上面示例,指定了 log4net 程序集(log4net 的 dll 文件)。该程序集必须位于 .NET 运行时能够找到的地方。例如,它位于跟应用程序相同的目录。如果 log4net 程序集被存放在 GAC(全局程序集缓存),那么完全限定的程序集名必须指定文化、版本和公钥。
当使用应用程序 .config 文件指定 log4net 配置时,小节名和 XML 元素必须为 log4net,即 XML 文件中 log4net 使用名为“log4net”元素,而 setion 小节的 name 属性也为“log4net”。
XmlConfigurator 可以直接读取任何 XML 文件来配置 log4net。这包括应用程序 .config 文件,如 MyApp.exe.config 或 Web.config。不直接读取配置文件的唯一理由是,如果应用程序没有足够的权限读取文件,那么,配置必须通过 .NET 配置 API 来加载(见上面)。
通过 log4net.Config.XmlConfigurator 方法读取配置文件,该方法接受 System.IO.FileInfo 对象。因为,文件系统可以被监控用于文件修改通知,ConfigureAndWatch 方法可用于监控文件的修改,并自动重新配置 log4net。
添加 log4net.Config.XmlConfiguratorAttribute 用于指定读取的配置文件。
配置可以从文件中的 log4net 元素读取。在文件中,只能指定一个 log4net 元素,但它可以位于 XML 结构中的任何位置。例如,它可以是根元素:
<?xml version="1.0" encoding="utf-8" ?>
<log4net>
<appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender" >
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger [%ndc] - %message%newline" />
</layout>
</appender>
<root>
<level value="INFO" />
<appender-ref ref="ConsoleAppender" />
</root>
</log4net>
或是,嵌入其他元素内:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="log4net" type="System.Configuration.IgnoreSectionHandler" />
</configSections>
<log4net>
<appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender" >
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger [%ndc] - %message%newline" />
</layout>
</appender>
<root>
<level value="INFO" />
<appender-ref ref="ConsoleAppender" />
</root>
</log4net>
</configuration>
上面例子显示,配置的数据如何嵌入到应用程序 .config 文件,即使该文件直接被 log4net 读取。注意:如果 .NET 配置文件分析器发现,没有通过 configSections 元素注册,那么,.NET 配置文件分析器将抛出一个异常。因此,上面例子,log4net 小节被注册,但是 type 指定的处理程序是 System.Configuration.IgnoreSectionHandler。这是一个内置类,指示将采用另一个方法读取配置的小节。
log4net 包含解析 XML DOM 的一个配置读取器,即 log4net.Config.XmlConfigurator。本小节说明配置器接受的语法。
下面是一个可用的 XML 配置。根元素必须是 log4net。注意:这不意味着该元素可以嵌入到另一个 XML 文档。关于如何嵌入的更多信息查看 Configuration Files。
<log4net>
<appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender" >
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger [%ndc] - %message%newline" />
</layout>
</appender>
<root>
<level value="INFO" />
<appender-ref ref="ConsoleAppender" />
</root>
</log4net>
log4net 元素支持如下属性:
属性 | 描述 |
debug | 可选。Value must be either true or false. The default value is false. Set this attribute to true to enable internal log4net debugging for this configuration. |
update | 可选。Value must be either Merge or Overwrite. The default value is Merge. Set this attribute to Overwrite to reset the configuration of the repository being configured before applying this configuration. |
threshold | 可选。Value must be the name of a level registered on the repository. The default value is ALL. Set this attribute to limit the messages that are logged across the whole repository, regardless of the logger that the message is logged to. |
log4net 元素支持如下子元素:
元素 | 描述 |
appender | 零个或多个。Defines an appender. |
logger | 零个或多个。Defines the configuration of a logger. |
renderer | 零个或多个。Defines an object renderer. |
root | 可选。只能有一个。Defines the configuration of the root logger. |
param | 零个或多个。Repository specific parameters |
appender 是 log4net 元素的子元素。每个 appender 必须具有唯一的名字。必须指定 appender 的实现类型 type。
下面展示定义一个 log4net.Appender.ConsoleAppender 的 appender。appender 名为 ConsoleAppender。
<appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender" >
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger [%ndc] - %message%newline" />
</layout>
</appender>
appender 元素的 name 属性可以为任何你期望的名称。
appender 元素支持如下属性:
属性 | 描述 |
name | 必需。该值必须是一个 appender 字符串名称(这不废话嘛)。该名称必须在定义配置文件中所有的 appender 中唯一。记录器的 appender-ref 元素会使用该名字引用一个 appender。 |
type | 必需。该值是 appender 的类型名。If the appender is not defined in the log4net assembly this type name must be fully assembly qualified. |
appender 元素支持如下子元素:
元素 | 描述 |
appender-ref | 零个或多个。Allows the appender to reference other appenders. Not supported by all appenders. |
filter | 零个或多个。Defines the filters used by this appender. |
layout | 可选。只能有一个。Defines the layout used by this appender. |
param | 零个或多个。Appender specific parameters. |
配置 appenders 的信息参看 Example Appender Configuration 文档。
Filters 是只能定义在 appender,作为它的子元素。
filter 元素支持如下属性:
属性 | 描述 |
type | 必需。Value must be the type name for this filter. If the filter is not defined in the log4net assembly this type name must be fully assembly qualified. |
filter 元素支持如下子元素:
元素 | 描述 |
param | 零个或多个。Filter specific parameters. |
Filter 形成一个事件必须通过的链。沿着这个链的任何 filter 能够接受时间并停止处理,拒绝事件并停止处理,或让事件到下一个 filter。如果事件到达 filter 链的末端,而没有被拒绝,将隐式接受,并被记录。
<filter type="log4net.Filter.LevelRangeFilter">
<levelMin value="INFO" />
<levelMax value="FATAL" />
</filter>
上面配置将记录所有在 INFO 和 FATAL 级别之间的事件。
如果我们只想记录具有指定字符串(如,database),那么需要指定如下 filter:
<filter type="log4net.Filter.StringMatchFilter">
<stringToMatch value="database" />
</filter>
<filter type="log4net.Filter.DenyAllFilter" />
第一个 filter 将查找事件信息文本中的子字符串 'database'。若发现,则 filter 接受消息,并且 filter 处理将停止,记录信息;否则,事件将被传递给下一个 filter 处理。如果不存在下一个 filter,那么,事件会隐式接受并记录。但是,我们不想记录不匹配的事件,因此,我们需要使用 log4net.Filter.DenyAllFilter,来拒绝所有到达的事件。这个 filter 只在 filter 链的末端有用。
If we want to allow events that have either 'database' or 'ldap' in the message text we can use the following filters:
<filter type="log4net.Filter.StringMatchFilter">
<stringToMatch value="database"/>
</filter>
<filter type="log4net.Filter.StringMatchFilter">
<stringToMatch value="ldap"/>
</filter>
<filter type="log4net.Filter.DenyAllFilter" />
Layout 元素只能定义在 appender,作为它的子元素。 Layout 就是具体日志的格式。
layout 元素支持如下属性:
属性 | 描述 |
type | 必需。Value must be the type name for this layout. If the layout is not defined in the log4net assembly this type name must be fully assembly qualified. |
layout 元素支持的子元素:
元素 | 描述 |
param | 零个或多个。Layout specific parameters. |
下面示例演示如何使用 log4net.Layout.PatternLayout 配置布局。
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger [%ndc] - %message%newline" />
</layout>
Only one root logger element may only be defined and it must be a child of <log4net> element. The root logger is the root of the logger hierarchy. All loggers ultimately inherit from this logger.
An example root logger:
<root>
<level value="INFO" />
<appender-ref ref="ConsoleAppender" />
</root>
root 元素没有属性。
root 元素支持的子元素:
元素 | 描述 |
appender-ref | 零个或多个。Allows the logger to reference appenders by name. |
level | 可选。只能有一个。Defines the logging level for this logger. This logger will only accept event that are at this level or above. |
param | 零个或多个。Logger specific parameters. |
Logger 元素只能定义在 log4net 元素,作为它的子元素。
示例:
<logger name="LoggerName">
<level value="DEBUG" />
<appender-ref ref="ConsoleAppender" />
</logger>
logger 元素支持的属性如下表所示:
属性 | 描述 |
name | 必需。Value must be the name of the logger. |
additivity | 可选。Value may be either true or false. The default value is true. Set this attribute to false to prevent this logger from inheriting the appenders defined on parent loggers. |
logger 元素支持的子元素如下表所示:
元素 | 描述 |
appender-ref | 零个或多个。Allows the logger to reference appenders by name. |
level | 可选。只能有一个。Defines the logging level for this logger. This logger will only accept event that are at this level or above. |
param | 零个或多个。Logger specific parameters. |
Renderer 元素只能定义在 log4net 元素,作为其子元素。
示例:
<renderer renderingClass="MyClass.MyRenderer" renderedClass="MyClass.MyFunkyObject" />
renderer 元素支持的属性如下表所示:
属性 | 描述 |
renderingClass | 必需。Value must be the type name for this renderer. If the type is not defined in the log4net assembly this type name must be fully assembly qualified. This is the type of the object that will take responsibility for rendering the renderedClass. |
renderedClass | 必需。Value must be the type name for the target type for this renderer. If the type is not defined in the log4net assembly this type name must be fully assembly qualified. This is the name of the type that this renderer will render. |
renderer 元素没有子元素。
Parameter 元素是很多元素的子元素。
示例:
<param name="ConversionPattern" value="%date [%thread] %-5level %logger [%ndc] - %message%newline" />
param 元素支持的属性如下表所示:
属性 | 描述 |
name | 必需。Value must be the name of the parameter to set on the parent object. |
value | 可选。One of value or type attributes must be specified. The value of this attribute is a string that can be converted to the value of the parameter. |
type | 可选。One of value or type attributes must be specified. The value of this attribute is a type name to create and set as the value of the parameter. If the type is not defined in the log4net assembly this type name must be fully assembly qualified. |
param 元素支持子元素如下表所示:
元素 | 描述 |
param | 零个或多个。Parameter specific parameters. |
An example param that uses nested param elements:
<param name="evaluator" type="log4net.spi.LevelEvaluator">
<param name="Threshold" value="WARN"/>
<param>
配置 parameters 直接映射到一个对象的可写属性。可用的属性取决于被配置对象的实际类型。log4net SDK 文档包含 log4net 程序集中所有组件的 API 引用。
所有 parameters 可交替使用参数名作为元素名,而不是使用 param 元素和 name 属性指定。
示例:
<param name="evaluator" type="log4net.spi.LevelEvaluator">
<param name="Threshold" value="WARN"/>
<param>
可以写为:
<evaluator type="log4net.spi.LevelEvaluator">
<threshold value="WARN"/>
<evaluator>
新闻热点
疑难解答