0
点赞
收藏
分享

微信扫一扫

spring5和spring4 日志新特性

青乌 2023-01-10 阅读 123


spring5和spring4 日志新特性

  • ​​使用现象对比​​
  • ​​spring 4​​
  • ​​spring5.0.9.RELEASE​​
  • ​​spring4 和 5 依赖关系图​​
  • ​​spring4 日志实现原码​​
  • ​​spring5 源码​​

使用现象对比

我们看看我们经常使用的框架日志是怎么玩的? 这一段时间一直在研究这个日志相关的东西,这些比较流行的框架如何玩?

我们先看看spring4 和 spring 5运行时的区别:

spring 4

没有增加log4j

spring5和spring4 日志新特性_svn


增加log4j

spring5和spring4 日志新特性_java_02

spring5.0.9.RELEASE

没有增加log4j

spring5和spring4 日志新特性_spring_03


增加log4j

spring5和spring4 日志新特性_spring_04

总结: spring4 默认实现jcl 打印日志,增加log4j后便使用log4j打印日志
spring5 默认实现jcl打印日志,但是(jcl已经修改),增加log4j后也不会使用log4j打印日志:

spring5低版本日志是开启的,高版本默认是关闭的(我使用5.3.9这个版本的时候发现的,具体哪个版本开始改的就不知道了没纠结)

if (this.logger.isDebugEnabled()) {
if (this.logger.isTraceEnabled()) {
this.logger.trace("Refreshing " + this);
} else {
this.logger.debug("Refreshing " + this.getDisplayName());
}
}
org.apache.commons.logging.LogAdapter.JavaUtilLog#isDebugEnabled
public boolean isDebugEnabled() {
这个值恒为false
return this.logger.isLoggable(Level.FINE);
}

spring4 和 5 依赖关系图

spring5和spring4 日志新特性_git_05


spring5和spring4 日志新特性_svn_06

spring4 日志实现原码

org.apache.commons.logging.impl.LogFactoryImpl#discoverLogImplementation
真正的log方法创建入口:

private Log discoverLogImplementation(String logCategory) throws LogConfigurationException {
if (isDiagnosticsEnabled()) {
this.logDiagnostic("Discovering a Log implementation...");
}

this.initConfiguration();
Log result = null;
String specifiedLogClassName = this.findUserSpecifiedLogClassName();
if (specifiedLogClassName != null) {
if (isDiagnosticsEnabled()) {
this.logDiagnostic("Attempting to load user-specified log class '" + specifiedLogClassName + "'...");
}

result = this.createLogFromClass(specifiedLogClassName, logCategory, true);
if (result == null) {
StringBuffer messageBuffer = new StringBuffer("User-specified log class '");
messageBuffer.append(specifiedLogClassName);
messageBuffer.append("' cannot be found or is not useable.");
this.informUponSimilarName(messageBuffer, specifiedLogClassName, "org.apache.commons.logging.impl.Log4JLogger");
this.informUponSimilarName(messageBuffer, specifiedLogClassName, "org.apache.commons.logging.impl.Jdk14Logger");
this.informUponSimilarName(messageBuffer, specifiedLogClassName, "org.apache.commons.logging.impl.Jdk13LumberjackLogger");
this.informUponSimilarName(messageBuffer, specifiedLogClassName, "org.apache.commons.logging.impl.SimpleLog");
throw new LogConfigurationException(messageBuffer.toString());
} else {
return result;
}
} else {
if (isDiagnosticsEnabled()) {
this.logDiagnostic("No user-specified Log implementation; performing discovery using the standard supported logging implementations...");
}

for(int i = 0; i < classesToDiscover.length && result == null; ++i) {
result = this.createLogFromClass(classesToDiscover[i], logCategory, true);
}

if (result == null) {
throw new LogConfigurationException("No suitable Log implementation");
} else {
return result;
}
}
}

for(int i = 0; i < classesToDiscover.length && result == null; ++i) {
result = this.createLogFromClass(classesToDiscover[i], logCategory, true);
}

