一文带你了解Jcl日志框架

全称为Jakarta Commons Logging,是Apache提供的一个通用日志API。

它是为 "所有的Java日志实现"提供一个统一的接口,它自身也提供一个日志的实现,但是功能非常常弱(SimpleLog)。所以一般不会单独使用它。他允许开发人员使用不同的具体日志实现工具: Log4j, Jdk自带的日志(JUL)。

JCL 有两个基本的抽象类:Log(基本记录器)和LogFactory(负责创建Log实例)。

在这里插入图片描述

JCL入门

  1. 建立maven工程
  2. 添加依赖
 <dependencies>
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.2</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF—8</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>
  1. 入门代码
	 @Test public void testQuick() throws Exception {
        // 创建日志对象
        /**
         * LogFactory是一个抽象类
         */
        Log log = LogFactory.getLog(Jcl_Test.class);
        /**
         * 执行之后,先是获取了当前的类加载对象,进入了getInstance()方法,返回了当前类的全名称
         */
        // 日志记录输出
        log.fatal("fatal");
        log.error("error");
        log.warn("warn");
        log.info("info");
        log.debug("debug");
    }

我们为什么要使用日志门面:

  1. 面向接口开发,不再依赖具体的实现类。减少代码的耦合
  2. 项目通过导入不同的日志实现类,可以灵活的切换日志框架
  3. 统一API,方便开发者学习和使用
  4. 统一配置便于项目日志的管理

源码分析

点Log接口 对应Log 接口按shift + Alt + b

在这里插入图片描述
进入JDK14Logger 类

//注意这里引入的类是JDK自带的日志JUL的Logger类
import java.util.logging.Level;
import java.util.logging.Logger;

public class Jdk14Logger implements Log, Serializable {
    
    protected static final Level dummyLevel = Level.FINE;
    protected transient Logger logger = null;//默认为null
	
    public Jdk14Logger(String name) {
        this.name = name;
        //调用了自身的getLogger()方法
        logger = getLogger();//见1
    }
    
    //1、获取Logger实例方法
    public Logger getLogger() {
        if (logger == null) {
            //重要:这里调用的是java.util.logging.Logger(即JUL)的获取实例方法
            logger = Logger.getLogger(name);
        }
        return logger;
    }
    
    //info日志等级调用
    public void info(Object message) {
        //实际上就是调用的JUL的INFO日志等级的Log方法
        log(Level.INFO, String.valueOf(message), null);
    }
    
    //fatal:我们之前在Log4j中看到是最高等级,这里也用来表示最高的意思
    public void fatal(Object message, Throwable exception) {
        //实际上就是调用JUL的SERVER日志等级(最高)的Log方法
        log(Level.SEVERE, String.valueOf(message), exception);
    }
}

jdk14Logger 类下的实现方法
在这里插入图片描述

看到这里其实心里就清楚了,原来就是外面套了个方法呀,通过继承Log接口来让Log接口统一管理各个日志框架,从而达到切换日志框架不改变代码的操作。那对于JCL中Log4j的方法我们也差不多知道了是如何实现了的。

先放入Log4j 的maven依赖

<dependency>
      <groupId>log4j</groupId>
       <artifactId>log4j</artifactId>
       <version>1.2.17</version>
</dependency>

进入Log4jLogger 界面

在这里插入图片描述
都实现了Log 接口 的日志等级方法

查看构造器以及静态方法代码块

//注意这里使用的是Log4j的日志
import org.apache.log4j.Logger;
import org.apache.log4j.Priority;
import org.apache.log4j.Level;

public class Log4JLogger implements Log, Serializable {

        //默认为null
		private transient volatile Logger logger = null;
    	private static final String FQCN = Log4JLogger.class.getName();
		
        //类被加载时进行初始化
        static {
            if (!Priority.class.isAssignableFrom(Level.class)) {
                throw new InstantiationError("Log4J 1.2 not available");
            }
            
            Priority _traceLevel;
            try {
            } catch(Exception ex) {
                _traceLevel = Level.DEBUG;
            }
            traceLevel = _traceLevel;
        }
    
        //无参构造器
        public Log4JLogger() {
            name = null;
        }
    
        //有参构造器
        public Log4JLogger(String name) {
            this.name = name;
            //通过调用自身类的getLogger()获取
            this.logger = getLogger();//见1
        }
    
       //1、通过org.apache.log4j.Logger的getLogger()来获取实例
       public Logger getLogger() {
            Logger result = logger;
            if (result == null) {
                synchronized(this) {
                    result = logger;
                    if (result == null) {
                        logger = result = Logger.getLogger(name);
                    }
                }
            }
            return result;
        }
        
