一、需要解耦
     日志是实际应用中的一个重要部分,日志系统也有许多开源的实现,如java.util.logging, logback, log4j系列等。
     在使用日志系统时,如果与具体的日志实现耦合太深,如使用log4j作为日志的实现,在每一处需要打印日志的地方都会创建日志实例:
     logger = LogManager.getLogger("instanceName");
     当由于性能或者其他方面的需求需要更换日志实现时,如log4j升级到log4j2,就不得不替换每一处日志实例得创建,将会是一个噩梦般的工作。
     
     因此需要将应用与具体的日志实现解耦。commons-logging提供了日志实现解耦方案,当然commons-logging也提供了简单的日志实现。
     commons-logging的官网地址为:http://commons.apache.org/proper/commons-logging/
     其中对commons-logging的介绍:
     The Logging package is an ultra-thin bridge between different logging implementations. A library that uses the commons-logging API can be used with any logging implementation at runtime. Commons-logging comes with support for a number of popular logging implementations, and writing adapters for others is a reasonably simple task.
 

二、commons-logging简单日志实现:
     commons-logging除了提供解耦合方案外,也提供了org.apache.commons.logging.impl.SimpleLog作为简单的日志实现。
     使用SimpleLog,需要做相应配置:
     1. 配置commons-logging.properties
     在classpath根路径放置名为commons-logging.properties的文件(该路径不可改变),内容为:
     org.apache.commons.logging.Log=org.apache.commons.logging.impl.SimpleLog
 
     2. 创建Log实例
     名为配置好了之后就可以创建Log实例并直接使用:
     private static final Log logger = LogFactory.getLog(CommonsLogTest.class);
     输出为,默认日志级别为INFO
    
 
     3. 输出配置
     当然,SimpleLog也可以做一些简单的输出配置,打开SimpleLog的源码,可以看到在初始化时:
     static {
        // Add props from the resource simplelog.properties
        InputStream in = getResourceAsStream("simplelog.properties");
        if(null != in) {
            try {
                simpleLogProps.load(in);
                in.close();
            } catch(java.io.IOException e) {
                // ignored
            }
        }
 
        showLogName = getBooleanProperty(systemPrefix + "showlogname", showLogName);
        showShortName = getBooleanProperty(systemPrefix + "showShortLogname", showShortName);
        showDateTime = getBooleanProperty(systemPrefix + "showdatetime", showDateTime);
 
        if(showDateTime) {
            dateTimeFormat = getStringProperty(systemPrefix + "dateTimeFormat",
                                               dateTimeFormat);
            try {
                dateFormatter = new SimpleDateFormat(dateTimeFormat);
            } catch(IllegalArgumentException e) {
                // If the format pattern is invalid - use the default format
                dateTimeFormat = DEFAULT_DATE_TIME_FORMAT;
                dateFormatter = new SimpleDateFormat(dateTimeFormat);
            }
        }
    }
    可以看到最开始试图从simplelog.properties读取配置,若不存在后面指定了默认配置,因此只要在classpath根路径放置simplelog.properties文件即可控制日志输出行为。
    可以从http://commons.apache.org/proper/commons-logging/apidocs/org/apache/commons/logging/impl/SimpleLog.html的关于SimpleLog的类介绍中了解配置详情:
     Simple implementation of Log that sends all enabled log messages, for all defined loggers, to System.err. The following system properties are supported to configure the behavior of this logger:

  • org.apache.commons.logging.simplelog.defaultlog - Default logging detail level for all instances of SimpleLog. Must be one of ("trace", "debug", "info", "warn", "error", or "fatal"). If not specified, defaults to "info".
  • org.apache.commons.logging.simplelog.log.xxxxx - Logging detail level for a SimpleLog instance named "xxxxx". Must be one of ("trace", "debug", "info", "warn", "error", or "fatal"). If not specified, the default logging detail level is used.
  • org.apache.commons.logging.simplelog.showlogname - Set to true if you want the Log instance name to be included in output messages. Defaults to false.
  • org.apache.commons.logging.simplelog.showShortLogname - Set to true if you want the last component of the name to be included in output messages. Defaults to true.
  • org.apache.commons.logging.simplelog.showdatetime - Set to true if you want the current date and time to be included in output messages. Default is false.
  • org.apache.commons.logging.simplelog.dateTimeFormat - The date and time format to be used in the output messages. The pattern describing the date and time format is the same that is used in java.text.SimpleDateFormat. If the format is not specified or is invalid, the default format is used. The default format is yyyy/MM/dd HH:mm:ss:SSS zzz.
 
     一个规范的simpleLog配置如下:
     
 

