一、日志的级别:java
咱们如今要调用logger的方法,不过在这个Logger对象中,有不少方法,因此要先了解log4j的日志级别,log4j规定了默认的几个级别:trace<debug<info<warn<error<fatal等。这里要说明一下:程序员
1)级别之间是包含的关系,意思是若是你设置日志级别是trace,则大于等于这个级别的日志都会输出。apache
2)基本上默认的级别没多大区别,就是一个默认的设定。你能够经过它的API本身定义级别。app
3)简单介绍一下不一样的级别的含义:测试
trace: 追踪,就是程序推动一下,你就能够写个trace输出,因此trace应该会特别多,通常设置最低日志级别不让他输出。线程
debug: 调试,通常就只用这个做为最低级别。debug
info: 输出感兴趣的或者重要的信息。调试
warn: 有些信息不是错误信息,可是也要给程序员的一些提示。日志
error: 错误信息。code
fatal: 级别比较高。重大错误,这种级别你能够直接中止程序了,是不该该出现的错误!
二、日志调用:
log4j的核心在配置文件上。
import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; public class Hello { static Logger logger = LogManager.getLogger(Hello.class.getName()); public boolean hello() { logger.entry(); //trace级别的信息,单独列出来是但愿你在某个方法或者程序逻辑开始的时候调用,和logger.trace("entry")基本一个意思 logger.error("Did it again!"); //error级别的信息,参数就是你输出的信息 logger.info("我是info信息"); //info级别的信息 logger.debug("我是debug信息"); logger.warn("我是warn信息"); logger.fatal("我是fatal信息"); logger.log(Level.DEBUG, "我是debug信息"); //这个就是制定Level类型的调用:谁闲着没事调用这个,也不必定哦! logger.exit(); //和entry()对应的结束方法,和logger.trace("exit");一个意思 return false; } }
若是没有自定义配置文件,上面这个类在写一个main方法,控制台会输出:
Did it again! 我是fatal信息
由于Log4j有一个默认的配置,它的日志级别是ERROR。若是日志级别改为了TRACE,控制台会输出:
entry Did it again! 我是info信息 我是debug信息 我是warn信息 我是fatal信息 我是debug信息 exit
3. 配置文件:
原本觉得Log4J 2应该有一个默认的配置文件的,不过好像没有找到,下面这个配置文件等同于缺省配置:
<?xml version="1.0" encoding="UTF-8"?> <configuration status="OFF"> <appenders> <Console name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/> </Console> </appenders> <loggers> <root level="error"> <appender-ref ref="Console"/> </root> </loggers> </configuration>
咱们只要把configuration>loggers>root的level属性改成trace,就能够输出刚才写的全部信息了。Log4J传统的配置一直是.properties文件,键值对的形式,那种配置方式很很差看,可是基本上咱们从这个配置文件也能看到Log4J 1的影子,无非是appender了,layout之类的,含义也基本同样的。
第一个例子:
<?xml version="1.0" encoding="UTF-8"?> <configuration status="OFF"> <appenders> <Console name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/> </Console> </appenders> <loggers> <!--咱们只让这个logger输出trace信息,其余的都是error级别--> <!-- additivity开启的话,因为这个logger也是知足root的,因此会被打印两遍。 不过root logger 的level是error,为何Bar 里面的trace信息也被打印两遍呢 --> <logger name="cn.lsw.base.log4j2.Hello" level="trace" additivity="false"> <appender-ref ref="Console"/> </logger> <root level="error"> <appender-ref ref="Console"/> </root> </loggers> </configuration>
先简单介绍一下下面这个配置文件。
1)根节点configuration,而后有两个子节点:appenders和loggers(都是复数,意思就是能够定义不少个appender和logger了)
2)appenders:这个下面定义的是各个appender,就是输出了,有好多类别,这个例子只有一个Console,这些节点可不是随便命名的,Console就是输出控制台的意思。而后就针对这个输出设置一些属性,这里设置了PatternLayout就是输出格式了,基本上是前面时间,线程,级别,logger名称,log信息等,差很少,能够本身去查他们的语法规则。
3)loggers下面会定义许多个logger,这些logger经过name进行区分,来对不一样的logger配置不一样的输出,方法是经过引用上面定义的logger,注意,appender-ref引用的值是上面每一个appender的name,而不是节点名称。
四、 name的机制:
咱们这里看到了配置文件里面是name很重要,没错,这个name可不能随便起(其实能够随便起)。这个机制意思很简单。就是相似于java package同样,好比咱们的一个包:cn.lsw.base.log4j2。并且,能够发现咱们前面生成Logger对象的时候,命名都是经过 Hello.class.getName(); 这样的方法,为何要这样呢? 很简单,由于有所谓的Logger 继承的问题。好比 若是你给cn.lsw.base定义了一个logger,那么他也适用于cn.lsw.base.lgo4j2这个logger。名称的继承是经过点(.)分隔的。而后你能够猜想上面loggers里面有一个子节点不是logger而是root,并且这个root没有name属性。这个root至关于根节点。你全部的logger都适用与这个logger,因此,即便你在不少类里面经过 类名.class.getName() 获得不少的logger,并且没有在配置文件的loggers下面作配置,他们也都可以输出,由于他们都继承了root的log配置。
咱们上面的这个配置文件里面还定义了一个logger,他的名称是 cn.lsw.base.log4j2.Hello ,这个名称其实就是经过前面的Hello.class.getName(); 获得的,咱们为了给他单独作配置,这里就生成对于这个类的logger,上面的配置基本的意思是只有cn.lsw.base.log4j2.Hello 这个logger输出trace信息,也就是他的日志级别是trace,其余的logger则继承root的日志配置,日志级别是error,只能打印出ERROR及以上级别的日志。若是这里logger 的name属性改为cn.lsw.base,则这个包下面的全部logger都会继承这个log配置(这里的包是log4j的logger name的“包”的含义,不是java的包,你非要给Hello生成一个名称为“myhello”的logger,他也就无法继承cn.lsw.base这个配置了。
那有人就要问了,他不是也应该继承了root的配置了么,那么会不会输出两遍呢?咱们在配置文件中给了解释,若是你设置了additivity="false",就不会输出两遍,不然,看下面的输出:
这里要在加入一个类作对比:
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; public class Test { private static Logger logger = LogManager.getLogger(Test.class.getName()); public static void main(String[] args) { logger.trace("开始程序."); Hello hello= new Hello(); // for (int i = 0; i < 10000;i++){ if (!hello.hello()) { logger.error("hello"); } // } logger.trace("退出程序."); } }
这里先把配置文件改一下方便对照,一个是刚才第一个logger的名称仍是cn.lsw.base.log4j2.Hello,additivity去掉或改成true(由于默认是true,因此能够去掉),第二是把root的level改成info方便观察。
而后运行Test,看控制台的日志输出:
test entry entry Did it again! Did it again! 我是info信息 我是info信息 我是debug信息 我是debug信息 我是warn信息 我是warn信息 我是fatal信息 我是fatal信息 我是debug信息 我是debug信息 exit exit hello
能够看出,Test的trace日志没有输出,由于他继承了root的日志配置,只输出info即以上级别的日志。Hello 输出了trace及以上级别的日志,可是每一个都输出了两遍。你能够试一下,把第一个logger的level该为error,那么error以上的级别也是输出两遍。这时候,只要加上additivity为false就能够避免这个问题了。
固然,你能够为每一个logger 都在配置文件下面作不一样的配置,也能够经过继承机制,对不一样包下面的日志作不一样的配置。由于loggers下面能够写不少歌logger。
下面在看一个稍微复杂的例子:
<?xml version="1.0" encoding="UTF-8"?> <configuration status="error"> <!--先定义全部的appender--> <appenders> <!--这个输出控制台的配置--> <Console name="Console" target="SYSTEM_OUT"> <!--控制台只输出level及以上级别的信息(onMatch),其余的直接拒绝(onMismatch)--> <ThresholdFilter level="trace" onMatch="ACCEPT" onMismatch="DENY"/> <!--这个都知道是输出日志的格式--> <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/> </Console> <!--文件会打印出全部信息,这个log每次运行程序会自动清空,由append属性决定,这个也挺有用的,适合临时测试用--> <File name="log" fileName="log/test.log" append="false"> <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/> </File> <!--这个会打印出全部的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份创建的文件夹下面并进行压缩,做为存档--> <RollingFile name="RollingFile" fileName="logs/app.log" filePattern="log/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}-%i.log.gz"> <PatternLayout pattern="%d{yyyy-MM-dd 'at' HH:mm:ss z} %-5level %class{36} %L %M - %msg%xEx%n"/> <SizeBasedTriggeringPolicy size="50MB"/> </RollingFile> </appenders> <!--而后定义logger,只有定义了logger并引入的appender,appender才会生效--> <loggers> <!--创建一个默认的root的logger--> <root level="trace"> <appender-ref ref="RollingFile"/> <appender-ref ref="Console"/> </root> </loggers> </configuration>
这一个例子主要是为了讲一下appenders。
这里定义了三个appender,Console,File,RollingFile,看意思基本也明白,第二个是写入文件,第三个是“循环”的日志文件,意思是日志文件大于阀值的时候,就开始写一个新的日志文件。
有一个比较有意思的是ThresholdFilter ,一个过滤器,其实每一个appender能够定义不少个filter,这个功能颇有用。若是你要选择控制台只能输出ERROR以上的类别,你就用ThresholdFilter,把level设置成ERROR,onMatch="ACCEPT" onMismatch="DENY" 的意思是匹配就接受,不然直接拒绝。