1 Spring-boot配置文件的使用


  由于日志服务一般都在ApplicationContext创建前就初始化了,它并不是必须通过Spring的配置文件控制。因此通过系统属性和传统的Spring
Boot外部配置文件依然可以很好的支持日志控制和管理。
根据不同的日志系统,你可以按如下规则组织配置文件名,就能被正确加载:
Logback:logback-spring.xml, logback-spring.groovy, logback.xml, logback.groovy
Log4j:log4j-spring.properties, log4j-spring.xml, log4j.properties, log4j.xml
Log4j2:log4j2-spring.xml, log4j2.xml JDK (Java Util Logging):logging.properties
  Spring
Boot官方推荐优先使用带有-spring的文件名作为你的日志配置(如使用logback-spring.xml,而不是logback.xml)。
众智平台log4j2的配置:
<?xml version="1.0" encoding="UTF-8"?> <Configuration status="warn"> <Appenders
> <Console name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="%m%n" />
</Console> </Appenders> <Loggers> <Root level="INFO"> <AppenderRef ref="Console"
/> </Root> </Loggers> </Configuration>
2 Log4j2日志配置文件解析与使用

  下面以实际项目中日志为例,从简单到复杂,讲解log4j2日志框架配置文件的使用。

2.1 简单版日志配置,输出控制台

  配置文件如下:
<?xml version="1.0" encoding="UTF-8"?> <Configuration> <Appenders> <Console
name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="[%-5p] %d %c - %m%n"
/> </Console> </Appenders> <Loggers> <Root level="debug"> <AppenderRef ref=
"Console"/> </Root> </Loggers> </Configuration>
  具体日志如下,只在控制台输出:

  将上述配置的root的level改为info后

  将上述配置的root的level改为error后


2.2 简单版日志配置,控制台、文件双输出

  配置文件如下
<?xml version="1.0" encoding="UTF-8"?> <Configuration> <Appenders> <Console
name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="[%-5p] %d %c - %m%n"
/> </Console> <File name="File" fileName="dist/my.log"> <PatternLayout pattern=
"[%-5p] %d %c - %m%n"/> </File> </Appenders> <Loggers> <Root level="error"> <
AppenderRef ref="Console"/> </Root> </Loggers> </Configuration>
  控制台输出:

  文件输出如下:

   注意,这个时候虽然生成了对应的文件,但是并没有日志输出到这个文件。修改配置文件如下:
<?xml version="1.0" encoding="UTF-8"?> <Configuration> <Appenders> <Console
name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="[%-5p] %d %c - %m%n"
/> </Console> <File name="File" fileName="dist/my.log"> <PatternLayout pattern=
"[%-5p] %d %c - %m%n"/> </File> </Appenders> <Loggers> <Logger name=
"com.onlyisssilence.muya" level="INFO"> <AppenderRef ref="File"/> </Logger> <
Root level="error"> <AppenderRef ref="Console"/> </Root> </Loggers> </
Configuration>
 控制台和文件分别输出如下:


  这个时候日志文件上面已经有日志输出了。

2.3 日志的级别“透传”——additivity属性


注意一个问题:对比配置文件修改前后的控制台的输出可以看到,配置修改前控制台的输出日志级别为error,意味着只有erro级别以上的日志才会打印,事实也确实是如此,如配置:
<Root level="error"> <AppenderRef ref="Console"/> </Root>

  修改了配置文件后,多加了一个logger,root的level并没有变化,但是这个时候的控制台输出日志级别变大了,info以上的级别都打印出来了,再继续做实验,修改logger的日志级别为error,看到控制台和文件输出如下:

  可以看到控制台和日志文件的日志输出级别均为error,得出结论:
  有了logger之后,日志级别是由logger的级别控制?
  (只要某个logger接受了该log请求,那么作为父亲的Root就会跟着接受此log请求,而不再考虑它的level
)要想让root和logger的级别互不干扰,准确的说是不让logger控制root的日志输出级别,可以在logger中使用addtivity属性,配置如下:
<?xml version="1.0" encoding="UTF-8"?> <Configuration> <Appenders> <Console
name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="[%-5p] %d %c - %m%n"
/> </Console> <File name="File" fileName="dist/my.log"> <PatternLayout pattern=
"[%-5p] %d %c - %m%n"/> </File> </Appenders> <Loggers> <Logger name=
"com.onlyisssilence.muya" level="warn" additivity = "false"> <AppenderRef ref=
"File"/> </Logger> <Root level="info"> <AppenderRef ref="Console"/>
<!--<AppenderRef ref="taoge"/>--> </Root> </Loggers> </Configuration>
  控制台和文件输出如下:

  如上,发现一个问题:日志文件中这个时候确实打印的是warn以上级别的日志,控制台也确实是info级别以上的日志,但是,