三、commons-logging解耦原理
     commons-logging最核心有用的功能是解耦,它的SimpleLog实现性能比不上其他实现,如log4j等。
     首先,日志实例是通过LogFactory的getLog(String)方法创建的:
     
 
     LogFatory是一个抽象类,它负责加载具体的日志实现,分析其Factory getFactory()方法:
     public static org.apache.commons.logging.LogFactory getFactory() throws LogConfigurationException {
        // Identify the class loader we will be using
        ClassLoader contextClassLoader = getContextClassLoaderInternal();
 
        if (contextClassLoader == null) {
            // This is an odd enough situation to report about. This
            // output will be a nuisance on JDK1.1, as the system
            // classloader is null in that environment.
            if (isDiagnosticsEnabled()) {
                logDiagnostic("Context classloader is null.");
            }
        }
 
        // Return any previously registered factory for this class loader
        org.apache.commons.logging.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);
        }
 
        // Load properties file.
        //
        // If the properties file exists, then its contents are used as
        // "attributes" on the LogFactory implementation class. One particular
        // property may also control which LogFactory concrete subclass is
        // used, but only if other discovery mechanisms fail..
        //
        // As the properties file (if it exists) will be used one way or
        // another in the end we may as well look for it first.
        // classpath根目录下寻找commons-logging.properties
        Properties props = getConfigurationFile(contextClassLoader, FACTORY_PROPERTIES);
 
        // Determine whether we will be using the thread context class loader to
        // load logging classes or not by checking the loaded properties file (if any).
        // classpath根目录下commons-logging.properties是否配置use_tccl
        ClassLoader baseClassLoader = contextClassLoader;
        if (props != null) {
            String useTCCLStr = props.getProperty(TCCL_KEY);
            if (useTCCLStr != null) {
                // The Boolean.valueOf(useTCCLStr).booleanValue() formulation
                // is required for Java 1.2 compatibility.
                if (Boolean.valueOf(useTCCLStr).booleanValue() == false) {
                    // Don't use current context classloader when locating any
                    // LogFactory or Log classes, just use the class that loaded
                    // this abstract class. When this class is deployed in a shared
                    // classpath of a container, it means webapps cannot deploy their
                    // own logging implementations. It also means that it is up to the
                    // implementation whether to load library-specific config files
                    // from the TCCL or not.
                    baseClassLoader = thisClassLoader;
                }
            }
        }
 
        // 这里真正开始决定使用哪个factory
        // 首先,尝试查找vm系统属性org.apache.commons.logging.LogFactory,其是否指定factory
        // Determine which concrete LogFactory subclass to use.
        // First, try a global system property
        if (isDiagnosticsEnabled()) {
            logDiagnostic("[LOOKUP] Looking for system property [" + FACTORY_PROPERTY +
                    "] to define the LogFactory subclass to use...");
        }
 
        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...");
            }
            // ignore
        } catch (RuntimeException e) {
            // This is not consistent with the behaviour when a bad LogFactory class is
            // specified in a services file.
            //
            // One possible exception that can occur here is a ClassCastException when
            // the specified class wasn't castable to this LogFactory type.
            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;
        }
 
        // 第二,尝试使用java spi服务发现机制,载META-INF/services下寻找org.apache.commons.logging.LogFactory实现
        // Second, try to find a service by using the JDK1.3 class
        // discovery mechanism, which involves putting a file with the name
        // of an interface class in the META-INF/services directory, where the
        // contents of the file is a single line specifying a concrete class
        // that implements the desired interface.
 
        if (factory == null) {
            if (isDiagnosticsEnabled()) {
                logDiagnostic("[LOOKUP] Looking for a resource file of name [" + SERVICE_ID +
                        "] to define the LogFactory subclass to use...");
            }
            try {
                // META-INF/services/org.apache.commons.logging.LogFactory, SERVICE_ID
                final InputStream is = getResourceAsStream(contextClassLoader, SERVICE_ID);
 
                if (is != null) {
                    // This code is needed by EBCDIC and other strange systems.
                    // It's a fix for bugs reported in xerces
                    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 {
                    // is == null
                    if (isDiagnosticsEnabled()) {
                        logDiagnostic("[LOOKUP] No resource file with name '" + SERVICE_ID + "' found.");
                    }
                }
            } catch (Exception ex) {
                // note: if the specified LogFactory class wasn't compatible with LogFactory
                // for some reason, a ClassCastException will be caught here, and attempts will
                // continue to find a compatible class.
                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...");
                }
                // ignore
            }
        }
 
        // 第三,尝试从classpath根目录下的commons-logging.properties中查找org.apache.commons.logging.LogFactory属性指定的factory
        // Third try looking into the properties file read earlier (if found)
 
        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
        // Fourth, try the fallback implementation class
 
        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).");
            }
 
            // Note: unlike the above code which can try to load custom LogFactory
            // implementations via the TCCL, we don't try to load the default LogFactory
            // implementation via the context classloader because:
            // * that can cause problems (see comments in newFactory method)
            // * no-one should be customising the code of the default class
            // Yes, we do give up the ability for the child to ship a newer
            // version of the LogFactoryImpl class and have it used dynamically
            // by an old LogFactory class in the parent, but that isn't
            // necessarily a good idea anyway.
            factory = newFactory(FACTORY_DEFAULT, thisClassLoader, contextClassLoader);
        }
 
        if (factory != null) {
            /**
             * Always cache using context class loader.
             */
            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;
    }
 
     可以看出,抽象类LogFactory加载具体实现的步骤如下:
     1. 从vm系统属性org.apache.commons.logging.LogFactory
     2. 使用SPI服务发现机制,发现org.apache.commons.logging.LogFactory的实现
     3. 查找classpath根目录commons-logging.properties的org.apache.commons.logging.LogFactory属性是否指定factory实现
     4. 使用默认factory实现,org.apache.commons.logging.impl.LogFactoryImpl
 
     LogFactory的getLog()方法返回类型是org.apache.commons.logging.Log接口,提供了从trace到fatal方法。可以确定,如果日志实现提供者只要实现该接口,并且使用继承自org.apache.commons.logging.LogFactory的子类创建Log,必然可以构建一个松耦合的日志系统。
 