        //fatal等级方法就是调用的Log4j的FATAL等级,是一致的
        public void fatal(Object message) {
            getLogger().log(FQCN, Level.FATAL, message, null);
        }
}

JCL原理分析

分析如下代码块

	@Test 
    public void testQuick() throws Exception {
        // 创建日志对象
        Log log = LogFactory.getLog(Jcl_Test.class);
       
        // 日志记录输出
        log.fatal("fatal");
        log.error("error");
        log.warn("warn");
        log.info("info");
        log.debug("debug");
    }

点进LogFactory 抽象类

public abstract class LogFactory {
    
    //1、实际调用了其实现类的方法
    public static Log getLog(Class clazz) throws LogConfigurationException {
        //获取LogFactoryImpl
        return getFactory().getInstance(clazz);//见2
    }
}

public class LogFactoryImpl extends LogFactory {
    
    protected Constructor logConstructor = null;
    //该数组中包含了对应的全限定类名
    private static final String[] classesToDiscover = {
            LOGGING_IMPL_LOG4J_LOGGER,//"org.apache.commons.logging.impl.Log4JLogger"
            "org.apache.commons.logging.impl.Jdk14Logger",
            "org.apache.commons.logging.impl.Jdk13LumberjackLogger",
            "org.apache.commons.logging.impl.SimpleLog"
    };
    
    //2、获取Log实例
    public Log getInstance(Class clazz) throws LogConfigurationException {
        //这里传入类的名称
        return getInstance(clazz.getName());//见3
    }
    
    //3、其中获取到Log实例
    public Log getInstance(String name) throws LogConfigurationException {
        Log instance = (Log) instances.get(name);
        if (instance == null) {
            //通过类名获取到Log实例
            instance = newInstance(name);//见4
            instances.put(name, instance);
        }
        return instance;
    }
    
    //4、获取新的实例
    protected Log newInstance(String name) throws LogConfigurationException {
        Log instance;
        try {
            //默认为null
            if (logConstructor == null) {
                //重要:查找Log实现类,及获取Log的单个实现类
                instance = discoverLogImplementation(name);//见5
            }
            else {
                Object params[] = { name };
                instance = (Log) logConstructor.newInstance(params);
            }
            if (logMethod != null) {
                Object params[] = { this };
                logMethod.invoke(instance, params);
            }
            //返回获取到的Log实例即可
            return instance;
        } catch (LogConfigurationException lce) {
		....
    }
        
    //5、该方法通过数组的顺序来进行实现类实例
    private Log discoverLogImplementation(String logCategory)
        throws LogConfigurationException {
        
        ...//无关紧要的先省略
        
        if (isDiagnosticsEnabled()) {
            logDiagnostic(
                "No user-specified Log implementation; performing discovery" +
                " using the standard supported logging implementations...");
        }
        //核心部分:从数组中进行遍历,可见classesToDiscover数组内容(上面),顺序依次为Log4JLogger、Jdk14Logger...
        //其中的result == null则是判断是否获取到了实例,若获取到退出for循环
        for(int i=0; i<classesToDiscover.length && result == null; ++i) {
            result = createLogFromClass(classesToDiscover[i], logCategory, true);
        }

        if (result == null) {
            throw new LogConfigurationException
                        ("No suitable Log implementation");
        }
		//返回指定实例
        return result;
    }

第38行:调用方法获取到Log的实现类,确定使用的日志框架。
第73行:核心代码,根据多种框架实现类的权限定类名去查找获取Log实例的

说明:我们在调用LogFactory.getLog()方法时获取了JCL中Log接口的实现类实例,在实现类中的构造器里获取到了真正对应的日志框架Logger实例。

这样的话我们之前的第二部分就解释的通为什么默认获取到的是JUL,在添加了jar包之后则为Log4j了!!!

总结

1、JCL日志门面中Log是通用接口,LogFactory.getLog()用于获取对应Log的实现类。

2、Log接口(包含了主要的日志等级方法)的实现类主要的是两个,Jdk14Logger(即JUL,不导任何包默认)和Log4JLogger(即Log4j,导入Log4j则切换)。

3、对于获取Log实例是在LogFactory.getLog()中执行获取的,执行顺序见如下核心代码

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

//核心部分:从数组中进行遍历,可见classesToDiscover数组内容(上面),顺序依次为Log4JLogger、Jdk14Logger...
//其中的result == null则是判断是否获取到了实例,若获取到退出for循环
for(int i=0; i<classesToDiscover.length && result == null; ++i) {
    result = createLogFromClass(classesToDiscover[i], logCategory, true);
}

4、在获取到指定Log实例时,即使用构造器会进行各个实现类中的Logger实例的赋值获取(通过对应的日志框架),其中的日志等级方法调用的是对应框架的日志等级方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值