日志体系(二) 认识spring 5.0.x中的spring-jcl jar包,任意替换默认日志框架jul为log4j

前言

  • 上篇博客:日志体系(一)JCL + JUL + LOG4J大概介绍了一下apache公司开源的jcl这么一款抽象的日志框架,我们可以借助它来随意更换框架中使用的日志框架。
  • 由于jcl的自我抛弃,不再进行维护了。但是框架总归是要记录日志的。所以对于spring 5.0.x框架而言,它自己提供了一个jcl框架spring-jcl。下面我们开始认识下它

一、spring 5.0.x版本spring-jcl

1.1 jar包结构

日志体系(二) 认识spring 5.0.x中的spring-jcl jar包,任意替换默认日志框架jul为log4j_第1张图片

  • Log.java。此接口抽象了日志的一些行为,比如记录info级别的日志、warn级别的日志等等
    public interface Log {
    
    	boolean isFatalEnabled();
    
    	boolean isErrorEnabled();
    
    	boolean isWarnEnabled();
    
    	boolean isInfoEnabled();
    
    	boolean isDebugEnabled();
    
    	boolean isTraceEnabled();
    
    	void fatal(Object message);
    
    	void fatal(Object message, Throwable t);
    
        void error(Object message);
    
        void error(Object message, Throwable t);
    
    	void warn(Object message);
    
    	void warn(Object message, Throwable t);
    
        void info(Object message);
    
        void info(Object message, Throwable t);
    
    	void debug(Object message);
    
    	void debug(Object message, Throwable t);
    
        void trace(Object message);
    
        void trace(Object message, Throwable t);
    
    }
    
  • LogFactory.java 创建Log类型的日志实例的工厂,里面大致做的事情就是根据当前项目中存在哪些日志的依赖来决定初始化哪种日志. 初始化顺序如下:
        // Log4j 2.x API 
    	1. org.apache.logging.log4j.spi.ExtendedLogger
    	// SLF4J 1.7 API
    	2. org.slf4j.spi.LocationAwareLogger
    	//  Try SLF4J 1.7 API
    	3. org.slf4j.Logger
    	// Default jul
    	4. java.util.logging.Logger
    
  • NoOpLog.java是Log接口的一个实现类,在spring-jcl中只有里面的SimpleLog.java类使用了它,但是SimpleLog.java类被spring使用@Deprecated注解标识了,这代表着,spring不使用它了,于是我们直接忽略NoOpLog.java和SimpleLog.java类。所以整个spring-jcl中,我们唯一要看的就是一个类LogFactory.java

1.2 spring-jcl之LogFactory类

  • 本次我们不总结日志的具体打印原理,只总结spring-jcl是如何根据依赖的jar包来生产对应的log对象的。主要看LogFactory的两个地方:静态代码块和getLog方法
  • LogFactory.java静态代码块
    private static LogApi logApi = LogApi.JUL;
    
    static {
    	ClassLoader cl = LogFactory.class.getClassLoader();
    	try {
    		// Try Log4j 2.x API
    		cl.loadClass("org.apache.logging.log4j.spi.ExtendedLogger");
    		logApi = LogApi.LOG4J;
    	}
    	catch (ClassNotFoundException ex1) {
    		try {
    			// Try SLF4J 1.7 SPI
    			cl.loadClass("org.slf4j.spi.LocationAwareLogger");
    			logApi = LogApi.SLF4J_LAL;
    		}
    		catch (ClassNotFoundException ex2) {
    			try {
    				// Try SLF4J 1.7 API
    				cl.loadClass("org.slf4j.Logger");
    				logApi = LogApi.SLF4J;
    			}
    			catch (ClassNotFoundException ex3) {
    				// Keep java.util.logging as default
    			}
    		}
    	}
    }
    
    由源码可知,spring-jcl加载日志的方式和apache开源出来的jcl加载日志的方式思想基本一致(这就是抽象的魅力)。spring-jcl LogFactory使用当前类的类加载器根据当前classpath中存在哪种jar包最终获取到对应的Class类赋值给logApi这个静态枚举对象(这个枚举可取: LOG4J, SLF4J_LAL, SLF4J, JUL四个值)。若在学习spring源码时,只引入了spring-context模块,那么此时logApi一定等于JUL。所以打印的是JUL的日志(上篇博客中特意提过,jul的日志的字体是红色的),如下图所示:(这要一定要注册一个bean进去,否则的话spring不会执行refresh方法,进而不会打印出日志!)
    日志体系(二) 认识spring 5.0.x中的spring-jcl jar包,任意替换默认日志框架jul为log4j_第2张图片
    刚刚看了,spring-jcl的加载顺序为Log4j 2.xTry SLF4J 1.7 SPISLF4J 1.7 API。那我们现在按照源码中的提示添加Log4j 2.x试试

