在项目开发过程中,我们可以通过 debug 查找问题。而在线上环境我们查找问题只能通过打印日志的方式查找问题。因此对于一个项目而言,日志记录是一个非常重要的问题。因此,如何选择一个合适的日志记录框架也非常重要。
在Java开发中,常用的日志记录框架有JDKLog、Log4J、LogBack、Log4J2、SLF4J。这些日志记录框架各有各的特点,各有各的应用场景。了解这些框架的特点及应用场景,有利于我们做技术选型的时候做出正确的判断。
1、commons-logging,slf4j只是一种日志抽象门面接口,不是具体的日志框架。
2、jul/jdk,log4j,log4j2,logback是具体的日志框架。
日志框架关系图:
slf4j与jul的集成
jar包依赖关系图
1.需要的jar包
- slf4j-api.jar
- slf4j-jdk14.jar
对应的maven依赖:
<!-- java.util.logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>1.7.24</version>
</dependency>
2.测试Demo
package com.jeiker.demo.slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @Author : xiao
* @Date : 17/3/16 上午10:57
*/
public class Slf4jJdkTest {
private static final Logger logger = LoggerFactory.getLogger(Slf4jJdkTest.class);
public static void main(String[] args){
if(logger.isDebugEnabled()){
logger.debug("slf4j-jdk debug message");
}
if(logger.isInfoEnabled()){
logger.debug("slf4j-jdk info message");
}
if(logger.isTraceEnabled()){
logger.debug("slf4j-jdk trace message");
}
}
}
3.分析
slf4j与log4j1的集成
jar包依赖关系图
1.需要的jar包
- slf4j-api.jar
- slf4j-log4j12.jar
- log4j.jar
对应的maven依赖:
<!-- log4j1 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.24</version>
</dependency>
2.测试Demo
package com.jeiker.demo.slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @Author : xiao
* @Date : 17/3/16 上午10:57
*/
public class Slf4jLog4j1Test {
private static final Logger logger = LoggerFactory.getLogger(Slf4jLog4j1Test.class);
public static void main(String[] args){
if(logger.isDebugEnabled()){
logger.debug("slf4j-log4j1 debug message");
}
if(logger.isInfoEnabled()){
logger.debug("slf4j-log4j1 info message");
}
if(logger.isTraceEnabled()){
logger.debug("slf4j-log4j1 trace message");
}
}
}
3.分析
slf4j与log4j2的集成
jar包依赖关系图
1.需要的jar包
- slf4j-api.jar
- log4j-slf4j-impl.jar
- log4j-api.jar
- log4j-core.jar
对应的maven依赖:
<!-- log4j2 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.8.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.8.1</version>
</dependency>
2.测试Demo
package com.jeiker.demo.slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @Author : xiao
* @Date : 17/3/16 上午10:57
*/
public class Slf4jLog4j2Test {
private static final Logger logger = LoggerFactory.getLogger(Slf4jLog4j2Test.class);
public static void main(String[] args){
if(logger.isDebugEnabled()){
logger.debug("slf4j-log4j2 debug message");
}
if(logger.isInfoEnabled()){
logger.debug("slf4j-log4j2 info message");
}
if(logger.isTraceEnabled()){
logger.debug("slf4j-log4j2 trace message");
}
}
}
3.分析
slf4j与logback的集成
jar包依赖关系图
1.需要的jar包
- slf4j-api.jar
- logback-core.jar
- logback-classic.jar
对应的maven依赖:
<!-- logback -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.1</version>
</dependency>
2.测试Demo
package com.jeiker.demo.slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @Author : xiao
* @Date : 17/3/16 上午10:58
*/
public class Slf4jLogbackTest {
private static final Logger logger = LoggerFactory.getLogger(Slf4jLogbackTest.class);
public static void main(String[] args){
if(logger.isDebugEnabled()){
logger.debug("slf4j-logback debug message");
}
if(logger.isInfoEnabled()){
logger.debug("slf4j-logback info message");
}
if(logger.isTraceEnabled()){
logger.debug("slf4j-logback trace message");
}
}
}
3.分析
使用slf4j统一各种日志
当项目是使用多种日志API时,可以统一适配到slf4j,然后通过slf4j适配底层实现。
中间使用slf4j进行统一项目中使用的多种日志框架的API,然后转发到slf4j,slf4j再底层用开发者想用的一个日志框架来进行日志系统的实现。从而达到了多种日志的统一实现。
slf4j在这中间充当了中间人,充当翻译的感觉。
slf4j官网桥接说明:https://www.slf4j.org/legacy.html
slf4j-jdk
问题:目前的应用程序(application)中已经使用了以下的几种API来进行日志的编程:
- commons-logging
- log4j
现在想统一将日志的输出交给jdk-logging。
解决方法:
将上面的日志系统全部转换到slf4j
- 移除commons-logging(可以不移除),使用jcl-over-slf4j(桥接)将commons-logging的底层日志输出切换到sl4j.
- 移除log4j1(必须移除),使用log4j-over-slf4j(桥接),将log4j1的日志输出切换到slf4j。
让slf4j选择jdk-logging作为底层日志输出
- 加入slf4j-api.jar
- 加入slf4j-jdk14.jar
slf4j-log4j
问题:目前的应用程序(application)中已经使用了以下的几种API来进行日志的编程:
- commons-logging
- jdk-loging
现在想统一将日志的输出交给log4j1。
解决方法:
将上面的日志系统全部转换到slf4j
- 移除commons-logging(可以不移除),使用jcl-over-slf4j(桥接)将commons-logging的底层日志输出切换到sl4j.
- 使用jul-to-slf4j(桥接),将jul的日志输出切换到slf4j.
让slf4j选择log4j1作为底层日志输出
- 加入slf4j-api.jar
- 加入slf4j-log4j12.jar
- 加入log4j.jar
slf4j-logback
问题:目前的应用程序(application)中已经使用了以下的几种API来进行日志的编程:
- commons-logging
- log4j1
- jdk-loging
现在想统一将日志的底层处理交给logback,也就是说,用多种API编程,用logback实现日志输出。
解决方法:
将上面的日志系统全部转换到slf4j
- 移除commons-logging(可以不移除),使用jcl-over-slf4j(桥接)将commons-logging的底层日志输出切换到sl4j.
- 移除log4j1实现(必须移除),使用log4j-over-slf4j(桥接)将log4j1的日志输出切换到sl4j.
- 使用jul-to-slf4j(桥接),将jul的日志输出切换到slf4j.
让slf4j选择logback作为底层日志输出
- 加入slf4j-api.jar
- 加入logback-core.jar
- 加入logback-classsic.jar
1.旧日志API到slf4j的适配器
介绍之前先说明下日志简称:
- jdk自带的logging->简称 jul (java-util-logging)
- apache commons-logging->简称 jcl (java-commons-logging)
日志适配器 说明 jcl-over-slf4j java-commons-logging API到slf4j的适配 jul-to-slf4j java-util-logging API到slf4j的适配 log4j-over-slf4j log4j 1 API到slf4j的适配 log4j-to-slf4j log4j 2 API到slf4j的适配 maven引用
<!-- JCL 1.2 implemented over SLF4J --> <dependency> <groupId>org.slf4j</groupId> <artifactId>jcl-over-slf4j</artifactId> <version>1.7.24</version> </dependency> <!-- JUL to SLF4J bridge --> <dependency> <groupId>org.slf4j</groupId> <artifactId>jul-to-slf4j</artifactId> <version>1.7.24</version> </dependency> <!-- Log4j implemented over SLF4J --> <dependency> <groupId>org.slf4j</groupId> <artifactId>log4j-over-slf4j</artifactId> <version>1.7.24</version> </dependency> <!-- Log4j 2 to SLF4J Adapter --> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-to-slf4j</artifactId> <version>2.8.1</version> </dependency>
2.slf4j到新日志实现的适配器
日志适配器 说明 slf4j-jcl slf4j到java-commons-logging的适配 slf4j-jdk14 slf4j到java-util-logging实现的适配 slf4j-log4j12 slf4j到log4j 1实现的适配 log4j-slf4j-impl slf4j到log4j 2实现的适配 maven引用
<!-- SLF4J JCL Binding --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-jcl</artifactId> <version>1.7.24</version> </dependency> <!-- SLF4J JDK14 Binding --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-jdk14</artifactId> <version>1.7.24</version> </dependency> <!-- SLF4J LOG4J-12 Binding --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.24</version> </dependency> <!-- SLF4J Log4j 2 Binding --> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-slf4j-impl</artifactId> <version>2.8.1</version> </dependency>
slf4j冲突说明
slf4j官网的冲突说明
其实明白上面介绍的各jar包的作用,就很容易理解为什么会出现冲突了。
冲突的出现主要是形成了日志的事件闭环。就相当于两个人在互相推卸责任。
1. jcl-over-slf4j 与 slf4j-jcl 冲突
- jcl-over-slf4j : commons-logging切换到slf4j
- slf4j-jcl : slf4j切换到commons-logging
jcl把日志输出事件交给slf4j,slf4j又还给了jcl。
如果这两者共存的话,必然造成相互委托,造成内存溢出。
2. log4j-over-slf4j 与 slf4j-log4j12 冲突
- log4j-over-slf4j : log4j1切换到slf4j
- slf4j-log4j12 : slf4j切换到log4j1
log4j1把日志输出事件交给slf4j,slf4j又还给了log4j1。
如果这两者共存的话,必然造成相互委托,造成内存溢出。
3. jul-to-slf4j 与 slf4j-jdk14 冲突
- jul-to-slf4j : jdk-logging切换到slf4j
- slf4j-jdk14 : slf4j切换到jdk-logging
jul把日志输出事件交给slf4j,slf4j又还给了jul。
如果这两者共存的话,必然造成相互委托,造成内存溢出。