市面上有许多的日志框架,比如 JUL( java.util.logging), JCL( Apache Commons Logging), Log4j, Log4j2, Logback、 SLF4j、 jboss-logging等等。
Spring Boot 2.*默认采用了slf4j+logback的形式 ,slf4j是个通用的日志门面,logback就是个具体的日志框架了,我们记录日志的时候采用slf4j的方法去记录日志,底层的实现就是根据引用的不同日志jar去判定了。所以Spring Boot也能自动适配JCL、JUL、Log4J等日志框架,它的内部逻辑就是通过特定的JAR包去适配各个不同的日志框架。
从上图可以看出,Spring Boot通过jul-to-slf4j.jar去适配了JUL日志框架,通过log4j-to-slf4j.jar去适配了log4j日志框架。我们得知道,Spring5.x相对于Spring4.x有个不同的地方就是对底层使用的日志框架有了个大的改变,去除了原来默认使用的JCL 框架,而是采用SLF4j这个通用的日志门面,所以Spring Boot2.x相对于Spring Boot1.x来说去除了对JCL的适配。
SpringBoot能自动适配所有的日志,其底层使用slf4j+logback的方式记录日志,引入其他框架的时候,只需要 把这个框架依赖的日志框架排除掉即可,因为Spring Boot会通过自己的jar去替代;
SpringBoot底层默认使用slf4j+logback的方式进行日志记录 ,这里简单介绍一下logback的使用。
logbac日志级别从低到高分别为TRACE, DEBUG, INFO, WARN, ERROR
我们一般是在类路径下建立一个logback.xml,也可命名为(logback-spring.xml , logback-spring.groovy , logback.xml ,logback.groovy)
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<contextName>spring-boot-loggingcontextName>
<property name="log.path" value="log" />
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFOlevel>
<onMatch>ACCEPTonMatch>
<onMismatch>DENYonMismatch>
filter>
<encoder>
<pattern>%d{HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%npattern>
encoder>
appender>
<appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/spring-boot-logging.logfile>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}/spring-boot-logging.%d{yyyy-MM-dd}.log.zipfileNamePattern>
<maxHistory>30maxHistory>
<totalSizeCap>1GBtotalSizeCap>
rollingPolicy>
<encoder>
<pattern>%d{HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%npattern>
encoder>
appender>
<root level="info">
<appender-ref ref="console" />
<appender-ref ref="file" />
root>
<logger name="com.baiding"/>
configuration>
<configuration>configuration>
<contextName>spring-boot-loggingcontextName>
每个logger都关联到logger上下文,默认上下文名称为“default”。但可以使用设置成其他名字,用于区分不同应用程序的记录。一旦设置,不能修改,可以通过%contextName来打印日志上下文名称。
用来定义变量值的标签, 有两个属性,name和value;其中name的值是变量的名称,value的值时变量定义的值。通过定义的值会被插入到logger上下文中。定义变量后,可以使“${}”来使用变量
<property name="log.path" value="log" />
####4.子节点appender
appender用来格式化日志输出节点,有俩个属性name和class,class用来指定哪种输出策略,常用就是控制台输出策略和文件输出策略。
控制台输出ConsoleAppender
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERRORlevel>
<onMatch>ACCEPTonMatch>
<onMismatch>DENYonMismatch>
filter>
<encoder>
<pattern>%d{HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%npattern>
encoder>
appender>
filter是一个过滤器,表示对输出到控制台的日记进行过滤。有两种过滤器,分别为LevelFilter 和ThresholdFilter。执行一个过滤器会有返回个枚举值,即DENY,NEUTRAL,ACCEPT其中之一。返回DENY,日志将立即被抛弃不再经过其他过滤器;返回NEUTRAL,有序列表里的下个过滤器接着处理日志;返回ACCEPT,日志会被立即处理,不再经过剩余过滤器。
其中LevelFilter 为级别过滤器,根据日志级别进行过滤。其下有三个子节点,level表示过滤的级别,用于配置符合过滤条件的操作,ACCEPT符合级别的输出到控制台,用于配置不符合过滤条件的操作,DENY不符合的拒绝输出到控制台。
ThresholdFilter为临界值过滤器,过滤掉低于指定临界值的日志。当日志级别等于或高于临界值时,过滤器返回NEUTRAL;当日志级别低于临界值时,日志会被拒绝。
表示对日志进行格式化
输出到文件RollingFileAppender
<appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/spring-boot-logging.logfile>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}/spring-boot-logging.%d{yyyy-MM-dd}.log.zipfileNamePattern>
<maxHistory>30maxHistory>
<totalSizeCap>1GBtotalSizeCap>
rollingPolicy>
<encoder>
<pattern>%d{HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%npattern>
encoder>
appender>
常见的日志输出到文件,随着应用的运行时间越来越长,日志也会增长的越来越多,将他们输出到同一个文件并非一个好办法。RollingFileAppender用于切分文件日志。
其中file
属性定义文件的带全路径的文件名,重要的是rollingPolicy
的定义。
class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"
是最常用的滚动策略,它根据时间来制定滚动策略,既负责滚动也负责触发滚动
定义了日志的切分方式——把每一天的日志归档到一个文件中,同理,可以使用%d{yyyy-MM-dd HH-mm}
来定义精确到分的日志切分方式。
表示只保留最近30天的日志
用来指定日志文件的上限大小,例如设置为1GB的话,那么到了这个值,就会删除旧的日志
####5. 子节点root
<root level="info">
<appender-ref ref="console" />
<appender-ref ref="file" />
root>
root节点是必选节点,用来指定最基础的日志输出级别,只有一个level属性,默认是DEBUG。
其中可以包含零个或多个元素,表示我们定义的appender将会添加到我们定义的loger子节点中。
用来设置某一个包或者具体的某一个类的日志打印级别、以及指定
仅有一个name属性,一个可选的level和一个可选的addtivity属性
name
:用来指定受此loger约束的某一个包或者具体的某一个类。level
:用来设置打印级别,如果未设置此属性,那么当前loger将会继承上级的级别。addtivity
:是否向上级loger传递打印信息。默认是true。
的实际使用有两种情况
第一种是不指定level,不指定appender
<root level="info">
<appender-ref ref="console" />
<appender-ref ref="file" />
root>
<logger name="com.baiding.logging.SpringBootLoggingApplicationTests"/>
这时候的处理流程是:当SpringBootLoggingApplicationTests执行日志方法时,首先交给
处理(继承上级的level–info),将级别大于等于info的日志交给root,本身没有打印任何日志
第二种是指定了level,指定了appender
<root level="info">
<appender-ref ref="console" />
<appender-ref ref="file" />
root>
<logger name="com.baiding" level="warn" addtivity="false">
<appender-ref ref="console" />
logger>
此logger指定了level为warn,addtivity为false,不在向上级传递打印信息,若设置了
属性,但没有设置addtivity为false,则com.baiding包下的日志会先在名为console的appender记录一次,之后传递给上级,root又会在名为console和file的appender记录一次,这样就打印了两次了。
Spring Boot在使用logback记录日志时,推荐使用logback-spring.xml的格式,这样的话,日志框架就不直接加载日志的配置项,而是由SpringBoot解析日志配置,就可以使用SpringBoot 的高级Profile功能
如下logback-spring.xml
<springProfile name="dev">
<logger name="com.baiding" level="info"/>
springProfile>
<springProfile name="prod">
<logger name="com.baiding" level="warn"/>
springProfile>
可以在不同的节点中使用springProfile
功能,用于指定某段配置只在某个环境下生效 。当然使用前,要激活profile。
Spring Boot虽然默认使用Logback日志框架,但其内部也集成了Log4j2框架。要知道的是,在Java中,Log4j2框架的性能是最强的,所以我们一般在程序中使用Log4j2框架。下面介绍一下Log4j2的使用及其内部属性的含义。
首先,要在Spring Boot使用Log4j2的话,那么第一件事就是去除Logback的jar包,并引入Log4j2的jar。
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-loggingartifactId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-log4j2artifactId>
dependency>
上面就是在依赖Web模块的时候去除引用spring-boot-starter-logging.jar,之后依赖spring-boot-starter-log4j2.jar包。
之后在类路径下新建一个log4j2.xml,其具体内容如下:
<configuration status="WARN" monitorInterval="30">
<Properties>
<Property name="log.path">logProperty>
Properties>
<appenders>
<console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
console>
<File name="log" fileName="${log.path}/test.log" append="false">
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
File>
<RollingFile name="RollingFileInfo" fileName="${log.path}/info.log" filePattern="${log.path}/logs/${date:yyyy-MM}/info-%d{yyyy-MM-dd}.log.zip">
<ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
<Policies>
<TimeBasedTriggeringPolicy modulate="true" interval="1"/>
Policies>
RollingFile>
appenders>
<loggers>
<logger name="org.springframework" level="INFO"/>
<logger name="org.mybatis" level="INFO"/>
<logger name="com.baiding" level="INFO"/>
<root level="info">
<appender-ref ref="Console"/>
<appender-ref ref="RollingFileInfo"/>
root>
loggers>
configuration>
根节点Configuration有两个属性,status和monitorinterval
status
: status用来指定log4j2本身的日志的级别monitorinterval
: monitorinterval用于指定log4j自动重新配置的监测间隔时间,单位是s,最小是5s####2. Properties
标签
在xml文件中,可以使用Properties
标签来自定义变量,方便其他地方的引用。
<Properties>
<Property name="log.path">logProperty>
Properties>
这里的log.path是定义日志存放的地方,此处是存放于项目根路径下的log文件夹中
Appenders
节点和logback一样,Appender是用来定义日志输出点的,一般常用有三个子节点,分别为Console、RollingFile、File。下面介绍一下各个子节点的用处及常用的属性
Console节点用来定义输出到控制台的Appender:
name
: 指定Appender的名字,用于Logger节点引用target
: SYSTEM_OUT 或 SYSTEM_ERR,一般设置为:SYSTEM_OUTPatternLayout
: 指定日志输出格式,默认为%m%n<console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
console>
File节点用来定义输出到指定位置的文件的Appender:
name
: 指定Appender的名字,用于Logger节点引用fileName
: 指定输出日志的目的文件带全路径的文件名append
: 是否追加,默认为ture。ture是将新日志追加到原日志文件尾部,false则是删除已有文件,重建新文件PatternLayout
: 指定日志输出格式,默认为%m%n<File name="log" fileName="log/test.log" append="false">
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
File>
RollingFile节点用来定义输出到指定位置的文件的Appender,但其记录的内容可拆分:
File对文件的约束很简单,而RollingFile则比较灵活,其可根据文件大小来分拆,还可以根据时间来分拆
name
: 指定Appender的名字,用于Logger节点引用fileName
: 指定输出日志的目的文件带全路径的文件名filePattern
:指定拆分出去的日志文件的全路径的文件名以及格式PatternLayout
: 指定日志输出格式,默认为%m%nPolicies
: 指定滚动日志的策略,就是什么时候进行新建日志文件输出日志.TimeBasedTriggeringPolicy
: 基于时间进行日志的滚动SizeBasedTriggeringPolicy
: 基于文件大小进行日志的滚动ThresholdFilter
: 日志过滤器<RollingFile name="RollingFileInfo" fileName="${log.path}/info.log" filePattern="${log.path}/logs/$${date:yyyy-MM}/info-%d{yyyy-MM-dd}-%i.log.zip">
<ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
<Policies>
<TimeBasedTriggeringPolicy modulate="true" interval="1"/>
Policies>
RollingFile>
这里说一下TimeBasedTriggeringPolicy这个滚动策略的属性interval,它是指日志进行滚动的间隔,那么它的单位具体是什么呢?关键点在于filePattern的日志文件名所含有的日期格式%d{yyyy-MM-dd}
,这里日期格式具体到了天,那么以天为单位,若是日期具体到%d{yyyy-MM-dd-HH-mm}
分钟的话,那么就是以分钟为单位。
这里还提到了日志过滤器,Log4j提供了许多的日志过滤器,具体可以看下文档 Filters。但我们一般采用ThresholdFilter,这个过滤器一般用来过滤掉所有级别低于它定义的级别的日志。
Loggers
节点Loggers
节点下一般有root和logger节点.
root节点用来指定项目的根日志,如果没有单独指定logger,那么就会默认使用该root日志输出。
<root level="info">
<appender-ref ref="Console"/>
<appender-ref ref="RollingFileInfo"/>
root>
Logger节点用来单独指定日志的形式,比如要为某个包下所有的class或者某个具体的class指定不同的日志级别等。
<logger name="org.springframework" level="INFO"/>
<logger name="org.mybatis" level="INFO"/>
<logger name="com.baiding" level="INFO"/>
若我们为logger指定了AppenderRef ,别忘了将logger的additivity 属性设置为false,要不然日志可能会在指定的Appender中输出两遍
Log4j2有个突出的功能就是支持高效低延迟的异步化写日志。日志异步输出的好处在于,使用单独的进程来执行日志打印的功能,可以提高日志执行效率,减少日志功能对正常业务的影响。
日志的异步输出使用了disruptor这个开源的并发框架,所以首先得导入disruptor.jar包
<dependency>
<groupId>com.lmaxgroupId>
<artifactId>disruptorartifactId>
<version>3.4.2version>
dependency>
异步输出分为两种情况,一种为全异步模式,一种为异步和非异步混合输出模式。通过官方的性能对比,一般线程数在2-16之间的话,混合使用同步和异步的logger来打印日志,性能是最好的。
异步和非异步混合模式
这种模式的启用,主要在于两个节点的使用,分别为AsyncRoot
和AsyncLogger
,这两个节点可以和Root
或 Logger
节点混合使用。
在这里修改一下上述的log4j2.xml文件中Loggers
节点就可以了。
<loggers>
<AsyncRoot level="info">
<appender-ref ref="Console"/>
<appender-ref ref="RollingFileInfo"/>
AsyncRoot>
<AsyncLogger name="com.baiding" level="INFO" />
<AsyncLogger name="org.mybatis" level="INFO" />
<AsyncLogger name="org.springframework" level="INFO" />
loggers>