1 日志框架场景:
某项目开发人员二蛋,为了了解项目运行情况,在我们代码中加入了,System.out.println("")来记录 日志,有一天, 项目经理觉得通过这种,System.out.println("")的方式很 low,要他把把代码中的,System.out.println 给去掉,但是过了几天之后,项目出问题了,查询很棘手又没有日志,然后经理又要求他把 System.out.println 加上...然后又去掉...又加上...
情况一: 二蛋是一个脾气暴躁的人,拿起板砖就跟经理干起来,然后牢底坐穿...全剧终... 没用案例场景...下课...
情况二: 二蛋为了解决 System.out.println("")比较 low 的情况,然后就写了一个记录日志的 jar 包名称叫 angle-logging.jar 用来替代 System.out.println(""),二蛋是一个爱专研的人,想出了一写比较牛逼的点子...比如日志异步记录, 日志归档...然后 起名叫 angle-logging-good.jar, 然后把项目中原来的 angle-logging.jar 中的卸下来,然后安装新的框架 angle-logging-good.jar,但是 由于二种方式实现的接口可能不一样,需要修改代码中的日志打印类。这种情况 这么办?此时二蛋,想到了一个好的点子,我把angle-logging.jar 和 angle-logging-good.jar 的功能都抽取出来形 成一个门面 angle-logging-intf.ja(r 也就是我们的接口)
然后在二个日志框架中实现不同的功能.这 样,我的业务代码中直接使用的是我们的 angle-logging-intf.jar 的方法, 然后根据需要导入了 angle- logging.jar 或者 angle-logging-good.jar
2. 我们 Java 中常用的日志框架是什么?
2.1 我们常常听说的就是如下的日子框架,还不知道怎么选?
(1) JUL(java.util.logging) , (2) JCL(Jakarta Commons-Logging) 由 apache 公司 Jakarta 小组开发的, (3) JBoss-logging (4) logback (5) log4j (6) log4j2 (7) slf4j(Simple Logging Facade for Java.)
我们 Spring 底层选择的是我们的这个 JCL 做为日志门面的 SpringBoot 选择的是 SLF4J 做为我们的日志门面(当时 log4j,和 logback)他选择了 logback
2.2 加入我们系统使用的是 SLF4J 作为日志门面,我们是如何匹配?
-
app(我们的应用系统)+日志门面(Slf4j slf4j-api.jar)+不加我们的日志实现 不会记录日志
-
app(我们的应用系统)+日志门面(Slf4j slf4j-api.jar)+logback(logback-classic.jar,logback-core.jar)
-
app(我们的应用系统)+日志门面(Slf4j slf4j-api.jar)+适配器(因为早期 log4j 压根不知道有一slf4j 的日志门面,所以降入适配器slf4j-log412.jar 实现我们slf4j 的接口,真正实现功能调用我们的 log4j.jar)+log4j.jar
-
app(我们的应用系统)+日志门面(Slf4j slf4j-api.jar)+适配器(因为早期 juc 压根不知道有一 slf4j 的 日志门面,所以降入适配器 slf4j-jdk14.jar 实现我们 slf4j 的接口,真正实现功能调用我们的 juc 的 jar 包的功 能)+juc.jar
-
app(我们的应用系统)+日志门面(Slf4j slf4j-api.jar)+slf4j(slf4j-simple.jar)
2.2.1 第一种情况
(我们业务系统直接使用的是 spring 底层用的日志框架 jcl+jdk 的日志实现)
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.20.RELEASE</version>
</dependency>
public class MainClass {
private static Logger logger = Logger.getLogger(MainClass.class.getName()); public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MainConfig.class);
logger.info("hello tuling"); ctx.start();
}
}
打印的日志格式是:(我们发现都是红色的)
2.2.2 第二种情况
(我们往 pom 依赖中加入 log4j 的包以及加入 log4j 的配置文件,spring 底层的日志门面使用的是 jcl,实现貌似 使用了是 log4j 的格式)
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.20.RELEASE</version>
</dependency>
<!--log4j 的日志-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
import org.apache.log4j.Logger;
private static Logger logger = Logger.getLogger(MainClass.class.getName()); public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MainConfig.class);
logger.info("hello tuling"); ctx.start();
}
日志打印情况:
2.2.3 第三种情况
(我们往容器中导入了是 slf4j 的门面,使用 log4j 的实现)
slf4j 的门面
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.10</version>
</dependency>
该包是转换包,实现 slf4j-api 接口,调用 log4j 的实现
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.10</version>
</dependency>
log4j 的日志实现
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MainClass {
private static Logger logger = LoggerFactory.getLogger(MainClass.class);
//private static Logger logger = Logger.getLogger(MainClass.class.getName());
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MainConfig.class);
logger.info("hello tuling"); ctx.start();
}
}
日志打印情况:
2.2.4 第四种情况
我们往容器中导入了是 slf4j 的门面,使用 logback 的实现加入的依赖:
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.7</version>
</dependency>
import org.slf4j.Logger;
import org.slf4j.LoggerFactory; public class MainClass {
private static Logger logger = LoggerFactory.getLogger(MainClass.class);
//private static Logger logger = Logger.getLogger(MainClass.class.getName());
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MainConfig.class);
logger.info("hello tuling");
ctx.start();
}
}
3 Spring4.Xspring 底层使用的日志技术
我们从这里可以看出来,spring4.x 获取的日志对象中,LOGGer 对象是 jCL 的,而他底层搭配的技术点就是先去找 log4j 的日志实现,若没有找到,底层去找 jdk 的日志框架。压根不支持 logback,log4j2 的日志技术。
4 Spring5.x 底层使用的日志技术
private static LogApi logApi = LogApi.JUL; static {
ClassLoader cl = LogFactory.class.getClassLoader(); try {
//第一步:先尝试去加载 log4j2 的日志框架cl.loadClass("org.apache.logging.log4j.spi.ExtendedLogger"); logApi = LogApi.LOG4J;
} catch (ClassNotFoundException ex1) { try {
//第二步:尝试去加载
LogApi.SLF4J_LAL cl.loadClass("org.slf4j.spi.LocationAwareLogger"); logApi = LogApi.SLF4J_LAL;
} catch (ClassNotFoundException ex2) {
//尝试去加载 slf4j 的日志实现cl.loadClass("org.slf4j.Logger"); logApi = LogApi.SLF4J;
} catch (ClassNotFoundException ex3) {
//使用原生的 JUL
}
}
}
}
public static Log getLog(String name) { switch (logApi) {
case LOG4J:
return Log4jDelegate.createLog(name); case SLF4J_LAL:
return Slf4jDelegate.createLocationAwareLog(name); case SLF4J:
return Slf4jDelegate.createLog(name); default:
// Defensively use lazy-initializing delegate class here as well since the
// java.logging module is not present by default on JDK 9. We are requiring
// its presence if neither Log4j nor SLF4J is available; however, in the
// case of Log4j or SLF4J, we are trying to prevent early initialization
// of the JavaUtilLog adapter - e.g. by a JVM in debug mode - when eagerly
// trying to parse the bytecode for all the cases of this switch clause. return JavaUtilDelegate.createLog(name);
}
}
文章到这就结束了,本文只是spring笔记的一部分,如需要可关注微信公众号“老周扯IT”
我正在参与掘金技术社区创作者签约计划招募活动