private static final String[] classesToDiscover = new String[]{"org.apache.commons.logging.impl.Log4JLogger", "org.apache.commons.logging.impl.Jdk14Logger",
"org.apache.commons.logging.impl.Jdk13LumberjackLogger",
"org.apache.commons.logging.impl.SimpleLog"};

从上面的这个数组我们可以看出来,如果没有引入 org.apache.commons.logging.impl.Log4JLogger 就默认使用 org.apache.commons.logging.impl.Jdk14Logger 这个是jdk自带的,我们的java代码都是使用的jdk所以后面的基本不会使用,

由此可以看出spring4 我们引入log4i就会被加载

通过分析jcl的代码可以得到:
jcl本身不实现日志记录,但是提供了记录日志的抽象方法即接口(info,debug,error…)
底层通过一个数组存放具体的日志框架的类名,然后循环数组依次去匹配这些类名是否在项目中被依赖了,如果找到被依赖的则直接使用,所以他有先后顺序。
下图为jcl中存放日志技术类名的数组,默认有四个,后面两个可以忽略。

jcl特点:
他不直接记录日志,他是通过第三方记录日志(jul)。
jcl是一个接口,默认有4个log实现类。

spring5 源码

Spring4当中依赖jcl,即Spring4当中采用的日志技术是jcl:commons-logging,即默认使用jul;加入log4j依赖和配置,即可切换为log4j
Spring5当中也是使用了jcl:spring-jcl,是重写为了jul框架。spring5使用的spring的jcl(spring改了jcl的代码)来记录日志的,但是jcl不能直接记录日志,采用循环优先的原则。

public static Log getLog(Class<?> clazz) {
return getLog(clazz.getName());
}
org.apache.commons.logging.LogFactory#getLog(java.lang.String)
//name 是使用日志的类名,我们再找一下logApi 这个变量的初始化
public static Log getLog(String name) {
switch(logApi) {
case LOG4J:
return LogFactory.Log4jDelegate.createLog(name);
case SLF4J_LAL:
return LogFactory.Slf4jDelegate.createLocationAwareLog(name);
case SLF4J:
return LogFactory.Slf4jDelegate.createLog(name);
default:
return LogFactory.JavaUtilDelegate.createLog(name);
}
}

private static LogFactory.LogApi logApi;

从下面的代码我们可以看出,spring5开始不再支持log4j而是直接支持log4j2

static {
logApi = LogFactory.LogApi.JUL;
ClassLoader cl = LogFactory.class.getClassLoader();

//第一步找log4j2
//第二步找slf4j
//第三步使用默认的jcl
try {

cl.loadClass("org.apache.logging.log4j.spi.ExtendedLogger");
logApi = LogFactory.LogApi.LOG4J;
} catch (ClassNotFoundException var6) {
try {
cl.loadClass("org.slf4j.spi.LocationAwareLogger");
logApi = LogFactory.LogApi.SLF4J_LAL;
} catch (ClassNotFoundException var5) {
try {
cl.loadClass("org.slf4j.Logger");
logApi = LogFactory.LogApi.SLF4J;
} catch (ClassNotFoundException var4) {
}
}
}

}

从上面spring5的源码可以看到,spring5使用的日志是spring-jcl,默认是jul,然后会依次加载log4j2,slf4j。在都加载不到的情况下,就使用默认的jul日志技术了。
因为spring5使用的是log4j2,所以在加入了log4j的依赖和配置文件,是不生效的。

这样我们就可以想想一下,目前不是spring5不支持log4j,但是我们的项目就是用的log4j又怎么办,
使用桥接器:

<dependencies>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.9.RELEASE</version>
</dependency>


<!--slf4j依赖(可省略)-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.32</version>
</dependency>

<!--slf4jbind绑定器,将slf4j绑定到log4j(已经包含了slf4j和log4j的依赖)-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.32</version>
</dependency>


</dependencies>

这样就实现了使用log4j;


举报

相关推荐

0 条评论