Java日志体系(commons-logging)

Java日志系统学习

作为一名Java开发者,日志是我们工作中经常接触到的一项技术。对于Web应用而言,日志的重要性不言而喻,是必不可少的一部分;日志提供了丰富的记录功能,例如程序运行时的错误信息,描述信息,状态信息和执行时间信息等。

在实际生产环境中,日志是查找问题的重要来源,良好的日志格式和记录可以帮助Developer快速定位到错误的根源,找到问题的原因;

尽管对于现在的应用程序来说,日志至关重要,但是在JDK最初的版本当中并不包含日志记录的API和实现,直到JDK1.4后才被加入;因此,开源社区在此期间提供了众多贡献,其中名声最大、运用最广泛的当log4j莫属,当然后续的logback、log4j2也在迅速的普及;下面,就让笔者来进行具体的介绍。

1 commons-logging

1.1 简介

Apache Commons Logging,又名JakartaCommons Logging (JCL),它是Apache提供的一个通用的日志接口,它的出现避免了和具体的日志方案直接耦合;在日常开发中,developer可以选择第三方日志组件进行搭配使用,例如log4j、logback等;

说的直白些,commons-logging提供了操作日志的接口,而具体实现交给log4j、logback这样的开源日志框架来完成;这样的方式,实现了程序的解耦,对于底层日志框架的改变,并不会影响到上层的业务代码。

1.2 commons-logging结构

Java日志体系(commons-logging)_第1张图片

Log:日志对象接口,封装了操作日志的方法,定义了日志操作的5个级别:trace < debug < info < warn < error

LogFactory:抽象类,日志工厂,获取日志类;

LogFactoryImpl:LogFactory的实现类,真正获取日志对象的地方;

Log4JLogger:对log4j的日志对象封装;

Jdk14Logger:对JDK1.4的日志对象封装;

Jdk13LumberjackLogger:对JDK1.3以及以前版本的日志对象封装;

SimpleLog:commons-logging自带日志对象;

1.3 使用

commons-logging的使用非常简单。首先,需要在pom.xml文件中添加依赖:


    commons-logging  
    commons-logging  
    1.1.3  
  

声明测试代码:

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");
    }
}

接下来,在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

在我们的项目中,如果只单纯的依赖了commons-logging,那么默认使用的日志对象就是Jdk14Logger,默认使用的日志工厂就是LogFactoryImpl

1.4 源码分析

public abstract class LogFactory {
    public static final String HASHTABLE_IMPLEMENTATION_PROPERTY="org.apache.commons.logging.LogFactory.HashtableImpl";
    
    private static final String WEAK_HASHTABLE_CLASSNAME = "org.apache.commons.logging.impl.WeakHashtable";

    public static final String FACTORY_PROPERTIES = "commons-logging.properties";

    public static final String FACTORY_PROPERTY = "org.apache.commons.logging.LogFactory";

    public static final String FACTORY_DEFAULT = "org.apache.commons.logging.impl.LogFactoryImpl";

    //LogFactory静态代码块:  
    static {
        //获取LogFactory类加载器:AppClassLoader
        thisClassLoader = getClassLoader(LogFactory.class);
        String classLoaderName;
        try {
            ClassLoader classLoader = thisClassLoader;
            if (thisClassLoader == null) {
                classLoaderName = "BOOTLOADER";
            } else {
                //获取classLoader的名称:sun.misc.Launcher$AppClassLoader@150838093
                classLoaderName = objectId(classLoader);
            }
        } catch (SecurityException e) {
            classLoaderName = "UNKNOWN";
        }
        diagnosticPrefix = "[LogFactory from " + classLoaderName + "] ";
        diagnosticsStream = initDiagnostics();
        logClassLoaderEnvironment(LogFactory.class);
        //创建存放日志的工厂缓存对象:实际为org.apache.commons.logging.impl.WeakHashtable
        factories = createFactoryStore();
        if (isDiagnosticsEnabled()) {
            logDiagnostic("BOOTSTRAP COMPLETED");
        }
    }

