spring日志相关jcl slf4j

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等

你可能感兴趣的:(spring日志相关jcl slf4j)