前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java日志体系(commons-logging)Java日志系统学习

Java日志体系(commons-logging)Java日志系统学习

作者头像
贾博岩
发布2018-05-11 16:04:13
1.5K0
发布2018-05-11 16:04:13
举报
文章被收录于专栏:博岩Java大讲堂博岩Java大讲堂

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结构

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文件中添加依赖:

代码语言:javascript
复制
<dependency>
    <groupId>commons-logging</groupId>  
    <artifactId>commons-logging</artifactId>  
    <version>1.1.3</version>  
</dependency>  

声明测试代码:

代码语言:javascript
复制
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:

代码语言:javascript
复制
#指定日志对象:
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 源码分析

代码语言:javascript
复制
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<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获取日志对象的全过程,具体文字总结如下:

代码语言:javascript
复制
获取当前线程的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,则不能获取具体的日志工厂对象,则导致启动失败,这样就是我们常说的--动态查找机制。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017.05.09 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Java日志系统学习
    • 1 commons-logging
      • 1.1 简介
      • 1.2 commons-logging结构
      • 1.3 使用
      • 1.4 源码分析
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档