很多技术在项目中用过,但是都是别人封装或搭建好的,自己没有从头到尾梳理过;直接调用或者直接复制到其它项目,也没有发现啥问题,没有踩坑的经历很多东西都会被忽略,log4net就是其中之一。由于好久没有配置过Log4net了,差不多都遗忘了,最近就写了一个基于MVC的小demo从零开始试试log4net,也记录一下学习过程和遇到的问题。
Log4net安装与配置
1、创建项目后用nuget直接安装log4net包
2、log4net配置
log4net配置可以直接写在系统配置文件中比如app.config、web.config,我为了方便修改和展示,也为了以后方便复用,就抽出来自定义一个配置文件log4net.config。
Log4net 有四种主要的组件,分别是Logger(记录器)、Repository(库)、Appender(附着器)以及Layout(布局)。
先放上log4net.config配置文件代码。
注:root节点的配置根据实际情况处理,否则可能会遇到无法写入日志或者重复写入日志问题
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<!--<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
</configSections>-->
<!-- Level的级别,由高到低 -->
<!-- OFF > Fatal > ERROR > WARN > DEBUG > INFO > ALL-->
<!-- 解释:如果level是ERROR,则在cs文件里面调用log4net的info()方法,则不会写入到日志文件中-->
<log4net>
<!--根记录器,所有Logger都默认继承它
如果没有定义根记录器,调用GetLogger没有找到定义的Logger时不会在日志文件写入日志
-->
<root>
<level value="ALL" />
<!--错误日志附着器引用
由于所有记录器都默认继承root,所以此附着器引用对所有的记录器都生效,因此如下配置的情况,调用错误日志记录器时会记录2次错误日志
-->
<appender-ref ref="ErrorAppender" />
<!--<appender-ref ref="ADONetAppender" />-->
</root>
<!--错误日志记录器-->
<logger name="logerror">
<level value="ALL" />
<!--定义记录的日志级别-->
<appender-ref ref="ErrorAppender" />
<!--记录到哪个介质中去-->
</logger>
<!--信息日志记录器-->
<logger name="loginfo">
<level value="ALL" />
<appender-ref ref="InfoAppender" />
<appender-ref ref="ADONetAppender" />
</logger>
<!--错误日志附着器-->
<appender name="ErrorAppender" type="log4net.Appender.RollingFileAppender">
<!-- name属性指定其名称,type则是log4net.Appender命名空间的一个类的名称,意思是,指定使用哪种介质-->
<param name="File" value="Log\LogError\" />
<!--日志输出到exe程序这个相对目录下-->
<param name="AppendToFile" value="true" />
<!--输出的日志不会覆盖以前的信息-->
<param name="MaxSizeRollBackups" value="100" />
<!--备份文件的个数-->
<param name="MaxFileSize" value="10240" />
<!--当个日志文件的最大大小-->
<param name="StaticLogFileName" value="false" />
<!--是否使用静态文件名-->
<param name="DatePattern" value="yyyyMMdd".txt"" />
<!--日志文件名-->
<param name="RollingStyle" value="Date" />
<!--文件创建的方式,这里是以Date方式创建-->
<!--错误日志布局-->
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%d [%t] %-5p %c %n%m%n" />
</layout>
</appender>
<!--信息日志附着器-->
<appender name="InfoAppender" type="log4net.Appender.RollingFileAppender">
<!--文件路径-->
<param name="File" value="Log\LogInfo\" />
<!--所有新的日志都进行追加-->
<param name="AppendToFile" value="true" />
<param name="MaxFileSize" value="10240" />
<param name="MaxSizeRollBackups" value="100" />
<!--文件名称不固定,可变-->
<param name="StaticLogFileName" value="false" />
<!--文件名称格式-->
<param name="DatePattern" value="yyyyMMdd".txt"" />
<!--文件名按日期滚动-->
<param name="RollingStyle" value="Date" />
<!--信息日志布局-->
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%d [%t] %-5p %c %n%m%n" />
</layout>
</appender>
<!--数据库日志附着器-->
<appender name="ADONetAppender" type="log4net.Appender.ADONetAppender,log4net">
<!--BufferSize为缓冲区大小,只有日志记录超设定值才会一块写入到数据库-->
<bufferSize value="1" />
<!--或写为<param name="BufferSize" value="1" />-->
<!--引用-->
<connectionType value="System.Data.SqlClient.SqlConnection, System.Data, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<!--连接数据库字符串-->
<connectionString value="data source=.;database=Test2;user id=sa;password=123456;Enlist=true;Pooling=true;Max Pool Size = 512;" />
<!--插入到表Log-->
<commandText value="INSERT INTO Log ([Date],[Thread],[Level],[Logger],[Message],[Location],[Exception]) VALUES (@log_date,@thread, @log_level, @logger,@message,@location,@exception)" />
<!--日志记录时间,RawTimeStampLayout为默认的时间输出格式 -->
<parameter>
<parameterName value="@log_date" />
<dbType value="DateTime" />
<layout type="log4net.Layout.RawTimeStampLayout" />
</parameter>
<parameter>
<parameterName value="@thread" />
<dbType value="String" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%thread" />
</layout>
</parameter>
<parameter>
<parameterName value="@Logger" />
<dbType value="String" />
<size value="100" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%logger" />
</layout>
</parameter>
<parameter>
<parameterName value="@log_level" />
<dbType value="String" />
<size value="100" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%level" />
</layout>
</parameter>
<parameter>
<parameterName value="@message" />
<dbType value="String" />
<size value="100" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%message" />
</layout>
</parameter>
<parameter>
<parameterName value="@location" />
<dbType value="String" />
<size value="4000" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%location" />
</layout>
</parameter>
<parameter>
<parameterName value="@exception" />
<dbType value="String" />
<size value="4000" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%exception" />
</layout>
</parameter>
</appender>
</log4net>
<!--log4net调式相关配置 需要放到web.config中才会生效 start-->
<appSettings>
<!--开启log4net调式-->
<add key="log4net.Internal.Debug" value="true"/>
</appSettings>
<!--log4net调式监听-->
<system.diagnostics>
<trace autoflush="true">
<listeners>
<add name="textWriterTraceListener" type="System.Diagnostics.TextWriterTraceListener" initializeData="Log\log4net.txt" />
</listeners>
</trace>
</system.diagnostics>
<!--log4net调式相关配置 需要放到web.config中 end-->
<system.web>
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5" />
</system.web>
</configuration>
可以看到主要包括2个节点configSections和log4net
1)configSections节点是对log4net配置节点的声明,如果没有这个节点会如下报错:
无法读取配置节“log4net”,因为它缺少节声明。
注:如果log4net的配置放在自定义的配置文件里,这个声明是可以省略的
2)所有关于log4net的配置都包含在log4net节点中。(要重点注意root(根记录器)和logger(记录器)之间的关联关系)
3、关联log4net的配置文件
需要在2个cs文件中指定log4net的配置文件,分别是AssemblyInfo.cs及Global.asax.cs
1) AssemblyInfo.cs
如果是系统配置web.config,加入以下代码,也可以按自定义配置文件的方式处理。
[assembly: log4net.Config.XmlConfigurator(Watch = true)]
自定义配置文件需指定具体文件名,加入以下代码
[assembly: log4net.Config.XmlConfigurator(ConfigFile = "log4net.config", Watch = true)]
2) Global.asax.cs
在全局文件中加入以下代码
注:如果在AssemblyInfo.cs中指定了配置文件且文件放在根目录下,以下代码是可以省略的
//配置log4
log4net.Config.XmlConfigurator.Configure(new System.IO.FileInfo(Server.MapPath("~/log4net.config")));
4、测试写入日志
做好以上3个步骤后,就可以开始测试写入日志了,写入日志分为2个步骤
1、首先通过GetLogger方法获取Logger(记录器),方法参数为记录器的名称.
2、调用记录器各级别方法写入日志
获取记录器有如下2种情况:
1、传入配置文件中定义好的记录器名称获取记录器,代码如下:
private ILog logError = LogManager.GetLogger("logerror");
2、也可以传入相关调用处的类名,3中方式任选其一,代码如下:
注:如果是使用这种方式,就一定要配置root根记录器,否则你会发现只创建了空的日志文件,没有往日志文件中写入日志
private ILog log = LogManager.GetLogger("DefaultController");
private ILog log = LogManager.GetLogger(typeof(DefaultController));
private ILog logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
从日志文件中可以看到,”测试错误记录器”这个日志写入了2次。这是因为我们定义了root根记录器和logerror记录器,而且所有记录器默认都继承root,所以当我们调用logerror记录器时相当于是logerror+root的效果,从下面的代码中可以看到,root和logerror都引用了ErrorAppender附着器,所以写入了2次日志。
如果不定义root根记录器,你用类名GetLogger获取记录器时会发现无法写入日志。
<root>
<level value="ALL" />
<!--错误日志附着器引用
由于所有记录器都默认继承root,所以此附着器引用对所有的记录器都生效,因此如下配置的情况,调用错误日志记录器时会记录2次错误日志
-->
<appender-ref ref="ErrorAppender" />
<!--<appender-ref ref="ADONetAppender" />-->
</root>
<!--错误日志记录器-->
<logger name="logerror">
<level value="ALL" />
<!--定义记录的日志级别-->
<appender-ref ref="ErrorAppender" />
<!--附着器引用-->
</logger>
到此处就完成了log4net配置,并成功写入了日志,下面再简单说下配置文件里各节点的关系。
log4net节点介绍
从配置文件中可以看到,我们主要用了3种节点root(根记录器)、logger(记录器)、appender(附着器)。
从数量上看,我们只定义了1个root节点、2个logger节点(错误日志记录器、信息日志记录器),3个appender节点(错误日志附着器、信息日志附着器、数据库日志附着器),这让我们知道我们可以定义多个logger记录器、多个appender附着器。
logger记录器
我们再看loginfo记录器,包含2种节点level(日志级别)、appender-ref(附着器引用)。
其中Level的级别,由高到低OFF> Fatal > ERROR > WARN > DEBUG > INFO > ALL,大于等于你配置级别的日志才能写入,比如你level节点value配置成DEBUG,则INFO级别日志不会写入。
appender-ref节点则表示附着器引用,一个记录器可以引用多个附着器,我们上面loginfo记录器就配置了写入日志文件,同时也写入数据库。
appender附着器
最后我们再看看数据库日志附着器ADONetAppender,里面有用到layout布局来获取插入数据库各字段的值,我们用的都是log4net自带的布局PatternLayout,能获取到一些常用信息,比如记录器名称%logger、信息%message,如果你要使用一些自定义字段,需要自己在代码中自定义布局,可参考Log4Net写入到数据库配置过程中的一些小问题备忘。常用信息如下所示:
%m(message)输出日志消息
%n(newline)换行
%d(datetime)输出当前语句运行的时刻
%r(runtime)输出当前语句的运行时刻
%t(thread id)当前语句所在的线程ID
%p(priority)日志的当前优先级别,DEBUG INFO ……
%c(class)当前日志对象的名称
%L 输出语句所在的行号
%F 输出语句所在的文件名
%-数字 表示该项的最小长度,如果不够,用空格填充
ADONetAppender节点下commandText节点是配置插入语句,需要在数据库中建好对应的表,数据库字段可以多于插入语句中的字段,但是不能少。创建表的语句:
CREATE TABLE [dbo].[Log]( [ID] [int] IDENTITY(1,1) NOT NULL, [Date] [datetime] NOT NULL, [Thread] [varchar](100) NULL, [Level] [varchar](100) NULL, [Logger] [varchar](200) NULL, [Message] [text] NULL, [Location] [text] NULL, [Exception] [text] NULL ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] GO
对于插入数据库相关附着器的配置,也可以直接查看bin下log4net.xml文件,这个文件是安装log4net包后在bin下生成的,提供了一个sql server相关附着器的例子,可以直接复制这个附着器,也能节省一点找资料的时间
log4net调试
最后再提一下log4net调试,当log4net出问题时可以看看里面记录的信息,如配置文件读取不到、记录器无法创建等等一些简单问题还是可以从中获取到的,至少比出问题时一脸蒙,无从下手要强。
需要在系统配置文件web.config中加入如下代码:
<appSettings>
<!--开启log4net调式-->
<add key="log4net.Internal.Debug" value="true"/>
</appSettings>
<!--log4net调式监听-->
<system.diagnostics>
<trace autoflush="true">
<listeners>
<add name="textWriterTraceListener" type="System.Diagnostics.TextWriterTraceListener" initializeData="Log\log4net.txt" />
</listeners>
</trace>
</system.diagnostics>
根据配置的路径查看log4net.txt文件,如下:
参考: