1 Log4net的结构
Log4net 有四种主要的组件,分别是logger(记录器)、Repository(库)、Appender(附着器)以及Layout(布局)。
1.1 Logger
Logger是应用程序需要交互的主要组件,它用来产生日志消息。产生的日志消息并不直接显示,还要预先经过Layout的格式化出来后才会输出。
Logger提供了多种方式来记录一个日志消息,你可以在你的应用程序里创建多个Logger,每个实例化的Logger对象都被log4net框架作为一个命名实体(named entity)来维护。这就意味着为了重用Logger对象,你不必将它在不同的类或对象之间传递,只需要用它的名字作为参数调用就可以了。Log4net框架使用继承体系,继承体系类似于.NET中的名字空间,也就是说,如果哦有两个logger,分别被定义为a.b.c 和a.b,那么我就说a.b是a.b.c的祖先。每一个Logger都集成了祖先的属性。
Logger框架定义了一个ILog接口,所有的logger类都必须实现这个接口。如果哦你想实现一个自定义的logger,你必须首先实现这个接口。
ILog接口的定义如下:
public interface Ilog
{
void Debug(object message);
void Info(object message);
void Warn(object message);
void Error(object message);
void Fatal(object message);
//以上每个方法都有一个重载的方法,用来支持异常处理。重载方法如下所示:
void Debug(object message,Except ex);
void Info(object message,Except ex);
void Warn(object message,Except ex);
void Error(object message,Except ex);
void Fatal(object message,Except ex);
//Boolean属性用来检查Logger的日志级别,如下所示:
bool isDebugEnabled;
bool isInfoEnabled;
bool isWarngEnabled;
bool isErrorEnabled;
bool isFatalEnabled;
}
Log4net框架定义一个叫LogManager的类,用来管理所有的logger对象。他有一个GetLogger()静态方法,用我们提供的名字参数来检索已经存在Logger对象(保存在配置文件中)。如果框架里面不存在该Logger对象,它也会为我们创建一个Logger对象。代码如下所示:
Log4net.Ilog log = log4net.LogManager.GetLogger(LoggerName);
通常来说,我们会为类的类型为参数调用GetLogger(),以便跟踪我们在进行日志记录的类,传递类的类型可以用typeof(ClassName)方法来获得,或者可以用如下的反射方法来获得:
System.Reflection.MethodBase.GetCurrentMethod().DeclaringType,尽管符号长了一些,但是后者可以用于一些场合,比如获去调用方法的类的类型。
1.1.1 日志级别
正如在ILog的接口中看到的一样,有五中不同的方法可以跟踪一个应用程序。事实上这五中方法是运作在Logger对象设置的不通日志优先级别上。这几种不通的级别是作为常量定义在log4net.spi.Level类中。你可以在程序中使用任何一种方法。但是在最后的发布中,你也许不想让所有的代码来浪费你的cpu周期,因此,框架提供了7中级别和对象的Boolean属性来控制日志的记录的类型。
表1-1 Logger的日志级别
级别 | 允许方法 | Boolean属性 | 优先级别 |
OFF | 拒绝所有 |
| Height |
FATAL | Void Fatal(…); | bool isFatalEnabled |
|
ERROR | VoidError(…); | bool isErrorEnabled |
|
WARN | Void Warn (…); | bool isWarnEnabled |
|
INFO | Void Info(…); | bool is InfoEnabled |
|
DEBUG | VoidDebug(…); | bool isDebugEnabled |
|
ALL | 允许所以 |
| Lowest |
在log4net框架里,通过设置配置文件,每个日志对象都被分配一个日志优先级别,如没有给一个日志对象显式地分配一个级别,那么改对象会试图从他的祖先继承一个级别的。
ILog接口的每个方法都有一个预先定义好了的界别。正如表1-1看到的,ILog的Info()方法具有INFO级别。同样的,以此类推,Error()方法具有ERROR级别。当我们使用以上任何一种方法时,log4net框架会检查日志对象logger的级别和方法的级别。只有当方法的级别高于日志级别时,日志请求才会被接受并执行。
1.2 Respository
Respository主要用于负责日志对象组织结构的维护。在log4net的以前版本中,框架仅支持分等级的组织结构(hierarchical organization)。这种等级结构本质上是库的一个实现,并且定义在log4net.Respository.Hierarchy名字空间中。要实现一个Respository,需要
实现log4net.Respository.ILoggerRespository接口。但是通常并不是直接实现该接口,而是以log4net.Respository.LoggerRespositorySkeleton为基类继承体系库(hierarchical respository)则由log4net.Respository.Hierarchy.Hierarchy类实现。
如果你是log4net框架的使用者,而非扩展者,那么你几乎不会在你的代码中用到Respository的类。相反的,你需要用到LogManager类来自动管理库和日志对象。
1.3 Appender
一个好的日志框架应该能够产生许多目的地的输出。如输出控制台,保存文件,或者存入数据库等。Log4net能够很好的满足这些要求。它是使用一个叫做Appender的组件来定义输出介质。正如名字所示,这些组件把他们附加到Logger日志组件上并将输出传递到输出流中。你可以把多个Appender组件附加到一个日志对象上。Log4net框架提供了几个Appender组件。
1.4 Appender Filters
一个Appender对象缺省地将所有的日志时间传递到输出流。Appender的过滤器(Appender Filters)可以按照不同的标准过滤日志事件。在log4net.Filter的名字控件下已经有几个预定义的过滤器。使用这些过滤器,你可以按照日志级别范围过滤日志时间,或者按照某个特殊的字符串进行过滤。你可以在API的帮助文件中发现更多有关Filter的信息。
1.5 Layout
Layout组件用于向用户显示最后经过格式化的输出信息。输出信息可以一多种格式显示,主要依赖与我们采用的layout组件的类型。可以是线性的或者XML文件。Layout组件和一个Appender组件一起工作哦。API帮助手册中有关于不通Layout组件的列表。一个Appender对象,只能对象一个Layout对象。要实现你自己的Layout类,你需要从log4net.Layout.LayoutSkeleton类继承,它实现了ILayout接口。
2 定义配置文件
配置信息可以放在如下几中形式文件的一种中。
1.在程序的配置文件里,如AssemblyName.Config 或 web.config.
2.在自己的文件里,文件名可以自取。
Log4net框架会在相对于AppDomain.CurrentDomain.Base.Directory属性定义的目录路径下查找配置文件。框架在配置文件里要查找的唯一表示是<log4net>标签。一个完整的配置文件(命名为:Log.config)的例子如下:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name ="log4net" type ="log4net.Config.Log4NetConfigurationSectionHandler,log4net-net-1.0"/>
</configSections>
<log4net>
<logger name="ManagerLog">
<level value="ALL"/>
<appender-ref ref ="LogFileAppender"/>
<appender-ref ref ="ADONetAppender"/>
</logger>
<!--<root>
<level value="ALL" />
<appender-ref ref="ADONetAppender"/>
</root>-->
<appender name="LogFileAppender" type="log4net.Appender.RollingFileAppender">
<file value="log.log"/>
<appenderToFile value="true"/>
<rollingstyle value="Date"/>
<datePattern value="yyyyMMdd"/>
<encoding value="utf-8"/>
<param name="MaxSizeRollBackups" value="5" />
<param name="MaximumFileSize" value="10MB" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%d [%-5level]: %m%n" />
</layout>
<!--<filter type="log4net.Filter.LevelRangeFilter">
<levelMin value ="OFF"/>
<levelMax value ="ALL"/>
</filter>-->
</appender>
<appender name ="ADONetAppender" type="log4net.Appender.ADONetAppender">
<bufferSize value ="10"/>
<connectionType value ="System.Data.SqlClient.SqlConnection,System.Data,Version=1.0.3300.0,Culture=neutral,publicKeyToken=b77a5c561934e089"/>
<connectionString value="server=SUNLIKE\SQLEXPRESS;database=blank;user id = sa;password = sa;timeout=300;"/>
<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"/>
<!--<conversionPattern value ="%t"/>-->
</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>
</configuration>
如果Log.config存放在debug目录下,需要在Assembly.cs 末尾添加代码:
[assembly: log4net.Config.XmlConfigurator(ConfigFile = @"Log.config", Watch = true)]
如果Log.config存放在其他目录下,则需要在Assembly.cs 末尾添加代码:
[assembly: log4net.Config.XmlConfigurator(Watch = true)](注:如果在app.config 或者web.config 只要写这行代码即可)
还需要在Program.cs中添加代码(确保找到配置文件):
string path = @" ";//存放Log.config的路径
FileInfo file = new FileInfo(path+ @"Log.config");
if (File.Exists(file.ToString()))
{
log4net.Config.XmlConfigurator.Configure(file);
}
else
{
throw new Exception("log4net日志配置文件未找到");
}
在程序中自定定义一个类ManagerLog类,对log4net的方法进行重写。
public class ManageLog
{
#region 字段定义
private static ILog logEntry = LogManager.GetLogger("ManagerLog");
//ManagerLog 与配置文件中的Logger节中name属性的值保持一致
// private static object objectMultx = new object();
//private static string logname = "log";
#endregion
#region 公共接口
/// <summary>
/// 输出程序调试信息
/// </summary>
/// <param name="message">输出信息</param>
public static void Debug(string message)
{
logEntry.Debug(message);
}
/// <summary>
/// 输出程序错误信息
/// </summary>
/// <param name="message">输出信息</param>
public static void Info(string message)
{
logEntry.Info(message);
}
/// <summary>
/// 输出程序错误信息
/// </summary>
/// <param name="message">输出信息</param>
public static void Warn(string message)
{
logEntry.Warn(message);
}
/// <summary>
/// 输出程序警告信息
/// </summary>
/// <param name="message">输出信息</param>
public static void Error(string message)
{
logEntry.Error(message);
}
/// <summary>
/// 输出程序致命错误信息
/// </summary>
/// <param name="message">输出信息</param>
public static void Fatal(string message)
{
logEntry.Fatal(message);
}
#endregion
}
在需要写日志的地方加上相应的调用语句,如:
ManageLog.Debug("记录时间……");
2.1 布局样式:
Class | Description |
ExceptionLayout | 只呈现日志事件中异常的文本信息 |
Layout2RawLayoutAdapter | 适应于Ilayout接口到IRawLayout |
LayoutSkeleton | 扩展这个抽象类来创建自定义的布局格式 |
PatternLayout | 可以通过类型字符串来配置的布局 |
PatternLayout.ConverterInfo | 把转换器名字映射为转换器类型的包装类 |
RawLayoutConverter | IrawLayout接口的类型转换器 |
RawPropertyLayout | 从日志事件中提取属性值 |
RawTimeStampLayout | 从日志事件中提取日期 |
RawUtcTimeStampLayout | 从日志事件中提取日期 |
SimpleLayout | 很简单的布局 |
XmlLayout | 把日志事件格式化为XML元素的布局 |
XmlLayoutBase | 把日志事件格式化为XML元素的布局 |
XmlLayoutSchemalLog4j | 把日志事件格式化为与log4j结构兼容的XML元素布局 |
2.2 一些说明
(一)log4配置说明
log4net的配置可以放在应用程序的默认配置文件中(app.config或web.config),也可以再你自己的配置文件中。(如果log4net的配置不是放在应用程序的配置文件里,而是在自己定义的文件里,<configSection>节点里的<section>节点是不需要的。
(二)节点分析
在配置章节中,定义section节点。节点名称为:须为log4net,且大小写敏感。
<configSections>
<section name="log4net"
type="log4net.Config.Log4NetConfigurationSectionHandler" />
</configSections>
(三)Log4net章节内容
debug 可选,取值是true或false,默认是false。设置为true,开启log4net的内部调试。
update 可选,取值是Merge(合并)或Overwrite(覆盖),默认值是Merge。设置为Overwrite,在提交配置的时候会重置已经配置过的库。
threshold 可选,取值是repository(库)中注册的level,默认值是ALL。
支持的子元素:
·appender 0或多个
·logger 0或多个
·renderer 0或多个
·root 最多一个
·param 0或多个
(四)两个Appenders(输出源)
定义日志的输出方式,只能作为 log4net 的子元素。name属性必须唯一,type属性必须指定
支持的属性:
name 必须的,Appender对象的名称
type 必须的,Appender对象的输出类型
支持的子元素:
·appender-ref 0个或多个,允许此appender引用其他appender,并不是所以appender类型都支持。
·filter 0个或多个,定义此app使用的过滤器。
·layout 最多一个。定义appender使用的输出格式。
·param 0个或多个, 设置Appender类中对应的属性的值。
·LogFileAppender
节点日志文件输出。type="log4net.Appender.FileAppender"
·ConsoleAppender
节点控制台输出。type="log4net.Appender.ConsoleAppender"
(五)一个root
根logger,所有其它logger都默认继承它。root元素没有属性。
支持的子元素:
·appender-ref 0个或多个,要引用的appender的名字。
·level 最多一个。 只有在这个级别或之上的事件才会被记录。
·param 0个或多个, 设置一些参数。
<root>
<level value="WARN" />
<appender-ref ref="LogFileAppender" />
<appender-ref ref="ConsoleAppender" />
</root>
(六)Logger
支持的属性:
name 必须的,logger的名称
additivity 可选,取值是true或false,默认值是true。设置为false时将阻止父logger中的appender。
支持的子元素:
·appender-ref 0个或多个,要引用的appender的名字。
·level 最多一个。 只有在这个级别或之上的事件才会被记录。
·param 0个或多个, 设置一些参数。
<logger name="testApp.Logging">
<level value="DEBUG"/>
</logger>
(七)Layout
布局,只能作为<appender>的子元素。
支持的属性:
type 必须的,Layout的类型
支持的子元素:param 0个或多个, 设置一些参数。
(八)Filter
过滤器,只能作为<appender>的子元素。
支持的属性:
type 必须的,Filter的类型
支持的子元素:param 0个或多个, 设置一些参数。
(九)Param
<param>元素可以是如何元素的子元素。
支持的属性:
name 必须的,取值是父对象的参数名。
value 可选的,value和type中,必须有一个属性被指定。value是一个能被转化为参数值的字符串。
type 可选的,value和type中,必须有一个属性被指定。type是一个类型名,如果type不是在log4net程序集中定义的,就需要使用全名。
支持的子元素:param 0个或多个, 设置一些参数
2.3 模式转化
%m(message)输出日志消息
%n(newline)换行
%d(datetime)输出当前语句运行的时刻
%r(runtime)输出当前语句的运行时刻
%t(thread id)当前语句所在的线程ID
%p(priority)日志的当前优先级别,DEBUG INFO ……
%c(class)当前日志对象的名称
%L 输出语句所在的行号
%F 输出语句所在的文件名
%-数字 表示该项的最小长度,如果不够,用空格填充
结束语
通过这两天的学习对log4net组件的配置有了一定的了解,能进行简单的日志输出,不仅能够在console显示,也可以导入到文本文件里,还可以导入到数据库中。方便简单,在开发中是一个很不错的助手。在学习的过程中也发现不少问题,比如做一个控件,该控件有写日志的功能,日志写入到sql server 2005中,仅当该控件销毁之后,才能一次写入数据库,即如果该控件还在活动,在数据库中是查不出刚刚添加的日志记录。具体原因不太清楚,也没有深究,我估计日志是当成一个事物来处理了。另外还有一个问题,就是目前还没有找到一种行之有效的方法写入oracle10g 或oracle 11g的方法。如果有朋友看到该疑问,请帮忙指正和赐教。网上也有很多资料,本篇篇文章写的比较肤浅,是参照百度文库的一篇文章和csdn一些n人的博客,结合自己的实际开发,总结而成,如有文章中有什么不妥之处,还请指正。不胜感激。