四、log4j+commons-logging解耦
     使用commons-logging解耦log4j,可按照下列步骤:
     1. 将log4j和commons-logging依赖放入classpath:
     <!--commons-logging-->
<dependency>
    <groupId>commons-logging</groupId>
    <artifactId>commons-logging</artifactId>
    <version>1.2</version>
</dependency>
<!-- log4j -->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>
 
2. 配置log4j.xml或者log4j.properties,放入类路径根目录;
 
3. 使用commons-logging得LogFactory获取日志实例
log4j提供了LogManager.getLogger(String) 方法创建日志实例,但是为了日志实现的解耦,必须使用commons-logging提供的抽象工厂LogFactory创建日志实例:
Log logger = LogFactory.getLog(String)
接下来就可以使用logger输出日志信息了。
 
log4j如何被commons-logging加载?
根据上述commons-logging解耦的原理,如果log4j是通过继承抽象工厂org.apache.commons.logging.LogFactory来创建实现org.apache.commons.logging.Log接口的
日志实例的,那么就可以通过3种方法让commons-logging加载log4j,分别是:
     1. 从vm系统属性org.apache.commons.logging.LogFactory
     2. 使用SPI服务发现机制,发现org.apache.commons.logging.LogFactory的实现
     3. 查找classpath根目录commons-logging.properties的org.apache.commons.logging.LogFactory属性是否指定factory实现
     
