美文网首页
Java门面日志commons-logging

Java门面日志commons-logging

作者: 贪睡的企鹅 | 来源:发表于2019-08-19 17:43 被阅读0次

    1 简介

    Apache Commons Logging,又名JakartaCommons Logging (JCL),它是Apache提供的一个通用的日志接口,也被称为“门面日志”。“门面日志”利用了设计模式中的门面模式思想,对外提供一套通用的日志记录的 API,而不提供具体的日志输出服务,如果要实现日志输出,需要集成其他的日志框架,比如 Log4j、Logback、Log4j2 等。

    这种门面模式的好处在于,记录日志的 API 和日志输出的服务分离开,代码里面只需要关注记录日志的 API,通过 SLF4J 指定的接口记录日志;而日志输出通过引入 JAR 包的方式即可指定其他的日志框架。当我们需要改变系统的日志输出服务时,不用修改代码,只需要改变引入日志输出框架 JAR 包。

    JUL最后更新于2014年7月,现以停止更新。

    image

    2 包结构

    image
    • Log:日志对象接口,封装了操作日志的方法,定义了日志操作的5个级别:trace < debug < info < warn < error
    • LogFactory:抽象类,日志工厂,获取日志类;
    • LogFactoryImpl:LogFactory的实现类,真正获取日志对象的地方;
    • Log4JLogger:对log4j的日志对象封装;
    • Jdk14Logger:对JDK1.4的日志对象封装;
    • Jdk13LumberjackLogger:对JDK1.3以及以前版本的日志对象封装;
    • SimpleLog:commons-logging自带日志对象;

    3 日志加载

    jcl可以通过在ClassPath下创建commons-logging.properties配置文件指定加载日志实现框架。

    #指定日志对象:
    org.apache.commons.logging.Log = org.apache.commons.logging.impl.Jdk14Logger
    #指定日志工厂:
    org.apache.commons.logging.LogFactory = org.apache.commons.logging.impl.LogFactoryImpl
    

    jcl如果没有指定日志实现框架则默认加载时按照顺序log4j>jul> simpleLog依次加载,如果查找到log4j包则使用log4j,如果没有依次类推。

    4 JCL使用

    引用JCL依赖包

        <dependency>
          <groupId>commons-logging</groupId>
          <artifactId>commons-logging</artifactId>
          <version>1.2</version>
        </dependency>
    

    测试代码

    此时由于项目也没有放入其他日志框架依赖包,会按照顺序log4j>jul> simpleLog加载,因此此时会使用jdk自带jul实现日志打印。

    public class commons_loggingDemo {
        Log log= LogFactory.getLog(commons_loggingDemo.class);
        @Test
        public void test() throws IOException {
            log.debug("Debug info.");
            log.info("Info info");
            log.warn("Warn info");
            log.error("Error info");
            log.fatal("Fatal info");
        }
    }
    

    引用log4j依赖包

    在不修改代码的前提下,引入log4j依赖包。在次调用测试代码,此时会使用log4j实现日志打印。

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

    5 JCL原理分析

    5.1 LogFactory 类初始化
       protected static Hashtable factories = null;
       
       private static PrintStream diagnosticsStream = null;
    
       static {
            /** 获取LogFactory类加载器:AppClassLoader设置到thisClassLoader **/
            thisClassLoader = getClassLoader(LogFactory.class);
            String classLoaderName;
            try {
                ClassLoader classLoader = thisClassLoader;
                if (thisClassLoader == null) {
                    classLoaderName = "BOOTLOADER";
                } else {
                    classLoaderName = objectId(classLoader);
                }
            } catch (SecurityException e) {
                classLoaderName = "UNKNOWN";
            }
            diagnosticPrefix = "[LogFactory from " + classLoaderName + "] ";
    
            /** 初始化diagnosticsStream,本质实现为PrintStream负责打印输出**/
            diagnosticsStream = initDiagnostics();
    
            /** 获取LogFactory.class的类加载器,并通过diagnosticsStream打印 **/
            logClassLoaderEnvironment(LogFactory.class);
    
            /** 实例化存放日志的工厂缓存对象:默认实现为org.apache.commons.logging.impl.WeakHashtable **/
            factories = createFactoryStore();
            if (isDiagnosticsEnabled()) {
                logDiagnostic("BOOTSTRAP COMPLETED");
            }
        }
    

    实例化存放日志的工厂缓存对象

    private static final Hashtable createFactoryStore() {
            Hashtable result = null;
            String storeImplementationClass;
            /** 从系统属性中获取工厂缓存的实现类**/
            try {
                storeImplementationClass = getSystemProperty(HASHTABLE_IMPLEMENTATION_PROPERTY, null);
            } catch (SecurityException ex) {
                storeImplementationClass = null;
            }
    
            /** 从WEAK_HASHTABLE_CLASSNAME获取工厂缓存的默认实现类**/
            if (storeImplementationClass == null) {
                storeImplementationClass = WEAK_HASHTABLE_CLASSNAME;
            }
    
            /** 实例化工厂缓存实现类对象 **/
            try {
                Class implementationClass = Class.forName(storeImplementationClass);
                result = (Hashtable) implementationClass.newInstance();
            } catch (Throwable t) {
                handleThrowable(t); // may re-throw t
                if (!WEAK_HASHTABLE_CLASSNAME.equals(storeImplementationClass)) {
                    if (isDiagnosticsEnabled()) {
                        logDiagnostic("[ERROR] LogFactory: Load of custom hashtable failed");
                    } else {
                        System.err.println("[ERROR] LogFactory: Load of custom hashtable failed");
                    }
                }
            }
            if (result == null) {
                result = new Hashtable();
            }
            /** 返回 结果**/
            return result;
        }
    
    5.2 从LogFactory获取Log对象
        /**
         * 获取日志对象
         */
        public static Log getLog(Class clazz) throws LogConfigurationException {
            /** 得到LogFactoryImpl日志工厂后,实例化具体的日志对象:**/
            return getFactory().getInstance(clazz);
        }
    
        /**
         * 获取日志对象
         */
        public static Log getLog(String name) throws LogConfigurationException {
            /** 得到LogFactoryImpl日志工厂后,实例化具体的日志对象:**/
            return getFactory().getInstance(name);
        }
    
    
    5.3 获取并实例化LogFactory
     public static LogFactory getFactory() throws LogConfigurationException {
            /** 获取上下文类加载器 **/
            ClassLoader contextClassLoader = getContextClassLoaderInternal();
    
            if (contextClassLoader == null) {
                if (isDiagnosticsEnabled()) {
                    logDiagnostic("Context classloader is null.");
                }
            }
    
            /** 从缓存factories中获取LogFactory **/
            LogFactory factory = getCachedFactory(contextClassLoader);
            if (factory != null) {
                return factory;
            }
    
            if (isDiagnosticsEnabled()) {
                logDiagnostic(
                        "[LOOKUP] LogFactory implementation requested for the first time for context classloader " +
                        objectId(contextClassLoader));
                logHierarchy("[LOOKUP] ", contextClassLoader);
            }
    
            /** 读取classpath下的commons-logging.properties文件:**/
            Properties props = getConfigurationFile(contextClassLoader, FACTORY_PROPERTIES);
    
            /**
             * 读取commons-logging.properties配置文件中use_tccl属性值
             * 如果属性值存在且为flase,设置baseClassLoader指向thisClassLoader,
             * 如果不存在use_tccl属性值或属性值为true,baseClassLoader指向上下文类加载器
             * **/
            ClassLoader baseClassLoader = contextClassLoader;
            if (props != null) {
                String useTCCLStr = props.getProperty(TCCL_KEY);
                if (useTCCLStr != null) {
                    if (Boolean.valueOf(useTCCLStr).booleanValue() == false) {
                        baseClassLoader = thisClassLoader;
                    }
                }
            }
    
            if (isDiagnosticsEnabled()) {
                logDiagnostic("[LOOKUP] Looking for system property [" + FACTORY_PROPERTY +
                              "] to define the LogFactory subclass to use...");
            }
    
            /**
             * 读取commons-logging.properties配置文件中org.apache.commons.logging.LogFactory属性值,作为LogFactory实现类,同时实例化LogFactory对象
             **/
            try {
                String factoryClass = getSystemProperty(FACTORY_PROPERTY, null);
                if (factoryClass != null) {
                    if (isDiagnosticsEnabled()) {
                        logDiagnostic("[LOOKUP] Creating an instance of LogFactory class '" + factoryClass +
                                      "' as specified by system property " + FACTORY_PROPERTY);
                    }
                    factory = newFactory(factoryClass, baseClassLoader, contextClassLoader);
                } else {
                    if (isDiagnosticsEnabled()) {
                        logDiagnostic("[LOOKUP] No system property [" + FACTORY_PROPERTY + "] defined.");
                    }
                }
            } catch (SecurityException e) {
                if (isDiagnosticsEnabled()) {
                    logDiagnostic("[LOOKUP] A security exception occurred while trying to create an" +
                                  " instance of the custom factory class" + ": [" + trim(e.getMessage()) +
                                  "]. Trying alternative implementations...");
                }
            } catch (RuntimeException e) {
                if (isDiagnosticsEnabled()) {
                    logDiagnostic("[LOOKUP] An exception occurred while trying to create an" +
                                  " instance of the custom factory class" + ": [" +
                                  trim(e.getMessage()) +
                                  "] as specified by a system property.");
                }
                throw e;
            }
    
            /**
             * 读取META-INF/services/org.apache.commons.logging.LogFactory配置文件中数据,作为LogFactory实现类,同时实例化LogFactory对象
             **/
            if (factory == null) {
                if (isDiagnosticsEnabled()) {
                    logDiagnostic("[LOOKUP] Looking for a resource file of name [" + SERVICE_ID +
                                  "] to define the LogFactory subclass to use...");
                }
                try {
                    final InputStream is = getResourceAsStream(contextClassLoader, SERVICE_ID);
    
                    if( is != null ) {
                        BufferedReader rd;
                        try {
                            rd = new BufferedReader(new InputStreamReader(is, "UTF-8"));
                        } catch (java.io.UnsupportedEncodingException e) {
                            rd = new BufferedReader(new InputStreamReader(is));
                        }
    
                        String factoryClassName = rd.readLine();
                        rd.close();
    
                        if (factoryClassName != null && ! "".equals(factoryClassName)) {
                            if (isDiagnosticsEnabled()) {
                                logDiagnostic("[LOOKUP]  Creating an instance of LogFactory class " +
                                              factoryClassName +
                                              " as specified by file '" + SERVICE_ID +
                                              "' which was present in the path of the context classloader.");
                            }
                            factory = newFactory(factoryClassName, baseClassLoader, contextClassLoader );
                        }
                    } else {
                        if (isDiagnosticsEnabled()) {
                            logDiagnostic("[LOOKUP] No resource file with name '" + SERVICE_ID + "' found.");
                        }
                    }
                } catch (Exception ex) {
    
                    if (isDiagnosticsEnabled()) {
                        logDiagnostic(
                            "[LOOKUP] A security exception occurred while trying to create an" +
                            " instance of the custom factory class" +
                            ": [" + trim(ex.getMessage()) +
                            "]. Trying alternative implementations...");
                    }
                }
            }
    
            /** 在次读取commons-logging.properties配置文件中org.apache.commons.logging.LogFactory属性值,作为LogFactory实现类,同时实例化LogFactory对象 **/
            if (factory == null) {
                if (props != null) {
                    if (isDiagnosticsEnabled()) {
                        logDiagnostic(
                            "[LOOKUP] Looking in properties file for entry with key '" + FACTORY_PROPERTY +
                            "' to define the LogFactory subclass to use...");
                    }
                    String factoryClass = props.getProperty(FACTORY_PROPERTY);
                    if (factoryClass != null) {
                        if (isDiagnosticsEnabled()) {
                            logDiagnostic(
                                "[LOOKUP] Properties file specifies LogFactory subclass '" + factoryClass + "'");
                        }
                        factory = newFactory(factoryClass, baseClassLoader, contextClassLoader);
    
                        // TODO: think about whether we need to handle exceptions from newFactory
                    } else {
                        if (isDiagnosticsEnabled()) {
                            logDiagnostic("[LOOKUP] Properties file has no entry specifying LogFactory subclass.");
                        }
                    }
                } else {
                    if (isDiagnosticsEnabled()) {
                        logDiagnostic("[LOOKUP] No properties file available to determine" + " LogFactory subclass from..");
                    }
                }
            }
    
            /**  如果上诉操作没有获取factory实现对象,则使用org.apache.commons.logging.impl.LogFactoryImpl作为默认日志工厂实现类,并实例化工厂对象 **/
            if (factory == null) {
                if (isDiagnosticsEnabled()) {
                    logDiagnostic(
                        "[LOOKUP] Loading the default LogFactory implementation '" + FACTORY_DEFAULT +
                        "' via the same classloader that loaded this LogFactory" +
                        " class (ie not looking in the context classloader).");
                }
                factory = newFactory(FACTORY_DEFAULT, thisClassLoader, contextClassLoader);
            }
    
            /** 将LogFactory添加到缓存factories当中: **/
            if (factory != null) {
                cacheFactory(contextClassLoader, factory);
    
                if (props != null) {
                    Enumeration names = props.propertyNames();
                    while (names.hasMoreElements()) {
                        String name = (String) names.nextElement();
                        String value = props.getProperty(name);
                        factory.setAttribute(name, value);
                    }
                }
            }
            /** 返回 **/
            return factory;
        }
    
    5.4 从日志工厂获取日志对象:
        public Log getInstance(Class clazz) throws LogConfigurationException {
                return getInstance(clazz.getName());
        }
        
        
        public Log getInstance(String name) throws LogConfigurationException {
                Log instance = (Log) instances.get(name);
                if (instance == null) {
                    instance = newInstance(name);
                    instances.put(name, instance);
                }
                return instance;
        }    
    
        /**
         * 构造日志对象构造器函数
         */
        protected Constructor logConstructor = null;
    
        /**
         * The signature of the Constructor to be used.
         */
        protected Class logConstructorSignature[] = { String.class };
    
        /**
         * 实例化日志对象反相依赖日志工厂的方法<code>setLogFactory</code>
         */
        protected Method logMethod = null;
        
        protected Log newInstance(String name) throws LogConfigurationException {
            Log instance;
            try {
                /** 如果构造器不存在,调用discoverLogImplementation尝试通过类名称实例化日志对象 **/
                if (logConstructor == null) {
                    instance = discoverLogImplementation(name);
                }
                /** 如果构造器存在,使用构造器实例化日志对象 **/
                else {
                    Object params[] = { name };
                    instance = (Log) logConstructor.newInstance(params);
                }
                /** 如果实例化日志对象,存在setLogFactory方法,则调用和构日志工厂方法反相关联 **/
                if (logMethod != null) {
                    Object params[] = { this };
                    logMethod.invoke(instance, params);
                }
    
                return instance;
    
            } catch (LogConfigurationException lce) {
                throw lce;
            } catch (InvocationTargetException e) {
                Throwable c = e.getTargetException();
                throw new LogConfigurationException(c == null ? e : c);
            } catch (Throwable t) {
                handleThrowable(t); 
                throw new LogConfigurationException(t);
            }
        }
        
        private Log discoverLogImplementation(String logCategory)
            throws LogConfigurationException {
            if (isDiagnosticsEnabled()) {
                logDiagnostic("Discovering a Log implementation...");
            }
    
            initConfiguration();
    
            Log result = null;
    
            /** 从classpath中的commons-logging.properties文件中获取“org.apache.commons.logging.Log”的value:**/
            String specifiedLogClassName = findUserSpecifiedLogClassName();
    
            /** 如果配置文件中存在日志实现类,那么就进行日志对象实例化: **/
            if (specifiedLogClassName != null) {
                if (isDiagnosticsEnabled()) {
                    logDiagnostic("Attempting to load user-specified log class '" +
                        specifiedLogClassName + "'...");
                }
    
                result = createLogFromClass(specifiedLogClassName,
                                            logCategory,
                                            true);
                if (result == null) {
                    StringBuffer messageBuffer =  new StringBuffer("User-specified log class '");
                    messageBuffer.append(specifiedLogClassName);
                    messageBuffer.append("' cannot be found or is not useable.");
    
                    // Mistyping or misspelling names is a common fault.
                    // Construct a good error message, if we can
                    informUponSimilarName(messageBuffer, specifiedLogClassName, LOGGING_IMPL_LOG4J_LOGGER);
                    informUponSimilarName(messageBuffer, specifiedLogClassName, LOGGING_IMPL_JDK14_LOGGER);
                    informUponSimilarName(messageBuffer, specifiedLogClassName, LOGGING_IMPL_LUMBERJACK_LOGGER);
                    informUponSimilarName(messageBuffer, specifiedLogClassName, LOGGING_IMPL_SIMPLE_LOGGER);
                    throw new LogConfigurationException(messageBuffer.toString());
                }
    
                return result;
            }
    
            if (isDiagnosticsEnabled()) {
                logDiagnostic(
                    "No user-specified Log implementation; performing discovery" +
                    " using the standard supported logging implementations...");
            }
            /**
             *如果配置文件中不存在“org.apache.commons.logging.Log”的value:
             *那么还有遍历classesToDiscover字符串数组,进行实例化:
             *此数组中包括:log4j、Jdk14Logger、Jdk13LumberjackLogger、SimpleLog
             **/
            for(int i=0; i<classesToDiscover.length && result == null; ++i) {
                result = createLogFromClass(classesToDiscover[i], logCategory, true);
            }
    
            /** 如果此时日志对象还未null,为报错; **/
            if (result == null) {
                throw new LogConfigurationException
                            ("No suitable Log implementation");
            }
    
            return result;
        }
    

    commons-logging获取日志对象的全过程

    • 获取当前线程的classLoader,根据classLoader从缓存中获取LogFactroy,使用的缓存是WeakHashTable对象;如果缓存中存在,则返回,没有则进入下面流程;

    • 读取classpath下的commons-logging.properties文件,判断其中是否设置了use_tccl属性,如果不为空则判断,该属性的值是否为false,若为false,则将baseClassLoader替换为当前类的classLoader;

    • 接着,继续获取LogFactory对象,此步骤分为4中方式:

      • (1)在系统属性中查找“org.apache.commons.logging.LogFactory”属性的值,根据值生成LogFactory对象;
      • (2)通过资源“META-INF/services/org.apache.commons.logging.LogFactory”文件,获取的值生成LogFactory对象;
      • (3)通过配置文件commons-logging.properties,获取以“org.apache.commons.logging.LogFactory”为key的值,根据值生成logFactory;
      • (4)如果以上均不成功,则创建系统默认的日志工厂:org.apache.commons.logging.impl.LogFactoryImpl
    • 成功获取日志工厂后,根据类名获取日志对象;

    • 主要逻辑在discoverLogImplementation方法中:

      • (1)检查commons-logging.properties文件中是否存在“org.apache.commons.logging.Log”属性,若存在则创建具体的日志对象;若不存在,进行下面逻辑;
      • (2)遍历classesToDiscover数组,该数组存有日志具体实现类的全限定类名:org.apache.commons.logging.impl.Log4JLogger、org.apache.commons.logging.impl.Jdk14Logger、org.apache.commons.logging.impl.Jdk13LumberjackLogger、org.apache.commons.logging.impl.SimpleLog;
      • (3)根据数组中存着的全限定类名,按照顺序依次加载Class文件,进行实例化操作,最后返回Log实例,默认为Jdk14Logger;

    6 缺陷

    JCL饱受诟病最多的是获取日志工厂的过程中使用了classLoader来寻找日志工厂实现,进而导致了其他组件,如若使用自己的classload,则不能获取具体的日志工厂对象,则导致启动失败,这样就是我们常说的--动态查找机制。

    相关文章

      网友评论

          本文标题:Java门面日志commons-logging

          本文链接:https://www.haomeiwen.com/subject/hxuesctx.html