u010900754的专栏

真心希望国内博客环境 多一些原创 少一些复制

【Java】common-logging 日志解耦

之前介绍了常用的日志组件log4j的使用,如果直接使用log4j会有一个问题,那就是我们的应用会与日志组件耦合太紧,如果想要换一个日志的实现比如log4j2(只是举个例子,没准真的可以无缝升级),可能我们需要把每一个调用了log4j接口的地方全部修改,如果是一个大工程,这个非常头疼也可能出错。

所以也就有了common-logging组件的出现,这个组件就是做日志解耦的。大体上,它定义了日志组件的接口,而且提供了简单的实现,当然这个实现还是比较鸡肋的,一般不会用。但是通用的日志组件都会实现这个接口,所以应用中使用common-logging就可以无缝切换到任意一个实现类中,这样就是一种松耦合的设计。

那么最好的使用方法就是common-logging + log4j。

我们直接用log4j:

log4j.rootLogger = DEBUG, mylogger

log4j.appender.mylogger = org.apache.log4j.ConsoleAppender
log4j.appender.mylogger.Target = System.out
log4j.appender.mylogger.layout = org.apache.log4j.PatternLayout
log4j.appender.mylogger.layout.ConversionPattern = [%p][%l,%d]:  %m %c %n
import org.apache.log4j.Logger;


public class T {
    public static void main(String args[]){
        Logger logger = Logger.getLogger(T.class);

        logger.info("log4j");
    }
}


项目中直接建立了对log4j的依赖。

如果用了common-logging:

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
//import org.apache.log4j.Logger;


public class T {
    public static void main(String args[]){
//        Logger logger = Logger.getLogger(T.class);
//
//        logger.info("log4j");

        Log logger = LogFactory.getLog(T.class);
        logger.info("common-logging + log4j");
    }
}

可以看到,二者的接口是不同的,common是Log类。那么现在我们想换一种实现,就比如common包的简单实现simpleLog。

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
//import org.apache.log4j.Logger;


public class T {
    public static void main(String args[]){
//        Logger logger = Logger.getLogger(T.class);
//
//        logger.info("log4j");

        Log logger = LogFactory.getLog(T.class);
        logger.info("common-logging + simplelog");
    }
}

只需要把log4j的依赖移除,换一下输出语句即可,源代码并不需要修改。


这里需要指出的是,如果项目中有了log4j的实现和配置,那么common将默认启动log4j,否则再找其他实现,都没有则使用自身默认实现。simplelog只能输出到system.err。

我们可以手动指定,那就是在classpath下新建common-logging.properties:

org.apache.commons.logging.Log=org.apache.commons.logging.impl.SimpleLog
来指定实现类。

那么common是如何识别log4j?这涉及到了common里getLogger方法的实现。


官网解释的很清楚。首先找配置文件,如果没有就在classpath下找log4j,如果没有就看是否是jdk1.4,如果是就是用jdk14logger,否则就是用simplelog。


调用LogFactory的get方法,默认是LogFactoryImpl类的get。

    public static Log getLog(Class clazz)
        throws LogConfigurationException {

        return (getFactory().getInstance(clazz));

    }

LogFactoryImpl:

    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 = null;
        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);

这里的discoverLogImplementation方法就是真正寻找实现的代码:

 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
                if (specifiedLogClassName != null) {
                    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...");
        }
        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;        
    }

这里的注释很清楚,先找配置文件,否则从一个数组的配置中找实现类:

    /**
     * The names of classes that will be tried (in order) as logging
     * adapters. Each class is expected to implement the Log interface,
     * and to throw NoClassDefFound or ExceptionInInitializerError when
     * loaded if the underlying logging library is not available. Any 
     * other error indicates that the underlying logging library is available
     * but broken/unusable for some reason.
     */
    private static final String[] classesToDiscover = {
            LOGGING_IMPL_LOG4J_LOGGER,
            "org.apache.commons.logging.impl.Jdk14Logger",
            "org.apache.commons.logging.impl.Jdk13LumberjackLogger",
            "org.apache.commons.logging.impl.SimpleLog"
    };
    

也就是官网中那段介绍。

所以,下次在使用log4j日志时,加上common-logging才会更健壮。


阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010900754/article/details/79951769
文章标签: common-logging
个人分类: java
想对作者说点什么? 我来说一句

commons-logging-1.1.3.jar

2018年02月25日 194KB 下载

没有更多推荐了,返回首页

不良信息举报

【Java】common-logging 日志解耦

最多只允许输入30个字

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