日志文件只打印与业务相关的日志,控制台这个时候打印的是与业务无关的系统和数据库操作日志,这是为什么?

  Logger的appender根据参数additivity决定是否要叠加root的appender,logger的级别是其自身定义的级别,和root的级别没什么关系。
判断一个类的日志输出情况,首先找到这个类所在的logger(没有特别定义则默认为root),然后根据以上规则判断出这个logger的appender和level。
然后既可以知道这个类的哪些日志会被输出到哪些地方了。注意:任何一个类只会和一个logger对应,要么是定义的logger,要么是root,判断的关键在于找到这个logger,然后判断这个logger的appender和level。
因此对于上述现象的解释是:业务相关的类都在包com.onlyisssilence.muya下,它们的日志输出到了文件中,而数据库以及其他的框架类日志使用的是默认的logger——root,而root的appender为控制台,所以均会打印到控制台上。
  在上述的logger配置中,如下:
<Logger name="com.onlyisssilence.muya" level="warn" additivity = "false"> <
AppenderRef ref="File"/> </Logger> <Root level="info"> <AppenderRef ref=
"Console"/> </Root>

  可以看到子logger的appender只有file,其属性”name”为”com.onlyisssilence.muya”,这意味着在”com.onlyisssilence.muya”包下所有的类打印出来的业务日志的appender只有一个”File”,并输出到该appender指定的文件中,如上图所示,这些包下的类唯一对应着这一个logger,并不会再在其他logger下(这里指的是父logger——root);父root的appender只有console,这个时候”com.onlyisssilence.muya”中的类由于已经从属于了子logger了,他们的业务日志就不会打印到root这个logger所在appender上(也就是控制台)。
如下修改配置文件

  日志打印如下:


2.4 Logger配置中root和logger的区别


  上面出现的现象实际上是logger配置元之前的关系,是一个“父子”级的概念。Logger配置元分为“根logger”——root和普通自定义的logger。Root默认输出所有。而新添加的logger根据其属性name所指示的包路径,负责把包下的类打印的日志输出到指定文件。这个特性有利于在比较大的项目中对日志进行分类。
Logger的等级制度
  Root Logger位于Logger最高层级,也就是说,它是所有Logger的祖先。它有2个特别的地方
   它总是存在;
   不能用name来获取它;
  调用类的静态方法Logger.getRootLogger可以获取Root
Logger。所有其他的Loggers都是调用类的静态方法Logger.getLogger来实例化的。Logger.getLogger需要传入要创建的Logger
的name作为参数。Logger类的一些基本的方法如下:

  当有多个logger的时候,不同类中的日志到底是按照哪一个logger的appender来输出日志呢?[看源码]
看下图的日志配置文件:



  上述的appender有4个,一个控制台appender——console,三个文件appender——File1、File2、File3,有6个logger,可以看到这6个logger的关系如下:

  打印的控制台和文件日志如下:



  现象:
+ File appender中的每个日志文件都生成了,my1.log/my2.log/my3.log;
+
my2.log、my3.log日志文件分别对应着com.onlyisssilence.muya.SchedledCon下的ScheduleRefreshDatabase、ScheduleTask类中打印的日志,my1.log日志文件中并没有任何的日志文件输出;
+
控制台输出了系统日志以及Log4j2Test2类中的业务日志,其中系统日志使用的默认的root这个logger,Log4j2Test2类的业务日志使用的是com.onlyisssilence.muya.log4j2Test这个logger;
分析结论:
  logger存在父子级别的概念,其中root是根级别的,当待打印的文件没有设置任何所属的logger时,会默认使用root级别的
,如上例中的sql的日志、系统日志(它们均没有设置其logger),对于这种层级的包中的类,如果重复设置了其logger那么以离该文件最近的节点设置的logger为主。

2.5 Log4j2中特殊appender之JAP


  项目中有的时候需要存储一些操作日志,比如用户登录登出,查询信息,删除信息等,这些操作对于前端可能就是一个接口的调用,对于后端而言就是controller层中的一个方法的调用,这种业务场景的需求可以考虑使用log4j2+切面技术的相结合来实现,总的思路是,每个接口添加日志操作的自定义注解;注解的处理类中调用log4j2的日志工具打印日志;打印的日志使用JAP这种属性的appender,这种appender再通过定义日志操作类实现自动的入库。