概述
本文的目的是梳理Java中各种日志Log之间是怎么的关系,如何作用、依赖,好让我们平时在工作中如果遇到“日志打不出”或者“日志jar包冲突”等之类的问题知道该如何入手解决,以及在各种场景下如何调整项目中的各个框架的日志输出,使得输出统一。
java日志组件发展历程
1、log4j(作者Ceki Gülcü)出来时就等到了广泛的应用(注意这里是直接使用),是Java日志事实上的标准,并成为了Apache的项目
2、Apache要求把log4j并入到JDK,SUN拒绝,并在jdk1.4版本后增加了JUL(java.util.logging)
毕竟是JDK自带的,JUL也有很多人用。同时还有其他日志组件,如SimpleLog等。这时如果有人想换成其他日志组件,如log4j换成JUL,因为api完全不同,就需要改动代码。
3、Apache见此,开发了JCL(Jakarta Commons Logging),即commons-logging-xx.jar。它只提供一套通用的日志接口api,并不提供日志的实现。很好的设计原则嘛,依赖抽象而非实现。这样应用程序可以在运行时选择自己想要的日志实现组件。
4、这样看上去也挺美好的,但是log4j的作者觉得JCL不好用,自己开发出slf4j,它跟JCL类似,本身不替供日志具体实现,只对外提供接口或门面。目的就是为了替代JCL。同时,还开发出logback,一个比log4j拥有更高性能的组件,目的是为了替代log4j。
5、Apache参考了logback,并做了一系列优化,推出了log4j2
java日志组件的市场现状
JCL
JCL方式的commons-logging 是动态查找绑定。默认使用jul实现,如果要替换其他的则需要将日志实现包引入,例如log4j
例如: jcl+log4j 通过在类路径加载 org.apache.commons.logging.Log的实现类来动态决定日志实现组件
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class JCLTest {
private static final Log logger = LogFactory.getLog(JCLTest.class);
public static void main(String[] args) {
logger.debug("DEBUG ...");
logger.info("INFO ...");
logger.error("ERROR ...");
}
}
SLF4J
因为当时Java的日志组件比较混乱繁杂,Ceki Gülcü推出slf4j后,也相应为行业中各个主流日志组件推出了slf4j的适配
slf4j通过静态绑定(org.slf4j.impl.StaticLoggerBinder)日志组件,新版是通过SPI机制绑定,需要加桥接包。如slf4j-log4j2
slf4j生态图
图的意思为如果你想用slf4j作为日志门面的话,你如何去配合使用其他日志实现组件,这里说明一下(注意jar包名缺少了版本号,在找版本时也要注意版本之间是否兼容)
- slf4j + log4j(log4j实现)
slf4j-api.jar + slf4j-log4j12.jar + log4j.jar - slf4j + jul(JDK自带的logging实现)
slf4j-api.jar + slf4j-jdk14.jar - slf4j + logback(logback是slf4j-api的天然实现,不需要桥接即可使用)
slf4j-api.jar + logback-classic.jar - slf4j + log4j2(log4j2实现)
slf4j-api.jar + log4j-slf4j-impl - 也可以只用slf4j无日志实现
slf4j-api.jar + slf4j-nop.jar
SLF4J + Log4j
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
</dependency>
log4j.properties
log4j.rootLogger=DEBUG,console
# 输出到控制台
log4j.appender.console=org.apache.log4j.ConsoleAppender
# 设置输出样式
log4j.appender.console.layout=org.apache.log4j.PatternLayout
# 日志输出信息格式为
log4j.appender.console.layout.ConversionPattern=[%-d{yyyy-MM-dd HH:mm:ss}]-[%t-%5p]-[%C-%M(%L)]: %m%n
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* slf4j + log4j
*
* @author MetaYoo
*/
public class Slf4jLog4jTest {
private static final Logger logger = LoggerFactory.getLogger(Slf4jLog4jTest.class);
public static void main(String[] args) {
logger.debug("DEBUG ...");
logger.info("INFO ...");
logger.error("ERROR ...");
}
}
SLF4J桥接
很多项目中会有这种需求,原来项目中使用的是其他日志api,如log4j,jul,jcl等,在不能改变代码的前提下,想把APP的日志重定向到实现了slf4j-api的某种日志实现中
如:
其实总的来说,无论就是以下几种情况
- 你在用JCL使用jcl-over-slf4j.jar适配
- 你在用log4j使用log4j-over-slf4j.jar适配
- 你在用JUL使用jul-to-slf4j.jar适配
SLFJ4体系
让Spring统一输出
这就是为了对slf4j的适配做一个例子说明。
Spring是用JCL作为日志门面的,那我们的应用是slf4j + logback,怎么让Spring也用到logback作为日志输出呢?这样的好处就是我们可以统一项目内的其他模块、框架的日志输出(日志格式,日志文件,存放路径等,以及其他slf4j支持的功能)
很简单,就是加入jcl-over-slf4j.jar就好了。
适配思路
首先确认需要统一日志的模块、框架是使用哪个日志组件的,然后再找到sfl4j的适配器。
记得去掉无用的日志实现组件,只保留你要用的。