首页 > 代码库 > Log4Net学习【二】

Log4Net学习【二】

Log4Net结构详解

当我们在描述为系统做日志这个动作的时候,实际上描述了3个点;做日志,其实就是在规定,在什么地方 用什么日志记录器 以什么样的格式做日志。把三个最重要的点抽取出来,即什么地方,日志记录器,什么格式。在Log4net中,就使用了三个最重要的组件来描述这三个要素,即Logger:日志记录器、Appender:什么地方、 Layout:什么格式。 下面我们就分别来看看这三个对象在Log4net中起的重要作用和一些基本的规则用法。

1、Logger

之前我们在创建Logger的时候,都是使用LogManager.GetLogger("seven");方法来得到一个类绑定的日志记录器的。实际上,当我们说把一个日志记录器绑定在一个类上,这种说法是不准确的,正确的说,我们仅仅是使用给定的类的全限定名为Logger取了一个名字。这里请大家注意一下,Logger都是有名字的,假如我们除开rootLogger不谈,我们可以把Logger想象成一张表里面的数据,Logger对应的名字就是其主键,当两个Logger的名字相同,这两个Logger就是同一个Logger实例。我们可以简单的通过一个实例来验证:

View Code

控制台打印:30 [9] DEBUG  seven<null>  - true;说明Logger自身维护着每一个名字的Logger实例的引用,保证相同名字的Logger在不同地方获取到的实例是一致的,这样就允许我们在统一的代码中配置不同Logger的特性。

另外,Logger的层次结构,也是靠Logger的名字来区分的,比如:名称为DoNet的Logger就是DoNet.Seven的父Logger;DoNet.Seven是DoNet.Seven.Song的父Logger;Logger的体系结构和命名空间的结构划分类似,使用.来区分;所以我们前面才说,使用类的全限定名是最简单,也是最符合logger的体系结构的命名方式。当然,你也可能使用任何的能想到的方式去处理Logger的命名;这也是可以的。

看到这里,可能有的人会提出疑问,那既然Logger的层次结构是按照Logger的名字来创建的,那在创建Logger的时候,是否必须按照其结构来顺序创建Logger?比如:

 ILog log = LogManager.GetLogger("DoNet"); 

 ILog log = LogManager.GetLogger("DoNet.Seven"); 

 ILog log = LogManager.GetLogger("DoNet.Seven.Song");

是否必须要按照这个顺序创建呢?不需要。在做前面的示例的时候,我们说到,大家可能认为当我们通过ILog log = LogManager.GetLogger("DoNet.Seven.Song");的时候,Log4J其实为我们创建了DoNet;DoNet.Seven和DoNet.Seven.Song这三个Logger;其实不然,如果是这样的话,Log4net可能会产生非常多不必要的Logger。所以,真正的方式应该是,当我通过ILog log = LogManager.GetLogger("DoNet.Seven.Song");的时候,Log4net仅仅为我们创建了DoNet.Seven.Song这个Logger;而当我们再次使用ILog log = LogManager.GetLogger("DoNet.Seven");的时候,Log4net才会为我们创建DoNet.Seven这个Logger,并且Log4net会自动的去寻找这个Logger的上下级关系,并自动的把这个新创建的Logger添加到已有的Logger结构体系中。

 在Logger中,非常重要的一个组件,就是Logger的日志级别,即Level。关于Level的划分,使用,我们在前面的例子中已经大概了解到了,下面来看看完整的Level的定义和其在Logger体系中的继承方式,这是一个很简单的概念。
首先,在Log4net中,为我们默认的定义了7种不同的Level级别,即all<debug<info<warn<error<fatal<off;而这7中Level级别又刚好对应着Level类中的7个默认实例:Level.ALL;Level.DEBUG;Level.INFO;Level.WARN;Level.ERROR;Level.FATAL和Level.OFF。我们在之前也只看到了其中的debug到fatal这5种;这五种日志级别正好对应着Logger中的五个方法,即:
 void Debug(object message);

 void Info(object message);

 void Warn(object message);

 void Error(object message);

 void Fatal(object message);

当然也对应着一些扩展方法,方便使用,这里不列举了。Log4Net学习【一】我们已经介绍了Logger的基本使用,以及Level的使用和限制。其实,在真正使用Log4net的时候,我们一般都不需要使用代码的方式去配置Level,这些配置更多的时候是使用配置文件来完成的,这个后面会详细介绍。但是,不管使用什么样的配置文件,最终也会解释成这样的配置代码,所以,理解了这些代码,再去使用配置文件,会更加清楚到底配置文件在干什么

2、Appender

使用Logger的日志记录方法,仅仅是发出了日志记录的事件,具体日志要记录到什么地方,需要Appender的支持。在Log4net中,Appender定义了日志输出的目的地。在上面所有的示例当中,我们日志输出的目的地都是控制台,在Log4j中,还有非常多的Appender可供选择,可以将日志输出到文件,网络,数据库等等,这个后面再介绍。说到这里,可能有人就已经会思考,既然Logger对象的info()等方法仅仅是发出了日志记录的事件,还需要指定输出目的地;那么我们之前的示例代码也并没有为任何一个Logger设置Appender啊?其实这很好理解,我们回顾一下之前的Level,按道理,也应该为每一个Logger指定对应的日志输出级别,但是我们也并没有这样做,正是因为Logger本身存在一个完整的体系结构,而Level能够在这个结构中自下而上的继承。同理,Appender也具有这种继承的特性.

 log4net.Config.BasicConfigurator.Configure();            ILog log = LogManager.GetLogger("DoNet.Seven");            log.Logger.Repository.Threshold = Level.Info;            #region 给log添加一个Append            IBasicRepositoryConfigurator configurableRepository = log.Logger.Repository as IBasicRepositoryConfigurator;            SimpleLayout layout=new SimpleLayout();            FileAppender appender = new FileAppender(layout, "test.log");            configurableRepository.Configure(appender);                        #endregion            ILog log2 = LogManager.GetLogger("DoNet.Seven.Song");            log.Debug("debug");            log.Info("Info");            log.Error("Error");            log2.Debug("Debug2");            log2.Info("Info2");            log2.Error("Error2");            Console.ReadKey();
View Code

结果如下:

这里文件中的输出明显已经和控制台的输入不一致了,在这里说明了两个问题:

 

  

  

Log4Net学习【二】