源码分析slf4j和logback的关系

来源:互联网 发布:软件研制过程概述 编辑:程序博客网 时间:2024/05/16 10:05

slf4j是几种日志框架的api框架,提供了几种常用日志框架的接口规范。如果在项目中引入了下面的依赖即可:

              <dependency>                <groupId>ch.qos.logback</groupId>                <artifactId>logback-classic</artifactId>                <version>1.1.3</version>            </dependency>

logback会依赖传递进来slfj4j的api包。项目启动时走读一下源码,首先通过 private static final Logger logger= LoggerFactory.getLogger(xx.class);获取logger。这里的调用暂时都是走在slfj4-api包里面的。参数传递的是类,实际会调用获取类name的重载函数如下:

public static Logger getLogger(String name) { ILoggerFactory iLoggerFactory = getILoggerFactory();        return iLoggerFactory.getLogger(name);    }

getILoggerFactory方法如下:

    public static ILoggerFactory getILoggerFactory() {        if (INITIALIZATION_STATE == UNINITIALIZED) {            INITIALIZATION_STATE = ONGOING_INITIALIZATION;            performInitialization();        }        switch (INITIALIZATION_STATE) {        case SUCCESSFUL_INITIALIZATION:            return StaticLoggerBinder.getSingleton().getLoggerFactory();        case NOP_FALLBACK_INITIALIZATION:            return NOP_FALLBACK_FACTORY;        case FAILED_INITIALIZATION:            throw new IllegalStateException(UNSUCCESSFUL_INIT_MSG);        case ONGOING_INITIALIZATION:            // support re-entrant behavior.            // See also http://bugzilla.slf4j.org/show_bug.cgi?id=106            return TEMP_FACTORY;        }        throw new IllegalStateException("Unreachable code");    }

一般情况下performInitialization()这个日志工厂初始化方法已经被提前调用了,为什么呢,这个初始化方法只会在第一次调用getILoggerFactory()被调用,因为你的项目中引入的别的常用框架在启动的时候可能会第一次调用了这个方法。比如较新颁布的spring框架等等。
如果是第一次调用,进入初始化方法看一下:

    private final static void bind() {        try {            Set<URL> staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();//寻找真实日志绑定类路径            reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);            // the next line does the binding            StaticLoggerBinder.getSingleton();            INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;            reportActualBinding(staticLoggerBinderPathSet);            fixSubstitutedLoggers();        } catch (NoClassDefFoundError ncde) {            String msg = ncde.getMessage();            if (messageContainsOrgSlf4jImplStaticLoggerBinder(msg)) {                INITIALIZATION_STATE = NOP_FALLBACK_INITIALIZATION;                Util.report("Failed to load class \"org.slf4j.impl.StaticLoggerBinder\".");                Util.report("Defaulting to no-operation (NOP) logger implementation");                Util.report("See " + NO_STATICLOGGERBINDER_URL + " for further details.");            } else {                failedBinding(ncde);                throw ncde;            }        } catch (java.lang.NoSuchMethodError nsme) {            String msg = nsme.getMessage();            if (msg != null && msg.indexOf("org.slf4j.impl.StaticLoggerBinder.getSingleton()") != -1) {                INITIALIZATION_STATE = FAILED_INITIALIZATION;                Util.report("slf4j-api 1.6.x (or later) is incompatible with this binding.");                Util.report("Your binding is version 1.5.5 or earlier.");                Util.report("Upgrade your binding to version 1.6.x.");            }            throw nsme;        } catch (Exception e) {            failedBinding(e);            throw new IllegalStateException("Unexpected initialization failure", e);        }    }

走了两层调用进入了bind()方法,为什么叫bind不叫 init之类的呢。其实这个方法最重要是做了从日志api框架到真实实现的logback和log4j的绑定。这个方法里面调用的第一个方法findPossibleStaticLoggerBinderPathSet()是寻找到真实的日志绑定类。进入如下:

    private static Set<URL> findPossibleStaticLoggerBinderPathSet() {        // use Set instead of list in order to deal with bug #138        // LinkedHashSet appropriate here because it preserves insertion order during iteration        Set<URL> staticLoggerBinderPathSet = new LinkedHashSet<URL>();        try {            ClassLoader loggerFactoryClassLoader = LoggerFactory.class.getClassLoader();            Enumeration<URL> paths;            if (loggerFactoryClassLoader == null) {                paths = ClassLoader.getSystemResources(STATIC_LOGGER_BINDER_PATH);            } else {                paths = loggerFactoryClassLoader.getResources(STATIC_LOGGER_BINDER_PATH);            }            while (paths.hasMoreElements()) {                URL path = (URL) paths.nextElement();                staticLoggerBinderPathSet.add(path);            }        } catch (IOException ioe) {            Util.report("Error getting resources from path", ioe);        }        return staticLoggerBinderPathSet;    }

首先获取类加载器,这个获取的是WebAppClassLoader。为什么会是这个类加载器,这个加载器的作用是啥?先看一下tomcat的类加载器树。
tomcat类加载树
java的类加载是采用双亲委派机制,默认只能从下往上,不过有几种方法可以破坏双亲委派机制,实现更高层的加载器调用更加低层的加载器的方式。不过不是本文的重点。如果是从jsp的类加载器就从 jasperLoader开始。本文中的类获取加载器时向上检索,第一个获取的是webAppClassLoader类加载器。这个类加载器的默认类文件检索范围是整个web应用(不包括依赖的所有可以由更高层类加载器加载的类)。然后调用
ClassLoader.getSystemResources(STATIC_LOGGER_BINDER_PATH)//STATIC_LOGGER_BINDER_PATH的值是org/slf4j/impl/StaticLoggerBinder.class

最终获取类文件的url路径 jar:file:/Users/XXX/Work/apache-tomcat-7.0.64/webapps/ROOT/WEB-INF/lib/logback-classic-1.1.3.jar!/org/slf4j/impl/StaticLoggerBinder.class
StaticLoggerBinder是logback的一个绑定类,该类实现了slf4j的LoggerFactoryBinder的接口。
最后StaticLoggerBinder.getSingleton()在StaticLoggerBinder的init()方法里完成了对logback的工厂类LoggerContext(实现了ILoggerFactory)的初始化,包括从配置文件如logback.xml里面加载配置信息。完成了整个logger工厂的初始化。未完待续

0 0
原创粉丝点击