    //获取日志对象:
    public static Log getLog(Class clazz) throws LogConfigurationException {
        //得到LogFactoryImpl日志工厂后,实例化具体的日志对象:
        return getFactory().getInstance(clazz);
    }
    //获取日志工厂
    public static LogFactory getFactory() throws LogConfigurationException {
        //获取当前线程的classCloader:
        ClassLoader contextClassLoader = getContextClassLoaderInternal();
        if (contextClassLoader == null) {
            .....
        }
        //从缓存中获取LogFactory:此缓存就是刚才在静态代码块中创建的WeakHashtable
        LogFactory factory = getCachedFactory(contextClassLoader);
        //如果存在就返回:
        if (factory != null) {
            return factory;
        }
        if (isDiagnosticsEnabled()) {
            ......
        }
        //读取classpath下的commons-logging.properties文件:
        Properties props = getConfigurationFile(contextClassLoader, FACTORY_PROPERTIES);
        ClassLoader baseClassLoader = contextClassLoader;
        if (props != null) {
            //如果Properties对象不为空,从中获取 TCCL_KEY 的值:
            String useTCCLStr = props.getProperty(TCCL_KEY);
            if (useTCCLStr != null) {
                if (Boolean.valueOf(useTCCLStr).booleanValue() == false) {
                    baseClassLoader = thisClassLoader;
                }
            }
        }
        .....
        try {
            /从系统属性中获取 FACTORY_PROPERTY 的值:
            String factoryClass = getSystemProperty(FACTORY_PROPERTY, null);
            if (factoryClass != null) {
                //如果该值不为空,则实例化日志工厂对象:
                factory = newFactory(factoryClass, baseClassLoader, contextClassLoader);
            } else {
                .....
            }
        } catch (SecurityException e) {
           .....
        }
        if (factory == null) {
            if (isDiagnosticsEnabled()) {
                ....
            }
            try {
                //如果日志工厂对象还为null,则从 META-INF/services/org.apache.commons.logging.LogFactory 中获取:
                final InputStream is = getResourceAsStream(contextClassLoader, SERVICE_ID);
                if( is != null ) {
                    .....
                }
            } catch (Exception ex) {
                ......
            }
        }
        if (factory == null) {
            if (props != null) {
                //如果此时日志工厂为null,并props有值,则获取 FACTORY_PROPERTY 为key的值:
                String factoryClass = props.getProperty(FACTORY_PROPERTY);
                if (factoryClass != null) {
                    if (isDiagnosticsEnabled()) {
                        logDiagnostic(
                            "[LOOKUP] Properties file specifies LogFactory subclass '" + factoryClass + "'");
                    }
                    //如果我们配置了,则实例化我们配置的日志工厂对象
                    factory = newFactory(factoryClass, baseClassLoader, contextClassLoader);
                } else {
                    .....
                }
            } else {
                ......
            }
        }
        if (factory == null) {
            if (isDiagnosticsEnabled()) {
               .....
            }
            //如果此时日志工厂依旧为null,则实例化默认工厂:org.apache.commons.logging.impl.LogFactoryImpl
            factory = newFactory(FACTORY_DEFAULT, thisClassLoader, contextClassLoader);
        }
        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;
    }

    //创建存放日志的工厂缓存对象:
    private static final Hashtable createFactoryStore() {
        Hashtable result = null;
        String storeImplementationClass;
        try {
            //从系统属性中获取 HASHTABLE_IMPLEMENTATION_PROPERTY 为key的值:
            storeImplementationClass = getSystemProperty(HASHTABLE_IMPLEMENTATION_PROPERTY, null);
        } catch (SecurityException ex) {
            storeImplementationClass = null;
        }
        //如果 storeImplementationClass 为null
        if (storeImplementationClass == null) {
            //将 WEAK_HASHTABLE_CLASSNAME 赋值给storeImplementationClass字符串
            storeImplementationClass = WEAK_HASHTABLE_CLASSNAME;
        }
        try {
            //反射实例化缓存对象:org.apache.commons.logging.impl.WeakHashtable
            Class implementationClass = Class.forName(storeImplementationClass);
            result = (Hashtable) implementationClass.newInstance();
        } catch (Throwable t) {
            .....
        }
        if (result == null) {
            result = new Hashtable();
        }
        return result;
    }
}

public class LogFactoryImpl extends LogFactory {

    protected Hashtable instances = new Hashtable();

    protected Constructor logConstructor = null;
    
    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 Log newInstance(String name) throws LogConfigurationException {
        Log instance;
        try {
            //日志构造器对象为null:
            if (logConstructor == null) {
                instance = discoverLogImplementation(name);
            }else {
                Object params[] = { name };
                instance = (Log) logConstructor.newInstance(params);
            }

            if (logMethod != null) {
                Object params[] = { this };
                logMethod.invoke(instance, params);
            }
            return instance;
        } catch (LogConfigurationException lce) {
           ....
        }
    }
    //发现日志实现类:
    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) {
               .......
            }
            return result;
        }
        if (isDiagnosticsEnabled()) {
            ....
        }
        /*
          *如果配置文件中不存在“org.apache.commons.logging.Log”的value:
          *那么还有遍历classesToDiscover字符串数组,进行实例化:
          *此数组中包括:log4j、Jdk14Logger、Jdk13LumberjackLogger、SimpleLog
        */
        for(int i=0; i

以上就是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;

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

你可能感兴趣的:(Java日志体系(commons-logging))