Apache Commons-Logging 源码分析

Log继承层次

Log.java

package org.apache.commons.logging;

/**
 * 一个简单的记录日志的抽象API
 * 为了通过 LogFactory 成功实例化,实现此接口的类必须提供一个构造函数
 * 该构造函数接受一个表示此 Log 的名字的String类型的参数
 * 
 * Log 使用的六个日志记录级别(按顺序):
 * trace (最不严重)
 * debug
 * info
 * warn
 * error
 * fatal (最严重)
 * 
 * 这些日志级别映射到底层日志记录系统使用的概念是依赖于实现的,但是实现应该确保这个排序按预期方式进行
 * 性能通常是日志记录所关心的问题
 * 通过检查适当的属性,组件能够避免昂贵的操作(产生要记录的信息)
 *    if (log.isDebugEnabled()) {
 *        ... do something expensive ...
 *        log.debug(theResult);
 *    }
 * 底层日志记录系统的配置通常将通过该系统支持的任何机制在日志记录API的外部进行
 */
public interface Log {

    /**
     * 使用调试日志级别记录消息
     */
    void debug(Object message);

    /**
     * 使用调试日志级别记录错误
     *
     * @param message 记录的消息
     * @param t 记录导致该错误的原因
     */
    void debug(Object message, Throwable t);

    void error(Object message);
    void error(Object message, Throwable t);
    void fatal(Object message);
    void fatal(Object message, Throwable t);
    void info(Object message);
    void info(Object message, Throwable t);
    void trace(Object message);
    void trace(Object message, Throwable t);
    void warn(Object message);
    void warn(Object message, Throwable t);

    /**
     * 判断当前是否启用调试日志记录
     * 调用此方法以防止在日志级别超过调试时执行昂贵的操作(例如:字符串连接)
     * 比如日志记录级别为 info,则 debug 级别就是没有被启用的,所以 isDebugEnabled() 方法返回 false
     * @return true 如果底层日志记录器中启用了调试功能
     */
    boolean isDebugEnabled();

    boolean isErrorEnabled();
    boolean isFatalEnabled();
    boolean isInfoEnabled();
    boolean isTraceEnabled();
    boolean isWarnEnabled();
}

LogSource.java
已经被标识为@deprecated,所以不推荐使用。
该类提供了获得Log实例对象的方法,LogFactory替代了这个类的作用。
该类的处理流程:

  • 首先判断 org.apache.log4j.Logger 是否在classpath中,也就是说默认采用log4j作为实现。
  • 其次判断
    java.util.logging.Loggerorg.apache.commons.logging.impl.Jdk14Logger是否存在,将使用jdk1.4提供的log作为底层实现。
  • 否则将使用org.apache.commons.logging.impl.NoOpLog作为默认实现,将不提供Log输出。

在LogSource的实现中也会判断是否设置了"org.apache.commons.logging.log或者org.apache.commons.logging.Log属性,其优先级比上面的处理流程更高。

java -Dorg.apache.commons.logging.log=***
java -Dorg.apache.commons.logging.Log=***
/**
 * 用于创建 Log 实例的工厂
 * 应用程序应调用 makeNewLogInstance() 方法来实例化配置的 Log 实现类的新实例
 * 默认情况下调用 getInstance() 将使用以下算法:
 *    如果Log4J可用则返回 org.apache.commons.logging.impl.Log4JLogger 的实例
 *    如果JDK 1.4或更高版本可用则返回 org.apache.commons.logging.impl.Jdk14Logger 的实例
 *    否则返回一个 org.apache.commons.logging.impl.NoOpLog 的实例
 * 可以通过以下两种方法之一更改默认行为:
 *    在启动命令行中,将系统属性 org.apache.commons.logging.log 设置为要使用的
 *    org.apache.commons.logging.Log 实现类的名称
 *    在运行时调用 LogSource.setLogImplementation() 方法
 *
 */
public class LogSource {

    // ------------------------------------------------------- Class Attributes

    /** Log 实例的缓存池 */
    static protected Hashtable logs = new Hashtable();

