说说日志框架的起源及现状
比如说,公司中张三要开发一个大型系统,需要打印日志的功能,他的日志完善的过程如下:
- System.out.println(“xxx”), 将关键数据打印在控制台;新增和去除一行日志的打印很麻烦;
- 使用自己写的日志框架来记录系统的一些关键信息,zhangsan-logging.jar;
- 接着他又把之前写的日志jar包加了几个高大上的功能,如异步模式、自动归档等,zhangsan-logging-good.jar;
- 公司又需要搭建新的系统,跟之前用的API不一样,张三需要给新的系统重新换上新的日志功能的实现包,zhangsan-logging-better.jar;
- 张三突然想到了jdbc与数据库驱动的设计方式,他写了一个统一的接口层(日志功能的一个抽象层logging-abstract.jar),然后他要做的就是给项目中导入具体的日志实现就可以了,张三之前写的日志框架都是实现的日志抽象接口;
其实目前市面上的日志框架也非常丰富,包括:
常见的日志抽象层:
JCL(Jakarta Commons Logging) 不建议使用
jboss-logging 不建议使用
- SLF4J(Simple Logging Facade for Java),推荐使用
常见的日志实现层:
- Log4j
- Log4j2(Log4j的升级版)
- Logback(Log4j的重置版),推荐使用
- JUL(java.util.logging)
Spring Boot使用的日志
spring boot 的底层是spring框架,而spring框架默认是用JCL(Jakarta Commons Logging)做日志输出的。spring boot在日志功能上做了一层包装,选用的是SLF4J和Logback。
如何在系统中使用SLF4J
SLF4J的用户手册,参考 SLF4J用户手册
SLF4J及常用实现包的使用如下图:

在应用开发的时候,做日志输出的时候,不应该直接调用日志输出的实现类,而是调用日志抽象层的方法。在此之前,需要给系统导入slf4j和logback的jar包。
1 2 3 4 5 6 7 8 9
| import org.slf4j.Logger; import org.slf4j.LoggerFactory;
public class HelloWorld { public static void main(String[] args) { Logger logger = LoggerFactory.getLogger(HelloWorld.class); logger.info("Hello World"); } }
|
注意:每一个日志的实现框架都有自己的配置文件,使用slf4j作为日志抽象层之后,配置文件还是需要做成日志实现框架 的配置文件。
使用SLF4J统一不同框架的日志输出
开发一个系统难免会用到很多框架,例如开发A系统,用到了 spring(日志使用commons-logging)、hibernate(日志使用jboss-logging)、mybatis等框架 。那么问题来了,怎样统一日志记录?并且即使是使用别的框架,那么如何统一使用SLF4J和Logback进行日志的输出?
SLF4J的办法请参考使用SLF4J统一日志输出,用一个成语概括就是 偷天换日,具体做法就是:
- 排除 spring的commons-logging 默认日志包(必须排除掉,否则与替换包中的类发生冲突);
- 使用 jcl-over-slf4j 替换原来的 commons-logging 包;
更多示例如下图所示:

Springboot底层的日志包依赖关系