但是,查看log4j的jar包,发现在META-INF下面并没有名为services得文件夹,更别说org.apache.commons.logging.LogFactory文件了:     
     
     而且,Logger类也没有实现commons-logging提供的Log接口:
     
     log4j中也没有找到继承org.apache.commons.logging.LogFactory的类。
     那么,log4j是如何被commons-logging加载的呢?
 
     按照上述LogFactory寻找factory实现的流程,LogFactory在找不到org.apache.commons.logging.LogFactory实现时,会使用默认实现org.apache.commons.logging.impl.LogFactoryImpl。
     
     通过分析LogFactoryImpl的getInstance()方法,其调用以下方法获得logger实例:
     protected Log newInstance(String name) throws LogConfigurationException {
        Log instance;
        try {
            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) {
 
            // this type of exception means there was a problem in discovery
            // and we've already output diagnostics about the issue, etc.;
            // just pass it on
            throw lce;
 
        } catch (InvocationTargetException e) {
            // A problem occurred invoking the Constructor or Method
            // previously discovered
            Throwable c = e.getTargetException();
            throw new LogConfigurationException(c == null ? e : c);
        } catch (Throwable t) {
            handleThrowable(t); // may re-throw t
            // A problem occurred invoking the Constructor or Method
            // previously discovered
            throw new LogConfigurationException(t);
        }
    }
     
     discoverLogImplementation方法如下:
     1. 该方法首先查找用户是否指定了日志实现
     2. classesToDiscover定义如下:
     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数组元素的顺序,依次创建对应Log实例,直到返回成功创建的Log实例
 
    private Log discoverLogImplementation(String logCategory) throws LogConfigurationException {
        if (isDiagnosticsEnabled()) {
            logDiagnostic("Discovering a Log implementation...");
        }
 
        initConfiguration();
 
        Log result = null;
 
        // See if the user specified the Log implementation to use
        // 查找用户是否指定了日志实现?
        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;
        }
 
        // No user specified log; try to discover what's on the classpath
        //
        // Note that we deliberately loop here over classesToDiscover and
        // expect method createLogFromClass to loop over the possible source
        // classloaders. The effect is:
        //   for each discoverable log adapter
        //      for each possible classloader
        //          see if it works
        //
        // It appears reasonable at first glance to do the opposite:
        //   for each possible classloader
        //     for each discoverable log adapter
        //        see if it works
        //
        // The latter certainly has advantages for user-installable logging
        // libraries such as log4j; in a webapp for example this code should
        // first check whether the user has provided any of the possible
        // logging libraries before looking in the parent classloader.
        // Unfortunately, however, Jdk14Logger will always work in jvm>=1.4,
        // and SimpleLog will always work in any JVM. So the loop would never
        // ever look for logging libraries in the parent classpath. Yet many
        // users would expect that putting log4j there would cause it to be
        // detected (and this is the historical JCL behaviour). So we go with
        // the first approach. A user that has bundled a specific logging lib
        // in a webapp should use a commons-logging.properties file or a
        // service file in META-INF to force use of that logging lib anyway,
        // rather than relying on discovery.
 
        if (isDiagnosticsEnabled()) {
            logDiagnostic(
                "No user-specified Log implementation; performing discovery" +
                " using the standard supported logging implementations...");
        }
        // 用户没有指定日志实现的情况下,按照classesToDiscover数组元素的顺序,依次创建对应Log实例,直到返回成功创建的Log实例
        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;
    } 
 
findUserSpecifiedLogClassName方法如下:
1. 该方法首先试图获取commons-logging.properties 中的org.apache.commons.logging.Log指定的日志实现
2. 试图获取commons-logging.properties 中的org.apache.commons.logging.log指定的日志实现
3. 试图获取vm系统属性org.apache.commons.logging.Log指定的日志实现
4. 试图获取vm系统属性org.apache.commons.logging.log指定的日志实现
5. 都没有返回null
private String findUserSpecifiedLogClassName() {
        if (isDiagnosticsEnabled()) {
            logDiagnostic("Trying to get log class from attribute '" + LOG_PROPERTY + "'");
        }
        // 试图获取commons-logging.properties 中的org.apache.commons.logging.Log指定的日志实现
        String specifiedClass = (String) getAttribute(LOG_PROPERTY);
 
        if (specifiedClass == null) { // @deprecated
            if (isDiagnosticsEnabled()) {
                logDiagnostic("Trying to get log class from attribute '" +
                              LOG_PROPERTY_OLD + "'");
            }
            // 试图获取commons-logging.properties 中的org.apache.commons.logging.log指定的日志实现
            specifiedClass = (String) getAttribute(LOG_PROPERTY_OLD);
        }
 
        if (specifiedClass == null) {
            if (isDiagnosticsEnabled()) {
                logDiagnostic("Trying to get log class from system property '" +
                          LOG_PROPERTY + "'");
            }
            try {
                // 试图获取vm系统属性org.apache.commons.logging.Log指定的日志实现
                specifiedClass = getSystemProperty(LOG_PROPERTY, null);
            } catch (SecurityException e) {
                if (isDiagnosticsEnabled()) {
                    logDiagnostic("No access allowed to system property '" +
                        LOG_PROPERTY + "' - " + e.getMessage());
                }
            }
        }
 
        if (specifiedClass == null) { // @deprecated
            if (isDiagnosticsEnabled()) {
                logDiagnostic("Trying to get log class from system property '" +
                          LOG_PROPERTY_OLD + "'");
            }
            try {
                // 试图获取vm系统属性org.apache.commons.logging.log指定的日志实现
                specifiedClass = getSystemProperty(LOG_PROPERTY_OLD, null);
            } catch (SecurityException e) {
                if (isDiagnosticsEnabled()) {
                    logDiagnostic("No access allowed to system property '" +
                        LOG_PROPERTY_OLD + "' - " + e.getMessage());
                }
            }
        }
 
        // Remove any whitespace; it's never valid in a classname so its
        // presence just means a user mistake. As we know what they meant,
        // we may as well strip the spaces.
        if (specifiedClass != null) {
            specifiedClass = specifiedClass.trim();
        }
 
        return specifiedClass;
    }
 
     如此一来,如果像上述一样没有指定任何org.apache.commons.logging.Log的实现,那么commons-logging首先使用的是  
     org.apache.commons.logging.impl.Log4JLogger作为Log实现,可以看到Log4jLogger是实现了Log接口的:
     public class Log4JLogger implements Log, Serializable;
     Log4jLogger则是通过组合log4j提供的org.apache.log4j.Logger,来提供trace到fatal功能。
     
     在log4j的jar包没有导入到classpath之前,这个类是无法通过编译的。
 
     到现在应该知道了commons-logging是如何与log4j实现解耦的了,不是通过commons-logging提供的抽象工厂,而是通过    
     org.apache.commons.logging.impl.LogFactoryImpl加载具体的日志实现。不得不说,在我看来这种设计极其不好,完全可以用抽象工厂提供的日志实现加载!
 

