其实我们之前就一直在用日志框架了,只不过spring boot为我们整合好了默认的日志框架的实现。我们通过Lombok
框架提供的日志相关的注解再配合编辑器的插件,直接用的内置对象log
进行日志api的调用。然后日志默认在控制台打印输出。
现在我们将对日志框架做进一步的定制,我们希望日志可以输出到文件中保存起来,方便我们日后排查问题。废话不多说,开干!
引入依赖
因为spring boot默认会包含spring-boot-starter-logging
依赖,其中使用logback
的日志实现,现在我们要用log4j2
,则需要在它所有的依赖中,都将这个模块排除,一个最简单的排除方式:
groovy
configurations {
all*.exclude module: 'spring-boot-starter-logging'
...
}
同时还要引入如下依赖:
groovy
dependencies {
...
implementation 'org.springframework.boot:spring-boot-starter-log4j2'
}
日志配置
接下来我们需要在resources
资源包下新建一个log4j2.xml
文件,在其中配置日志:
日志级别
日志的级别优先级从高到低:
-
OFF
-
FATAL(导致应用退出的重大错误)
-
ERROR(不影响系统继续运行的错误)
-
WARN
-
INFO
-
DEBUG
调试程序输出的信息
-
TRACE
输出调用栈中每一步的详细的跟踪日志
-
ALL
打印所有的日志
配置结构
现在我们写一个日志配置的基本结构:
xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- status记录的是log4j在抓取和输出日志过程中自身输出的日志级别,这里输出fatal以及以上优先级的级别的日志 -->
<configuration status="fatal">
<Properties>
<!-- 这里定义被下面配置引用的常量 -->
</Properties>
<Appenders>
<!-- 用于向控制台输出日志 -->
<Console name="consoleLog" target="SYSTEM_OUT">
</Console>
<!-- 向文件输出日志,这里可以配置不同的日志级别输出到不同的日志文件中 -->
<RollingFile name="fileInfoLog">
</RollingFile>
<!-- error级别日志文件输出 -->
<RollingFile name="fileErrorLog">
</RollingFile>
<!-- 异步日志记录器,在写文件时实现异步写入,提高程序执行性能 -->
<Async name="myAsync">
<AppenderRef ref="fileInfoLog"/>
<AppenderRef ref="fileErrorLog"/>
</Async>
</Appenders>
<loggers>
<!-- 配置程序各个模块(包)所使用的日志记录器以及配置日志输出的级别 -->
<!-- 所有模块继承的默认设置,子模块可以覆盖 -->
<Root level="warn">
<AppenderRef ref="consoleLog"/>
</Root>
<!-- 对我们的应用指定日志输出级别为info,并且从根设置继承,支持控制台输出,默认additivity为true -->
<logger name="com.juan" level="info" additivity="true">
<AppenderRef ref="myAsync"/>
</logger>
</loggers>
</configuration>
日志文件输出路径
这里我们把要输出的日志文件的路径设置为一个常量,值表示的是相对于项目根路径的路径,这里我们设置为本地开发运行、测试时和项目同一层级的logs
目录下:
xml
<Properties>
<!-- 这里定义被下面配置引用的常量 -->
<Property name="LOG_HOME" value="../logs"/>
</Properties>
日志Layout
对于输出到文件中的日志格式,我们也定义在常量中:
xml
<Properties>
...
<Property name="File_LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %6p %5pid --- [%15.15t] %-40.40logger{39}: %m%n"/>
</Properties>
关于这里的格式可以参考logback
的官方文档layout章节,因为格式方面日志框架基本相通的,且logback
这方面文档写的更好。
控制台日志
现在我们div控制台日志的输出风格,这里下卷直接给出一个仿spring boot控制台默认的输出风格的布局:
xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration status="fatal">
<Appenders>
<!-- 用于向控制台输出日志 -->
<Console name="consoleLog" target="SYSTEM_OUT">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} %highlight{%6p} %style{%5pid}{bright,magenta} --- [%15.15t] %style{%-40.40logger{39}}{bright,cyan}: %highlight{%m%n}{INFO=BLACK, DEBUG=BLACK}" disableAnsi="false"/>
</Console>
</Appenders>
<loggers>
<Root level="info">
<AppenderRef ref="consoleLog"/>
</Root>
</loggers>
</configuration>
按照以上配置后,小伙伴们可以自行运行一个单元测试来看看控制台的输出效果。
文件日志配置
接下来我们再来完善下文件的日志配置,这里我们将输出到文件中的日志分为两类:info日志和error日志。我们主要来看下info日志文件的配置,error的则类似。
xml
<!-- 向文件输出日志,这里可以配置不同的日志级别输出到不同的日志文件中 -->
<RollingFile name="fileInfoLog" fileName="${LOG_HOME}/info.log" filePattern="${LOG_HOME}/$${date:yyyy-MM-dd}/info_%i.%d{yyyy-MM-dd-HH}.log" append="true">
<!-- 过滤器 -->
<Filters>
<!--只接受info级别的日志,其它全部拒绝掉-->
<ThresholdFilter level="INFO"/>
<ThresholdFilter level="WARN" onMatch="DENY" onMismatch="NEUTRAL"/>
</Filters>
<!-- 日志格式 -->
<PatternLayout pattern="${File_LOG_PATTERN}"/>
<!-- 策略 -->
<Policies>
<!-- 每隔一天转存 -->
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
<!-- 文件大小 -->
<SizeBasedTriggeringPolicy size="10 MB"/>
</Policies>
<!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始自动清理-->
<DefaultRolloverStrategy max="10">
<Delete basePath="${LOG_HOME}/$${date:yyyy-MM-dd}/" maxDepth="2">
<IfFileName glob="*/*.log" />
<!--7天-->
<IfLastModified age="168H" />
</Delete>
</DefaultRolloverStrategy>
</RollingFile>
这里的fileName
指定的是当前日志的输出路径,filePattern
则是被转储后的文件路径,这里我们会以日期来划分子目录,并且文件的写入形式为追加,而不是覆盖(append="true"
控制,默认为true
),当一个文件写满了(达到<SizeBasedTriggeringPolicy>
配置的大小)后,再按照命名中的%i
以索引自增的方式,生成新的文件,也就是我们说的日志滚动,这里我们指定了日志滚动的大小为10 MB。最后我们还指定了日志文件的清理策略。
error日志的配置类似,处理文件命名,还有过滤的规则不一样:
xml
<Filters>
<!-- 只考虑error级别 -->
<ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
</Filters>
然后,我们为文件日志配置异步记录器:
xml
<Async name="myAsync">
<AppenderRef ref="fileInfoLog"/>
<AppenderRef ref="fileErrorLog"/>
</Async>
并为我们的小卷生鲜应用模块(包)指定下要记录的日志级别和记录器:
xml
<logger name="com.xiaojuan" level="info">
<AppenderRef ref="myAsync"/>
</logger>
示例配置
最后为了方便小伙伴们练习,小卷给出完整的日志配置文件:
xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- status记录的是log4j在抓取和输出日志过程中自身输出的日志级别,这里输出fatal以及以上优先级的级别的日志 -->
<configuration status="fatal">
<Properties>
<!-- 这里定义被下面配置引用的常量 -->
<Property name="LOG_HOME" value="../logs"/>
<Property name="File_LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %6p %5pid --- [%15.15t] %-40.40logger{39}: %m%n"/>
</Properties>
<Appenders>
<!-- 用于向控制台输出日志 -->
<Console name="consoleLog" target="SYSTEM_OUT">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} %highlight{%6p} %style{%5pid}{bright,magenta} --- [%15.15t] %style{%-40.40logger{39}}{bright,cyan}: %highlight{%m%n}{INFO=BLACK, DEBUG=BLACK}" disableAnsi="false"/>
</Console>
<!-- 向文件输出日志,这里可以配置不同的日志级别输出到不同的日志文件中 -->
<RollingFile name="fileInfoLog" fileName="${LOG_HOME}/info.log" filePattern="${LOG_HOME}/$${date:yyyy-MM-dd}/info_%i.%d{yyyy-MM-dd-HH}.log" append="true">
<!-- 过滤器 -->
<Filters>
<!--只接受info级别的日志,其它全部拒绝掉-->
<ThresholdFilter level="INFO"/>
<ThresholdFilter level="WARN" onMatch="DENY" onMismatch="NEUTRAL"/>
</Filters>
<!-- 日志格式 -->
<PatternLayout pattern="${File_LOG_PATTERN}"/>
<!-- 策略 -->
<Policies>
<!-- 每隔一天转存 -->
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
<!-- 文件大小 -->
<SizeBasedTriggeringPolicy size="10 MB"/>
</Policies>
<!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始自动清理-->
<DefaultRolloverStrategy max="10">
<Delete basePath="${LOG_HOME}/$${date:yyyy-MM-dd}/" maxDepth="2">
<IfFileName glob="*/*.log" />
<!--7天-->
<IfLastModified age="168H" />
</Delete>
</DefaultRolloverStrategy>
</RollingFile>
<!-- error级别日志文件输出 -->
<RollingFile name="fileErrorLog" fileName="${LOG_HOME}/error.log" filePattern="${LOG_HOME}/$${date:yyyy-MM-dd}/error_%i.%d{yyyy-MM-dd-HH}.log" append="true">
<!-- 过滤器 -->
<Filters>
<!-- 只考虑error级别 -->
<ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
</Filters>
<!-- 日志格式 -->
<PatternLayout pattern="${File_LOG_PATTERN}"/>
<Policies>
<!-- 每隔一天转存 -->
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
<!-- 文件大小 -->
<SizeBasedTriggeringPolicy size="10 MB"/>
</Policies>
<DefaultRolloverStrategy max="10">
<Delete basePath="${LOG_HOME}/$${date:yyyy-MM-dd}/" maxDepth="2">
<IfFileName glob="*/*.log" />
<!--7天-->
<IfLastModified age="168H" />
</Delete>
</DefaultRolloverStrategy>
</RollingFile>
<!-- 异步日志记录器,在写文件时实现异步写入,提高程序执行性能 -->
<Async name="myAsync">
<AppenderRef ref="fileInfoLog"/>
<AppenderRef ref="fileErrorLog"/>
</Async>
</Appenders>
<loggers>
<!-- 配置程序各个模块(包)所使用的日志记录器以及配置日志输出的级别 -->
<!-- 所有模块继承的默认设置,子模块可以覆盖 -->
<Root level="debug">
<AppenderRef ref="consoleLog"/>
</Root>
<logger name="org.springframework" level="info">
</logger>
<logger name="org.mybatis" level="info">
</logger>
<!-- 对我们的应用指定日志输出级别为info,并且从根设置继承,支持控制台输出,默认additivity为true -->
<!-- <logger name="com.xiaojuan" level="info" additivity="true">-->
<logger name="com.xiaojuan" level="info">
<AppenderRef ref="myAsync"/>
</logger>
</loggers>
</configuration>
注意
最后为了查看spring boot整合的框架启动的debug信息,我们可以将Root级别设置为
debug
,这样很多框架都会打印debug信息。我们可以将不要打印debug信息的框架(包)的日志级别设置为info
。一般来说,我们开发不关心debug日志输出,推荐设置为下面的配置:
xml<loggers> <Root level="info"> <AppenderRef ref="consoleLog"/> </Root> <logger name="com.xiaojuan"> <AppenderRef ref="myAsync"/> </logger> </loggers>