上篇介绍了 Spring Boot Maven 多模块项目的搭建方法以及 MyBatis 的集成。通常在调试接口或者排查问题时我们主要借助于日志,一个设计合理的日志文件配置能大大降低我们的排查难度,本篇主要介绍 Logback 集成步骤。
其实 Spring Boot 提供的父工程中已经包含了所依赖的 Logback jar 包,可通过项目父 pom 中的 「spring-boot-starter-parent」>> 「spring-boot-dependencies」找到 Logback 的三个依赖包。
<dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-access</artifactId> <version>${logback.version}</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>${logback.version}</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-core</artifactId> <version>${logback.version}</version> </dependency>
在自定义日志配置之前我们可以先尝试一下 Spring Boot 默认的日志配置,可通过修改 application.properties 文件中的配置项设置。
① 更改默认日志级别
默认情况下 Spring Boot 从控制台打印出来的日志级别只有 ERROR、WARN、INFO 这三种,如果需要打印 DEBUG 级别的日志,可以使用以下配置项设置。
logging.level.root = DEBUG
② 将日志输出到文件中
默认情况下 Spring Boot 只会在控制台打印日志,可以使用「 logging.path 」或「 logging.file 」其中一个配置项将日志输出到文件中。
logging.path = ./logs 或 logging.file = ./logs/demo.log
注意事项:
我们可能需要将一些特定包或者特定级别的日志打印到单独的文件中方便排查问题,显然默认的日志配置并不能满足我们需求,需要我们自定义。
首先熟悉下常规的配置项,详见: Logback XML 基础配置详解
然后在 demo-web 层的 resources 目录下创建名为「 logback.xml 」的文件,具体内容如下:
<?xml version="1.0" encoding="UTF-8"?> <!-- 每隔一分钟扫描配置文件 --> <configuration scan="true" scanPeriod="60 seconds" debug="false"> <!-- 设置上下文名称为 demo --> <contextName>demo</contextName> <!-- 定义日志输出格式变量:%d表示时间 花括号内为时间格式 %level表示日志级别 %thread表示线程名 %logger{0}表示输出日志的类名 [%line]表示行号用方括号包裹 %msg表示日志消息 %n换行 --> <property name="log.pattern" value="[%d{'MM-dd HH:mm:ss,SSS'}] %level [%thread] %logger{0}[%line] - %msg%n"/> <!-- 定义日志字符集 --> <property name="log.charset" value="UTF-8"/> <!-- 定义日志级别 --> <property name="log.level" value="INFO"/> <!-- 定义日志存放路径 --> <property name="log.path" value="logs"/> <!-- 输出到控制台 --> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <!-- 日志输出格式 --> <encoder> <!-- 日志字符集 --> <charset>${log.charset}</charset> <!-- 日志输出格式 --> <pattern>${log.pattern}</pattern> </encoder> </appender> <!-- 时间滚动输出日志 --> <appender name="COMMON" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- 写入的文件名 --> <file>${log.path}/common.log</file> <!-- 追加到文件结尾 --> <append>true</append> <!-- 滚动策略:按照每天生成日志文件 --> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!-- 每天日志归档路径及文件名格式 --> <fileNamePattern>${log.path}/common.%d{yyyy-MM-dd}.log</fileNamePattern> <!-- 日志文件保留天数 --> <maxHistory>30</maxHistory> </rollingPolicy> <encoder> <charset>${log.charset}</charset> <pattern>${log.pattern}</pattern> </encoder> </appender> <appender name="ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${log.path}/error.log</file> <append>true</append> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${log.path}/error.%d{yyyy-MM-dd}.%i.log</fileNamePattern> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <!-- 单日志文件最大限制100兆 超过则将文件内容归档到按照 fileNamePattern 命名的文件中 源文件则清空 --> <maxFileSize>100MB</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> </rollingPolicy> <!-- 级别过滤器匹配 ERROR 级别日志 --> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>ERROR</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> <encoder> <charset>${log.charset}</charset> <pattern>${log.pattern}</pattern> </encoder> </appender> <appender name="DB" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${log.path}/db.log</file> <append>true</append> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${log.path}/db.%d{yyyy-MM-dd}.log</fileNamePattern> </rollingPolicy> <encoder> <charset>${log.charset}</charset> <pattern>${log.pattern}</pattern> </encoder> </appender> <!-- 指定 com.example.demo.dao.mapper 包要使用的 appender 且不向上级传递 --> <logger name="com.example.demo.dao.mapper" level="DEBUG" additivity="false"> <!-- 指定使用 DB 及 ERROR 这两个 appender --> <appender-ref ref="DB"/> <appender-ref ref="ERROR"/> </logger> <!-- 根 logger --> <root level="${log.level}"> <appender-ref ref="CONSOLE"/> <appender-ref ref="COMMON"/> <appender-ref ref="ERROR"/> </root> </configuration>
然而,上述配置中 <property> 标签的值都是写死的,但我们的项目环境可能有多套,每套环境的日志配置都有所区别,这就需要借助 Spring Boot 提供的 <springProfile> 及 <springProperty> 标签解决。
① 首先将刚才新建的 「 logback.xml 」文件重命名为「 logback-spring.xml 」。
注:因为文件的命名与加载顺序有关,logback.xml 早于 application.properties 加载,而 logback-spring.xml 晚于 application.properties 加载。而且 logback-spring.xml 中 Spring Boot 提供了一些特定的配置项支持,如 <springProperty>、<springProfile>。
② 其次将 <property> 标签定义的配置项改为使用 <springProperty> 标签声明。
<springProperty scope="context" name="log.charset" source="log.charset" defaultValue="UTF-8"/> <springProperty scope="context" name="log.level" source="log.level" defaultValue="INFO"/> <springProperty scope="context" name="log.path" source="log.path" defaultValue="./logs"/> <springProperty scope="context" name="log.pattern" source="log.pattern" defaultValue="[%d{'MM-dd HH:mm:ss,SSS',GMT+8:00}] %level [%thread] %logger{0}[%line] - %msg%n"/>
注:因为只有使用 <springProperty> 标签才能使用 application.properties 文件中的配置项,它的工作方式与 Logback 标准的 <property> 类似,source 指定 application.properties 文件中的配置项。defaultValue 为默认值。
③ 使用 <springProfile> 标签指定配置生效环境
<!-- 开发及测试环境才打印 SQL 日志 --> <springProfile name="dev,test"> <!-- 指定 com.example.demo.dao.mapper 包要使用的 appender 且不向上级传递 --> <logger name="com.example.demo.dao.mapper" level="DEBUG" additivity="false"> <!-- 指定使用 DB 及 ERROR 这两个 appender --> <appender-ref ref="DB"/> <appender-ref ref="ERROR"/> </logger> </springProfile>
注:上述配置生效的前提是在 application.properties 文件中指定生效环境(即 spring.profiles.active = dev )
④ 启动项目可以看到在项目根目录生成 logs 目录,目录中有三个日志文件(即 common.log 、db.log 、error.log ),访问 上篇的 http://localhost:8080/demo/test 接口后 db.log 输出如下日志:
[01-30 17:58:05,296] DEBUG [http-nio-8080-exec-1] selectById[159] - ==> Preparing: SELECT `id`, `user_name` FROM `db_user` WHERE `id` = ? [01-30 17:58:05,317] DEBUG [http-nio-8080-exec-1] selectById[159] - ==> Parameters: 1(Integer) [01-30 17:58:05,373] DEBUG [http-nio-8080-exec-1] selectById[159] - <== Total: 1
至此 Spring Boot 集成 Logback 的具体步骤介绍完毕,我们自定义了一个简单的日志配置,也看到了最后的输出结果。后续将继续介绍其余中间件或者工具的集成步骤。