    /** Is log4j available (in the current classpath) */
    static protected boolean log4jIsAvailable = false;

    /** Is JDK 1.4 logging available */
    static protected boolean jdk14IsAvailable = false;

    /** Constructor for current log class */
    static protected Constructor logImplctor = null;

    // ----------------------------------------------------- Class Initializers

    static {

        // Is Log4J Available?
        try {
            log4jIsAvailable = null != Class.forName("org.apache.log4j.Logger");
        } catch (Throwable t) {
            log4jIsAvailable = false;
        }

        // Is JDK 1.4 Logging Available?
        try {
            jdk14IsAvailable = null != Class.forName("java.util.logging.Logger") &&
                               null != Class.forName("org.apache.commons.logging.impl.Jdk14Logger");
        } catch (Throwable t) {
            jdk14IsAvailable = false;
        }

        // Set the default Log implementation
        String name = null;
        try {
            name = System.getProperty("org.apache.commons.logging.log");
            if (name == null) {
                name = System.getProperty("org.apache.commons.logging.Log");
            }
        } catch (Throwable t) {
        }
        if (name != null) {
            try {
                setLogImplementation(name);
            } catch (Throwable t) {
                try {
                    setLogImplementation("org.apache.commons.logging.impl.NoOpLog");
                } catch (Throwable u) {
                    // ignored
                }
            }
        } else {
            try {
                if (log4jIsAvailable) {
                    setLogImplementation("org.apache.commons.logging.impl.Log4JLogger");
                } else if (jdk14IsAvailable) {
                    setLogImplementation("org.apache.commons.logging.impl.Jdk14Logger");
                } else {
                    setLogImplementation("org.apache.commons.logging.impl.NoOpLog");
                }
            } catch (Throwable t) {
                try {
                    setLogImplementation("org.apache.commons.logging.impl.NoOpLog");
                } catch (Throwable u) {
                    // ignored
                }
            }
        }

    }

    // ------------------------------------------------------------ Constructor

    /** 作为一个工厂类不允许创建该类的实例 */
    private LogSource() {
    }

    // ---------------------------------------------------------- Class Methods

    /**
     * 按类的名称设置日志实现/日志实现工厂
     * 给定的类必须实现 Log接口,并提供一个构造函数,它接受一个String类型的参数(Log 的名称)
     */
    static public void setLogImplementation(String classname)
        throws LinkageError, NoSuchMethodException, SecurityException, ClassNotFoundException {
        try {
            Class logclass = Class.forName(classname);
            Class[] argtypes = new Class[1];
            argtypes[0] = "".getClass();
            logImplctor = logclass.getConstructor(argtypes);
        } catch (Throwable t) {
            logImplctor = null;
        }
    }

    /**
     * 按类设置日志实现/日志实现工厂
     * 给定的类必须实现 Log接口,并提供一个构造函数,它接受一个String类型的参数(Log 的名称)
     */
    static public void setLogImplementation(Class logclass)
        throws LinkageError, ExceptionInInitializerError, NoSuchMethodException, SecurityException {
        Class[] argtypes = new Class[1];
        argtypes[0] = "".getClass();
        logImplctor = logclass.getConstructor(argtypes);
    }

    /** 通过类名获取 Log 实例 */
    static public Log getInstance(String name) {
        Log log = (Log) logs.get(name);
        if (null == log) {
            log = makeNewLogInstance(name);
            logs.put(name, log);
        }
        return log;
    }

    /** 通过 Class 获取 Log 实例 */
    static public Log getInstance(Class clazz) {
        return getInstance(clazz.getName());
    }