springboot底层spring排除commons-logging依赖:
1 2 3 4 5 6 7 8
| <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <exclusions> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> </exclusions> </dependency>
|
总结:
- springboot底层也是使用slf4j+logback的方式进行日志记录的;
- springboot也把其他形式的日志实现替换成了slf4j的形式(还记得偷天换日吗?);
所以springboot能自动适配所有的日志,而且底层使用slf4j+logback的方式记录日志,引入其他框架的时候只需要把这个框架依赖的日志jar包排除掉即可。
Spring Boot 日志设置
日志功能默认配置
关于spring boot官方网站的日志介绍,请参考 spring boot官网日志章节介绍。
测试spring boot的默认设置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| @SpringBootTest @RunWith(SpringRunner.class) public class Test01 { @Test public void test01() { Logger logger = LoggerFactory.getLogger(getClass()); logger.trace("这是 trace ..."); logger.debug("这是 debug ..."); logger.info("这是 info ..."); logger.warn("这是 warn ..."); logger.error("这是 error ..."); } }
|
spring boot底层日志默认的设置参考 spring-boot-2.1.3.RELEASE.jar:
1 2 3
| package org.springframework.boot.logging.java; package org.springframework.boot.logging.log4j2; package org.springframework.boot.logging.logback;
|
日志功能的手动配置
在配置文件中手动设置日志的输出级别:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| logging.level={com.keyllo.demo.controller: trace, com.keyllo.service: debug}
logging.partern.console="%d{yyyy/MM/dd-HH:mm:ss} [%thread] %-5level %logger- %msg%n"
logging.partern.file="%d{yyyy/MM/dd-HH:mm:ss} [%thread] %-5level %logger- %msg%n"
|
切换底层日志的实现框架
我们前面说过,spring boot默认的日志实现是logback,如果我们想用log4j作为spring boot的底层日志实现(虽然这样做没有意义,因为正是因为log4j写的不好,log4j的作者才又开发了logback,但是我们还是想更深入地测试一下),该怎么做呢?
其实我们前面已经说过,我们只需要按照 slf4j的日志适配图进行相应包的排除和替换即可。
借用 idea工具的 diagrams->show dependencies 功能,我们可以很方便地做到以下事情:
- 把logback的依赖全部干掉,加入logback转换层的依赖(可省略)
- 把log4j转换层log4j-to-slf4j依赖干掉
- 把log4j适配层slf4j-log4j12(依赖与log4j实现包)、实现层log4j包依赖加入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <artifactId>logback-classic</artifactId> <groupId>ch.qos.logback</groupId> </exclusion> <exclusion> <artifactId>log4j-to-slf4j</artifactId> <groupId>org.apache.logging.log4j</groupId> </exclusion> </exclusions> </dependency>
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> </dependency>
|
或者
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </exclusion> </exclusions> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j2</artifactId> </dependency>
|
【注】log4j.properties 示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| project=zdemo-springboot04 logpath=/Users/xxx/Documents/mytmp/logs
log4j.rootLogger=INFO, Console
log4j.appender.Console=org.apache.log4j.ConsoleAppender log4j.appender.Console.Threshold=INFO log4j.appender.Console.layout=org.apache.log4j.PatternLayout log4j.appender.Console.layout.ConversionPattern=[%d] [%-3r] [%t,%x] [%-5p] %l - %m%n
log4j.appender.File=org.apache.log4j.FileAppender log4j.appender.File.Threshold=ERROR log4j.appender.File.layout=org.apache.log4j.PatternLayout log4j.appender.File.layout.ConversionPattern=[%d] [%-3r] [%t,%x] [%-5p] %l - %m%n log4j.appender.File.file=${logpath}/${project}.log log4j.appender.File.encoding=UTF-8
log4j.appender.RollingFile=org.apache.log4j.RollingFileAppender log4j.appender.RollingFile.Threshold=INFO log4j.appender.RollingFile.layout=org.apache.log4j.PatternLayout log4j.appender.RollingFile.layout.ConversionPattern=[%d] [%-3r] [%t,%x] [%-5p] %l - %m%n log4j.appender.RollingFile.file=${logpath}/${project}.log log4j.appender.RollingFile.encoding=UTF-8 log4j.appender.RollingFile.MaxFileSize=500MB log4j.appender.RollingFile.MaxBackupIndex=10
log4j.appender.DailyRollingFile=org.apache.log4j.RollingFileAppender log4j.appender.DailyRollingFile.Threshold=INFO log4j.appender.DailyRollingFile.layout=org.apache.log4j.PatternLayout log4j.appender.DailyRollingFile.layout.ConversionPattern=[%d] [%-3r] [%t,%x] [%-5p] %l - %m%n log4j.appender.DailyRollingFile.file=${logpath}/${project}.log log4j.appender.DailyRollingFile.encoding=UTF-8
log4j.appender.JDBC01=org.apache.log4j.jdbc.JDBCAppender log4j.appender.JDBC01.Threshold=INFO log4j.appender.JDBC01.layout=org.apache.log4j.PatternLayout log4j.appender.JDBC01.driver=com.mysql.jdbc.Driver log4j.appender.JDBC01.url=jdbc:mysql://192.168.1.96:3306/test?useUnicode=true&characterEncoding=UTF-8 log4j.appender.JDBC01.user=zhangqingli log4j.appender.JDBC01.password=qweasd log4j.appender.JDBC01.sql=INSERT INTO T_LOG VALUES('%x','%d','%C','%p','%m')
|
日志功能的自定义配置文件
如果不使用spring boot默认的日志设置,也可以定义自己的日志配置文件(日志配置文件放在类路径下):
- Logback:
logback-spring.xml,logback-spring.groovy,logback.xml,logback.groovy
- Log4j2:
log4j2-spring.xml,log4j2.xml
- JUL: logging.properties
推荐使用带spring扩展名的日志配置文件,例如对于 logback:
【注】:logback-spring.xml 日志设置参考:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
| <?xml version="1.0" encoding="UTF-8"?>
<configuration scan="false" scanPeriod="60 seconds" debug="false"> <property name="LOG_HOME" value="/Users/zhangqingli/Documents/mytmp/logs" /> <property name="appName" value="zdemo-springboot04"></property> <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout"> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> </layout> </appender>
<appender name="appLogAppender" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${LOG_HOME}/${appName}.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/${appName}-%d{yyyy-MM-dd}-%i.log</fileNamePattern>
<MaxHistory>365</MaxHistory>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>100MB</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> </rollingPolicy> <layout class="ch.qos.logback.classic.PatternLayout"> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [ %thread ] - [ %-5level ] [ %logger{50} : %line ] - %msg%n</pattern> </layout> </appender>
<logger name="org.springframework" level="debug" additivity="false"></logger>
<root level="info"> <springProfile name="dev"> <appender-ref ref="stdout" /> </springProfile> <appender-ref ref="appLogAppender" /> </root> </configuration>
|