spring 5.0以下日志使用 jcl commons-logging commons-logging
通过循环加载相应的类文件,尝试使用log4j、jul等日志工具,直到对应的类存在加载成功。
private static final String[] classesToDiscover = {
LOGGING_IMPL_LOG4J_LOGGER,
"org.apache.commons.logging.impl.Jdk14Logger",
"org.apache.commons.logging.impl.Jdk13LumberjackLogger",
"org.apache.commons.logging.impl.SimpleLog"
};
for(int i=0; (i<classesToDiscover.length) && (result == null); ++i) {
result = createLogFromClass(classesToDiscover[i], logCategory, true);
}
细心的朋友可能会发现尝试加载的那几个类就是自己jar中的,那为什么加载自己jar包中的类会出现不成功呢?原因就在于jcl这个包中有关于log4j的optional=true的依赖,这句话的意思就是说,本身jar是可以编译成功的,那么对外也就是在其他项目中被引用运行的时候,类加载器就会抛出NoClassDefFoundError这个异常,这个异常的意思就是:导入的类存在依赖的类不存在。
spring 5.0以上(包含)日志使用 spring-jcl org.springframework
private static final String LOG4J_SPI = "org.apache.logging.log4j.spi.ExtendedLogger";
private static final String LOG4J_SLF4J_PROVIDER = "org.apache.logging.slf4j.SLF4JProvider";
private static final String SLF4J_SPI = "org.slf4j.spi.LocationAwareLogger";
private static final String SLF4J_API = "org.slf4j.Logger";
private static final LogApi logApi;
static {
if (isPresent(LOG4J_SPI)) {
if (isPresent(LOG4J_SLF4J_PROVIDER) && isPresent(SLF4J_SPI)) {
// log4j-to-slf4j bridge -> we'll rather go with the SLF4J SPI;
// however, we still prefer Log4j over the plain SLF4J API since
// the latter does not have location awareness support.
logApi = LogApi.SLF4J_LAL;
}
else {
// Use Log4j 2.x directly, including location awareness support
logApi = LogApi.LOG4J;
}
}
else if (isPresent(SLF4J_SPI)) {
// Full SLF4J SPI including location awareness support
logApi = LogApi.SLF4J_LAL;
}
else if (isPresent(SLF4J_API)) {
// Minimal SLF4J API without location awareness support
logApi = LogApi.SLF4J;
}
else {
// java.util.logging as default
logApi = LogApi.JUL;
}
}
首先明确几个问题,上面说的那几个类都是什么jar里面的?
| jar | 类 | 版本 |
|---|---|---|
| log4j-api | org.apache.logging.log4j.spi.ExtendedLogger | log4j 2.x版本 |
| log4j-to-slf4j | org.apache.logging.slf4j.SLF4JProvider | log4j 2.x的slf4j桥接 |
| slf4j-api | org.slf4j.spi.LocationAwareLogger | slf4j版本>=1.3 |
| slf4j-api | org.slf4j.Logger | slf4j版本<1.3 |
知道了上面的类出自哪里,那么spring-jcl选择底层日志框架的逻辑就清晰了。
- 引入log4j 2.x
- 引入log4j-to-slf4j以及slf4j-api>=1.3,那么选择slf4j新版本
- 相反,选择log4j 2.x
- slf4j-api>=1.3,那么选择slf4j新版本
- slf4j-api<1.3,那么选择slf4j老版本
看了上面的逻辑,要么选择log4j 2.x版本直接打印日志,要么选择的结果是slf4j,但是它不具体执行打印日志操作,具体实现需要靠slf4j的门面模式,即需要绑定一个具体的实现。
由此诞生了很多的binder以及解决同一项目多个日志实现问题的bridge(桥接)。
常见的binder实现比如:log4j、logback、jcl、jul等
常见的bridge实现比如:jcl-over-slf4j、log4j-over-slf4j等
387

被折叠的 条评论
为什么被折叠?