    /**
     * 根据给定的名称创建一个新的 Log 实现
     * 返回的特定 Log 实现:由"org.apache.commons.logging.log"属性的值决定
     * 可以将"org.apache.commons.logging.log"的值设置为实现 Log 接口的类的完全限定名
     * 此类必须有一个公有的构造函数,它接受一个 String 类型的参数(包含要构造的日志的名称)
     * 
     * 当没有设置"org.apache.commons.logging.log"或者"org.apache.commons.logging.Log"属性时:
     * 如果log4j Logger 类在 LogSource 的类路径中可用,则该方法将返回一个 Log4JLogger
     * 如果我们在JDK 1.4或更高版本的系统上,则该方法将返回一个 Jdk14Logger
     * 如果上述条件均不为真则返回 NoOpLog
     *
     */
    static public Log makeNewLogInstance(String name) {
        Log log;
        try {
            Object[] args = { name };
            log = (Log) logImplctor.newInstance(args);
        } catch (Throwable t) {
            log = null;
        }
        if (null == log) {
            log = new NoOpLog(name);
        }
        return log;
    }

    /**
     * 返回一个String数组,其中包含所有 Log 的名称
     */
    static public String[] getLogNames() {
        return (String[]) logs.keySet().toArray(new String[logs.size()]);
    }
}

LogConfigurationException.java

/**
 * 仅当适当的 LogFactory 或 Log 实例不能由相应的工厂方法创建时抛出异常
 */
public class LogConfigurationException extends RuntimeException {

    /** Serializable version identifier. */
    private static final long serialVersionUID = 8486587136871052495L;

    /**
     * Construct a new exception with <code>null</code> as its detail message.
     */
    public LogConfigurationException() {
        super();
    }

    /**
     * Construct a new exception with the specified detail message.
     *
     * @param message The detail message
     */
    public LogConfigurationException(String message) {
        super(message);
    }

    /**
     * Construct a new exception with the specified cause and a derived
     * detail message.
     *
     * @param cause The underlying cause
     */
    public LogConfigurationException(Throwable cause) {
        this(cause == null ? null : cause.toString(), cause);
    }

    /**
     * Construct a new exception with the specified detail message and cause.
     *
     * @param message The detail message
     * @param cause The underlying cause
     */
    public LogConfigurationException(String message, Throwable cause) {
        super(message + " (Caused by " + cause + ")");
        this.cause = cause; // Two-argument version requires JDK 1.4 or later
    }

    /**
     * The underlying cause of this exception.
     */
    protected Throwable cause = null;

    /**
     * Return the underlying cause of this exception (if any).
     */
    public Throwable getCause() {
        return this.cause;
    }
}

LogFactory.java
抽象类工厂类,用于产生Log的实例对象。
最常用的方法是

public static Log getLog(Class clazz) throws LogConfigurationException {
        return getFactory().getInstance(clazz);
    }
    public static Log getLog(String name) throws LogConfigurationException {
        return getFactory().getInstance(name);
    }

getInstance是一个抽象方法,我们留在以后讲。
解析下getFactory方法:

  1. 获得当前线程的classloader,命名为contextClassLoader
  2. 根据contextClassLoader在缓存中获得logFactory,这里的缓存使用的是org.apache.commons.logging.impl.WeakHashTable,key是classloader,value是logFactory,如果在缓存中有logFactory则直接返回,没有进入下面的流程
  3. 读取配置文件commons-logging.properties
  4. 如果读到了配置文件,判断其中use_tccl属性,然后设定baseClassLoader是使用contextClassLoader还是使用加载本Class文件的那个classloader(名字为thisClassLoader)
  5. 下面生成logFactory,这里会有使用四种方式,依次来尝试生成。
  6. 将生成的logFactory缓存起来
  7. 返回logFactory

第5条对应的四种方式是:

  • 通过系统属性中寻找org.apache.commons.logging.LogFactory的value值,根据值为类名生成logFactory
  • 通过资源META-INF/services/org.apache.commons.logging.LogFactory,获得的值为类名生成logFactory
  • 通过刚才读取的配置文件commons-logging.properties,从中获取以org.apache.commons.logging.LogFactory为key的value值,根据值为类名生成logFactory
  • 如果上面都不成功的话,会使用默认的实现类org.apache.commons.logging.impl.LogFactoryImpl来生成logFactory

参考:
https://my.oschina.net/xianggao/blog/520323
http://pangwu86.iteye.com/blog/1122082

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

N3verL4nd

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值