1.3 切换日志源为LOG4J

  • 因为在源码中有提示,需要加载的Log4j是2.x之后的版本,源码中有提供一个类:org.apache.logging.log4j.spi.ExtendedLogger。按照java包名的开发规范,这应该是apache开发的一个项目,于是我们去maven搜索下log4j, 找到2.12.1版本(主要要选择groupId为****),并添加依赖
    日志体系(二) 认识spring 5.0.x中的spring-jcl jar包,任意替换默认日志框架jul为log4j_第3张图片
    maven添加依赖如下:
    	<dependency>
            <groupId>org.apache.logging.log4jgroupId>
            <artifactId>log4j-apiartifactId>
            <version>2.12.1version>
        dependency>
    
        <dependency>
            <groupId>org.apache.logging.log4jgroupId>
            <artifactId>log4j-coreartifactId>
            <version>2.12.1version>
        dependency>
    
    于是我们启动项目,纳尼,日志不打印了, 反而打印了这么一句话:
    ERROR StatusLogger No Log4j 2 configuration file found. Using default configuration (logging only errors to the console), or user programmatically provided configurations. Set system property 'log4j2.debug' to show Log4j 2 internal initialization logging. See https://logging.apache.org/log4j/2.x/manual/configuration.html for instructions on how to configure Log4j 2
    
    大致的意思就是需要一个配置文件。可以给jvm添加参数log4j2.debug来显示log4j 2.x内部初始化日志,可以按照如下图所示方式配置, 这里就不演示了。
    日志体系(二) 认识spring 5.0.x中的spring-jcl jar包,任意替换默认日志框架jul为log4j_第4张图片
    我们言归正传,添加一个名叫log4j.xml的配置文件,并配置如下内容(这个配置的由来就是上面抛出异常指定的url, 只不过我做了修改,修改了打印日志的级别为dubug及以上):
    
    <Configuration status="debug">
        <Appenders>
            <Console name="Console" target="SYSTEM_OUT">
                <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
            Console>
        Appenders>
        <Loggers>
            <Root level="info">
                <AppenderRef ref="Console"/>
            Root>
        Loggers>
    Configuration>
    
    最终的项目结构和运行结果如下:
    日志体系(二) 认识spring 5.0.x中的spring-jcl jar包,任意替换默认日志框架jul为log4j_第5张图片,可能这个理由还不够足以证明打印日志的是log4j对象, 这样,我们查看打印spring日志的方法处打断点查看(在AbstractApplicationContext类的prepareRefresh方法下), 如下图:日志体系(二) 认识spring 5.0.x中的spring-jcl jar包,任意替换默认日志框架jul为log4j_第6张图片
    综上所述,我们已经将spring5.0.x自带的jul日志切换成log4j了。切换其他的日志源也同理。

二、总结

  • 所有日志的对象都是从依赖的jar中生成的,所以关于日志的一些配置,比如支持打印的日志登记、打印的格式都是由对应的日志来决定的。比如上说的log4j日志,使用的就是自己的log4j.xml配置文件来指定要打印的日志级别
  • spring为了集成日志框架,自己开发了一个spring版本的jcl
  • I am a slow walker, but I never walk backwards

你可能感兴趣的:(日志体系)