五、log4j2+commons-logging解耦
     log4j2是Apache推出的log4j的升级版本,在性能等各方面都做了提升。
     要想使用commons-logging解耦log4j2,可按照下列步骤:
     1. 导入log4j2, commons-logging, log4j-jcl依赖
     其中,log4j-jcl是用来作为log4j和commons-logging的桥接的
     <!--log4j2-->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>2.7</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.7</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-jcl</artifactId>
    <version>2.7</version>
</dependency>
 
     2. 配置好log4j2依赖的配置
     log4j2支持了更多的配置方式,包括xml和json格式;若不指定配置文件位置,将其放置在classpath根路径
 
     3. 创建日志实例使用
     log4j2也提供了自己的日志实例创建的方法:
     logger = LogManager.getLogger(Log4jTwoTest.class);
     为了实现解耦,需要使用commons-logging提供的抽象共产LogFactory.getLog(String)方法获取日志实例即可;
 
     解耦原理:
     log4j-jcl提供了解耦的实现,观察其jar包:
     
     该文件内容为:org.apache.logging.log4j.jcl.LogFactoryImpl
     根据commons-logging抽象日志工厂的第二步骤spi服务发现规则,抽象工厂使用org.apache.logging.log4j.jcl.LogFactoryImpl作为实际产生日志实现的工厂。
     分析LogFactoryImpl的代码:
     private final LoggerAdapter<Log> adapter = new LogAdapter();
 
private final ConcurrentMap<String, Object> attributes = new ConcurrentHashMap<>();
 
@Override
public Log getInstance(final String name) throws LogConfigurationException {
    return adapter.getLogger(name);
}
 
Log实例是由LogAdapter类的getLogger(String)方法创建的,其继承了抽象适配器AbstractLoggerAdapter<Log>的getLogger方法:
public L getLogger(String name) {
    LoggerContext context = this.getContext();
    ConcurrentMap loggers = this.getLoggersInContext(context);
    Object logger = loggers.get(name);
    if(logger != null) {
        return logger;
    } else {
        loggers.putIfAbsent(name, this.newLogger(name, context));
        return loggers.get(name);
    }
}
 
最终调用LogAdapter的newLogger()方法产生日志实例:
@Override
protected Log newLogger(final String name, final LoggerContext context) {
    return new Log4jLog(context.getLogger(name));
}
 
Log4jLog组合了log4j2中的ExtendedLogger提供日志打印功能。
可以看出,log4j2和commons-logging的解耦,完全使用了commons-logging提供的spi服务发现机制。
     
     
 
 
 
参考:
SimpleLog类介绍:
commons.logging1.1.1源代码研究(2)-- 基本使用及配置文件
使用commons.logging中的SimpleLog显示调试和日志信息
Apache Commons Logging 是如何决定使用哪个日志实现类的
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

