一、基础:
log4net分为三部分:配置、设置和调用。配置通常是在app.webconfig或web.config文件中;为了增加灵活性,我们也可以使用单独的配置文件。设置通常是几行代码,作用是设置和实例化一个logger连接。最后一部分是调用。
二、日志级别:
有7个日志级别,其中5个可以在代码中调用。下面是日志级别列表,按照优先级排列:
- OFF - nothing gets logged (cannot be called)
- FATAL
- ERROR
- WARN
- INFO
- DEBUG
- ALL - everything gets logged (cannot be called)
这些级别会在代码和配置文件中多次使用。除了第一个和最后一个外,其他的级别没有指明代表什么含义。
三、配置:
在app.webconfig或web.config文件中配置log4net logger是标准做法。
1.Root:
我们需要一个root节点来覆盖最高级别的logger引用,这些是logger的继承信息。root节点唯一的其他作用就是定义日志的最低级别。因为所有logger均继承自root,因此低于指定级别的不会被记录为日志。这是控制程序中日志级别的简单方式,例如下面的例子中使用默认的INFO级别(这样DEBUG消息将会被忽略),并且应该启用root下面的两个appender的引用:
<root> <level value="INFO"/> <appender-ref ref="FileAppender"/> <appender-ref ref="ConsoleAppender" /> </root>
2.Additional Logger:
有时我们希望知道程序特定一部分的更多信息。log4net允许我们在root logger之外指定额外的logger。例如,下面的例子中指定了一个额外的logger,记录在OtherClass类对象内的控制台日志:
<logger name="Log4NetTest.OtherClass"> <level value="DEBUG"/> <appender-ref ref="ConsoleAppender"/> </logger>
需要注意的是logger name的值为包含命名空间的类名。如果我们想要监控整个命名空间,就只要使用需要监控的命名空间填充即可。我(作者)建议不要在多个logger中重复使用日志输出目的地。我们可以这样做,但会有一些 不可预知的结果。
3.CofigSections:
在一个配置文件中除了log4net外通常还会有其他配置信息,我们需要定义一个节点来标识log4net配置的位置。下面的例子,表示log4net的配置信息在“log4net”标签下面:
<configSections> <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/> </configSections>
4.通用的Appender:
appender是记录信息的名字。它用来指定信息被记录在哪里,怎样被记录以及在什么情况下被记录。虽然每个appender都有不同的参数来确定信息被记录在哪里,但是还是有一些通用的元素。第一个是name和type。必须为每个appender命名,并为他分配一个类型。例如:
<appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
4.1.Layout:
在每个appender内部必须有一个layout节点。尽管根据不同的appender类型会有不同,但是基础的是相同的。我们需要一个类型来指定数据如何被写入。有多个选项,但是我(作者)推荐的是使用模式布局(pattern layout)类型。因为这样允许我们指定数据如何被写入数据存储。如果我们指定模式布局类型,我们还需要一个子标签来指定转换模式(conversion pattern)。我们的数据使用这种模式写入的到数据存储。例如:
<layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%date [%thread] %-5level %logger [%ndc] - %message%newline"/> </layout>
4.2.Conversion Pattern:
正如上面提到的,转换模式实体是模式布局用来告诉appender如何存储信息的。在这些模式中可以使用很多不同的关键词和字符串。在这里列出我(作者)认为最有用和最重要的。所有的可以可以查看log4net文档:
- %date - 使用本地时区信息输出日期。也可以使用花括号和布局模式格式化日期,例如%date{MMMM DD,yyyy HH:mm:ss,fff}会输出"January 01,2011 14:15:43,767"。但是建议使用log4net的日期格式(ABSOLUTE,DATE或ISO8601)这样性能会比较好。
- %utcdate - 与%date相同,只是它输出世界时。
- %exception - 如果传递过来一个异常,它将会被使用,并在异常信息后插入新的一行。如果没有异常传递过来,这个实体会被忽略并且不会产生新的一行。这个转换模式通常放在日志实体的最后,并且在异常前面通常也会插入一行。
- %level - 这是我们指定的事件级别(DEBUG,INFO,WARN等)。
- %message - 我们传递到日志事件的消息。
- %newline - 这是新行实体。根据我们程序的平台翻译成新行字符。这是换行的首选方法,并且相对于特定的平台供应商没有性能问题。
- %timestamp - 程序运行以来的毫秒数。
- %thread - 实体产生的线程名(如果线程没有命名则是线程的编号)。
下面几个也非常有用,但是使用时要特别注意,因为它们会对性能造成影响:
- %identity - 使用Principal.Identity.Name方法获取的当前用户的用户名。
- %location - 在调试时特别有用,它将告诉我们日志方法是否被调用(行号、方法等)。然而 ,在Release模式下信息的数量将会减少,这取决于系统访问编译后的代码。
- %line - 代码的行号(即上面location的行号)。
- %method - 调用日志实体的方法(即上面location的方法)。
- %username - 输出WindowsIdentity属性的值。
有些配置文件使用的是字母而不是名字。这有利于整个实体。我(作者)在这里不会深度介绍,我们只要知道这些实体可以被格式化为合适的宽度。可以在两边添加空格,也可以将值截断来适应内部固定宽度的列。基本的语法是将数字或值放在%号和名字之间:
- X - 指定了字符的最小长度。如果字符的长度小于此值,将会在左侧填充空格。例如,%10message将会输出" hi"。
- -X - 和上面相同,不同的是在右侧填充空格。例如,%-10message将会输出"hi "。
- .X - 指定字符的最大长度。如果超出长度将会从左侧开始截取。例如,%.10message如果字符串为"Error entry"将会输出"rror entry"。
我们可以将上面的组合使用,例如"%10.20message",如果长度不足10则在左侧填充空格,如果超过20将会截取。
4.3.Filter:
Filter是appender的一部分。使用filter我们可以指定哪个(或哪些)级别被记录,甚至可以在消息中寻找关键字。Filter可以混合和匹配,当时当我们这样做的时候要特别注意。当消息符合filter的内部标准,它将会被记录并且filter的过程结束。这是Filter最大的缺陷,因此当我们处理复杂的filter时,filter的排序就变得非常重要。
4.3.1.StringMatchFilter:
字符串匹配filter在被记录的信息中寻找指定的字符串。我们可以知道多个字符串匹配filter。它们在一个查询语句中类似OR组合。需要注意的是不要试图匹配一个指定的字符串不排除一个实体(因为它可以继续下一个字符串匹配filter)。这意味着,我们可能会遇到没有匹配的情况。这种情况下,默认的行为是记录这个实体。因此,在字符串匹配filter设置的最后,包含一个否认所有filter(下面会介绍)在没有匹配时组织实体被记录是非常必要的。下面是字符串匹配filter的例子:
<filter type="log4net.Filter.StringMatchFilter"> <stringToMatch value="test" /> </filter>
4.3.2.LevelRangeFilter:
级别范围filter告诉系统只有在指定范围内的实体才会被记录。下面的例子中INFO、WARN、ERROR级别的事件会被记录,但是DEBUG事件不会被记录。我们在这个实体后面不需要定义否认所有filter,因为否定所有filter是默认的。
<filter type="log4net.Filter.LevelRangeFilter"> <levelMin value="INFO" /> <levelMax value="FATAL" /> </filter>
4.3.3.LevelMatchFilter:
级别匹配filter与级别范围filter类似,不同的是它只捕获指定的一种级别。它没有默认的否定所有filter,因此要在级别匹配filter之后添加否定所有filter。
<filter type="log4net.Filter.LevelMatchFilter"> <levelToMatch value="ERROR"/> </filter>
4.3.4.DenyAllFilter:
如果被忘记,appender通常不会按照所期望的工作。指定这个条目的唯一目的是指定不产生日志实体。如果只定义了这个条目,将不会有日志产生。
<filter type="log4net.Filter.DenyAllFilter" />
5.Appender:
每个类型的appender都有它自己的语法定义数据存储到哪里。最不寻常的是把日志存储到数据库。
5.1.Console Appender:
我(作者)通常使用这个appender来测试,但是它在产品上同样有用。它写入到输出窗口,如果我们使用的是console程序它会写入到命令窗口。下面的filter输出的值类似"2010-12-26 15:41:03,581 [10] WARN Log4NetTest.frmMain - This is a WARN test.",在它的末尾将会换行。
<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>
5.2.File Appender:
这个appender将会写入一个文本文件。该appender必须指定文件名(下面的例子中文件名为mylogfile.txt,将会存储在与可执行文件相同的路径),我们指定在文件末尾添加(而不是覆盖),并且指定FileAppender使用最小锁这样该文件可以被多个appender使用。
<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>
5.3.Rolling File Appender:
应该尽量使用该Appender代替file appender。Rolling file appender的目的是与file appender执行相同的功能,但是每个文件只存储一定量的数据。这样就不用担心日志耗尽存储空间。如果没有使用滚动选项,在足够时间的情况下,即使是一个小的应用程序也会耗尽存储空间。在下面的例子中使用于上面例子类似的方式记录日志,但是指定了每个文件的大小为10MB,并且在删除之前有5个存档文件(从最之前的文件开始删除)。存档文件具有相同的名字,只是在每个文件名字后添加一个点和数组(例如,第二个存档的文件名为mylogfile.txt.2).staticLogFileName确保日志文件与指定的文件名相同(本例中为mylogfile.txt)。
<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>
5.4.ADO.NET Appender:
下面的例子是写入SQL Server数据库,但是我们可以使用这种模式写入任意数据库。
注意:如果我们发现ADO.NET appender没有反应,查看bufferSize的值。这个值定义的是将日志写入数据库之前log4
net缓存的语句数量。
更多信息请参考:http://logging.apache.org/log4net/release/config-examples.html。
<appender name="AdoNetAppender" type="log4net.Appender.AdoNetAppender"> <bufferSize value="100" /> <connectionType value="System.Data.SqlClient.SqlConnection, System.Data, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> <connectionString value="data source=[database server]; initial catalog=[database name];integrated security=false; persist security info=True;User ID=[user];Password=[password]" /> <commandText value="INSERT INTO Log ([Date],[Thread],[Level],[Logger], [Message],[Exception]) VALUES (@log_date, @thread, @log_level, @logger, @message, @exception)" /> <parameter> <parameterName value="@log_date" /> <dbType value="DateTime" /> <layout type="log4net.Layout.RawTimeStampLayout" /> </parameter> <parameter> <parameterName value="@thread" /> <dbType value="String" /> <size value="255" /> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%thread" /> </layout> </parameter> <parameter> <parameterName value="@log_level" /> <dbType value="String" /> <size value="50" /> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%level" /> </layout> </parameter> <parameter> <parameterName value="@logger" /> <dbType value="String" /> <size value="255" /> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%logger" /> </layout> </parameter> <parameter> <parameterName value="@message" /> <dbType value="String" /> <size value="4000" /> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%message" /> </layout> </parameter> <parameter> <parameterName value="@exception" /> <dbType value="String" /> <size value="2000" /> <layout type="log4net.Layout.ExceptionLayout" /> </parameter> </appender>
四、代码:
在我们的程序中引入log4net的dll时,有三行代码是我们需要知道的。第一个是需要一次性放置在我们的类中。我(作者)通常它放在Program.cs文件的using语句下面:
[assembly: log4net.Config.XmlConfigurator(Watch = true)]
第二个是需要在每个类中用到的,定义一个变量用来调用log4net的方法。它使用System.Reflection调用后去当前类的信息:
private static readonly log4net.ILog log = log4net.LogManager.GetLogger (System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
最后的代码块是实际调用来记录一些信息。例如:
log.Info("Info logging");
注意,我们可以在后面添加一个可选参数包含异常信息。与下面的非常类似:
log.Error("This is my error", ex);
ex是异常对象。我们需要使用%exception来捕获异常信息。
五、记录额外的数据:
通常使用log4net的基本配置就可以为应用程序提供足够的信息。但是,有时我们想要使用标准方法记录更多的信息。例如,在ADO.NET appender中,我们可能想要添加用户名字段而不仅仅是信息字段。没有一种转换模式与应用程序中的用户名匹配。然而,我们可以使用上下文属性来自定义可以被appender访问的属性。例如:
log4net.GlobalContext.Properties["testProperty"] = "This is my test property information";
需要注意几点:1.自定义属性可以任意命名,但是如果命名与已经存在的命名相同,将会把原来的覆盖。2.本例中使用的是Global上下文,但是有四个上下文可以被利用。它们是基于线程的。Global定义的可以应用在程序进程、逻辑继承和进一步限制时间范围的任何地方。如果两个属性具有相同的名字,将会取窄范围的那个的值。
捕获自定义属性的例子:
<layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%date{ABSOLUTE} [%thread] %level %logger - %message%newlineExtra Info: %property{ testProperty}%newline%exception"/> </layout>
更多信息请查看:http://logging.apache.org/log4net/release/manual/contexts.html。
六、不使用app.config/web.config:
有时我们需要单独的文件存储log4net的配置信息。实际上这是存储配置信息最好的方法,因为我们可以有不同的配置副本应用于项目。这可以减少开发时间并且允许我们记录标准化的日志信息。如果要单独存储配置文件,我们需要更改程序的两个地方。第一件是将配置文件保存在另一个文件。第二件是修改调用设置,例如:
[assembly: log4net.Config.XmlConfigurator(ConfigFile = "MyStandardLog4Net.config", Watch = true)]
还可以使用另一种方法,这种方法需要把配置文件命名为程序集名称(包括扩展名):
[assembly: log4net.Config.XmlConfigurator(ConfigFileExtension = "mylogger", Watch = true)]
在上面的例子中如果我们的程序是test.exe,log4net的配置文件需要命名为test.exe.mylogger。
七、配置文件模板:
<!--This is the root of your config file--> <configuration> <!-- Level 0 --> <!--This specifies what the section name is--> <configSections> <!-- Level 1 --> <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/> <!-- Level 2 --> </configSections> <log4net> <!-- Level 1 --> <appender> <!-- Level 2 --> <layout> <!-- Level 3 --> <conversionPattern /> <!-- Level 4 --> </layout> <filter> <!-- Level 3 --> </filter> </appender> <root> <!-- Level 2 --> <level /> <!-- Level 3 --> <appender-ref /> <!-- Level 3 --> </root> <logger> <!-- Level 2 --> <level /> <!-- Level 3 --> <appender-ref /> <!-- Level 3 --> </logger> </log4net> </configuration>
发现已经有园友翻译过这篇文章,而且比我翻译的好很多:Log4Net指南