commons-logging日志实现解耦的更多相关文章

  1. Spring 使用 SLF4J代替 Commons Logging 写日志 异常

    项目的日志更换成slf4j和logback后,发现项目无法启动.错误提示 Caused by: java.lang.NoClassDefFoundError: Lorg/apache/commons/ ...

  2. 廖雪峰Java3异常处理-2断言和日志-3使用Commons Logging

    Commons Logging是Apache创建的日志模块: 可以挂接不同的日志系统 可以通过配置文件指定挂接的日志系统 自动搜索并使用Log4j 如果Log4j不存在,使用JDK Logging(J ...

  3. org.apache.log4j与org.apache.commons.logging这两个包有什么区别

    apache common logging是一种log的框架接口,它本身并不实现log记录的功能,而是在运行时动态查找目前存在的日志库,调用相关的日志函数,从而隐藏具体的日志实现log4j是具体的日志 ...

  4. 使用slf4j取代Apache Commons Logging

    假如你正在开发应用程序所调用的组件当中已经使用了 JCL(之前叫 Jakarta Commons Logging,JCL) 的,还有一些组建可能直接调用了 java.util.logging,这时你需 ...

  5. com.sun.org.apache.commons.logging.LogConfigurationException: java.lang.NullPointerException

    在日志中, 查看导入的包是否是 import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory;

  6. day31 logging 日志模块

    # logging 日志模块 ****** # 记录用户行为或者代码执行过程 # print 来回注释比较麻烦的 # logging # 我能够“一键”控制 # 排错的时候需要打印很多细节来帮助我排错 ...

  7. 使用Commons Logging

    Commons Logging 和Java标准库提供的日志不同,Commons Logging是一个第三方日志库,它是由Apache创建的日志模块,需要导入commons-logging-1.2.ja ...

  8. Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/commons/logging/LogFactory

    学习架构探险,从零开始写Java Web框架时,在学习到springAOP时遇到一个异常: "C:\Program Files\Java\jdk1.7.0_40\bin\java" ...

  9. logging 日志模块学习

    logging 日志模块,用于记录系统在运行过程中的一些关键信息,以便于对系统的运行状况进行跟踪,所以还是灰常重要滴,下面我就来从入门到放弃的系统学习一下日志既可以在屏幕上显示,又可以在文件中体现. ...

随机推荐

  1. jQuery-How to Create a Basic Plugin

    官方插件:http://learn.jquery.com/plugins/basic-plugin-creation/ $.extend方法和$.fn.extend方法都可以用来扩展jQuery功能. ...

  2. HBase基础讲解

    HBase定义        HBase 是一个高可靠.高性能.面向列.可伸缩的分布式存储系统,利用Hbase技术可在廉价PC Server上搭建 大规模结构化存储集群.        HBase 是 ...

  3. Linux远程连接和FTP

    远程连接: ssh 用户名@IP地址 输入密码 FTP连接: sftp 用户名@IP 输入密码 远程连接支持所有命令,就相当于操作电脑 FTP连接只支持部分命令: ls.cd.get.put get: ...

  4. <linux下内置命令和外部命令>

    Linux下内置命令和外部命令 1.linux的命令可以分为内部命令和外部命令: 内部命令在系统启动时就调入内存,是常驻内存的,所以执行效率高. 而外部命令是系统的软件功能,用户需要时才从硬盘中读入内 ...

  5. notepad++上配置ruby执行环境

    1.安装NppExec 插件 2.按快捷键F6,在弹出框中输入如下命令: npp_save  cd "$(CURRENT_DIRECTORY)"  jruby "$(FI ...

  6. orcale函数

    字符函数    1.ASCII 返回与指定的字符对应的十进制数;  select ascii('A') A,ascii('a') a,ascii('0') zero,ascii(' ') space ...

  7. Backbone源码风格

         代码风格: 一.自执行匿名函数创建执行环境 var root = this; root保存全局执行环境的指针.浏览器端为window对象 二.依赖库 (1).underscore 如果bac ...

  8. vue-extend 选项

    vue-extend 选项 mixins 和extend 很相似,但有区别: var extendNews={ //后来的内容用变量接收 updated:function(){ console.log ...

  9. js基础笔录

    1.页面中获取对象 document.getElementById("demo") 2.在页面加载时向 HTML 的 <body> 写文本 document.write ...

  10. Gym - 100676H Capital City(边强连通分量 + 树的直径)

    H. Capital City[ Color: Black ]Bahosain has become the president of Byteland, he is doing his best t ...