slf4j 源码解读

1 核心接口

2 LoggerFactory.getLogger

    public static Logger getLogger(String name) {
        ILoggerFactory iLoggerFactory = getILoggerFactory();
        return iLoggerFactory.getLogger(name);
    }
//定义了5种状态,标识LoggerFactory当前状态
//未初始化
    static final int UNINITIALIZED = 0;
//正在初始化
    static final int ONGOING_INITIALIZATION = 1;
//初始化失败
    static final int FAILED_INITIALIZATION = 2;
//初始化成功
    static final int SUCCESSFUL_INITIALIZATION = 3;
//无Logger初始化
    static final int NOP_FALLBACK_INITIALIZATION = 4;

    static volatile int INITIALIZATION_STATE = UNINITIALIZED;
   static final SubstituteLoggerFactory SUBST_FACTORY = new SubstituteLoggerFactory();
   static final NOPLoggerFactory NOP_FALLBACK_FACTORY = new NOPLoggerFactory();
   
    public static ILoggerFactory getILoggerFactory() {
//为了兼顾效率和线程安全,此处采用了双重检查锁(double checked locking)。先判断对象是否已经被初始化,再决定要不要加锁。
//注意:INITIALIZATION_STATE 字段采用了volatile关键字修饰。重排序被禁止,所有的写(write)操作都将发生在读(read)操作之前
        if (INITIALIZATION_STATE == UNINITIALIZED) {
            synchronized (LoggerFactory.class) {
                if (INITIALIZATION_STATE == UNINITIALIZED) {
                    INITIALIZATION_STATE = ONGOING_INITIALIZATION;
	//多线程情况下,INITIALIZATION_STATE被修改成ONGOING_INITIALIZATION后,performInitialization()需要一定的时间来完成。
	//如果此时其他线程调用getILoggerFactory()方法,会直接返回SUBST_FACTORY,用来暂时接管目标ILoggerFactory。
                    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://jira.qos.ch/browse/SLF4J-97
            return SUBST_FACTORY;
        }
        throw new IllegalStateException("Unreachable code");
    }
    private final static void performInitialization() {
        bind();
        if (INITIALIZATION_STATE == SUCCESSFUL_INITIALIZATION) {
            versionSanityCheck();
        }
    }
  private final static void bind() {
        try {
            Set<URL> staticLoggerBinderPathSet = null;
            // skip check under android, see also
            // http://jira.qos.ch/browse/SLF4J-328
            if (!isAndroid()) {
		//通过类加载器,加载StaticLoggerBinder类。
                staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
		//如果加载了多个StaticLoggerBinder类(size>1),则打印告警。
                reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);
            }

	//确保StaticLoggerBinder类实现了getSingleton方法。因为getILoggerFactory()方法中:
	// switch (INITIALIZATION_STATE) {
	//case SUCCESSFUL_INITIALIZATION:
	//return StaticLoggerBinder.getSingleton().getLoggerFactory();

            StaticLoggerBinder.getSingleton();

	//设置LoggerFactory的状态为SUCCESSFUL_INITIALIZATION
            INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
	//打印最终采用的StaticLoggerBinder名字
            reportActualBinding(staticLoggerBinderPathSet);
	//多线程情况下,INITIALIZATION_STATE被修改成ONGOING_INITIALIZATION后,performInitialization()需要一定的时间来完成。
	//如果此时其他线程调用getILoggerFactory()方法,会直接返回SUBST_FACTORY,用来暂时接管目标ILoggerFactory。
	//SUBST_FACTORY的默认实现是SubstituteLoggerFactory。
	//SubstituteLoggerFactory内部有一个eventQueue,会缓存performInitialization()期间所有的日志事件。
	//此处,LoggerFactory初始化完成后,立刻将eventQueue缓存的所有日志事件重演一遍,用目标Logger打印输出。
            fixSubstituteLoggers();
            replayEvents();
            // release all resources in SUBST_FACTORY
            SUBST_FACTORY.clear();
        } 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.contains("org.slf4j.impl.StaticLoggerBinder.getSingleton()")) {
                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);
        }
    }
    private static String STATIC_LOGGER_BINDER_PATH = "org/slf4j/impl/StaticLoggerBinder.class";

//通过类加载器,去加载org/slf4j/impl/StaticLoggerBinder.class类。
//每个具体日志实现(logback、log4j等)适配slf4j时,必须包含org/slf4j/impl/StaticLoggerBinder.class类。
    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 = paths.nextElement();
                staticLoggerBinderPathSet.add(path);
            }
        } catch (IOException ioe) {
            Util.report("Error getting resources from path", ioe);
        }
        return staticLoggerBinderPathSet;
    }

3 slf4j-simple

典型的StaticLoggerBinder实现:

//必须是这个包路径,不能是其他包路径
package org.slf4j.impl;

import org.slf4j.ILoggerFactory;
import org.slf4j.LoggerFactory;
import org.slf4j.spi.LoggerFactoryBinder;

public class StaticLoggerBinder implements LoggerFactoryBinder {

    /**
     * The unique instance of this class.
     * 
     */
    private static final StaticLoggerBinder SINGLETON = new StaticLoggerBinder();

    /**
     * Return the singleton of this class.
     * 
     * @return the StaticLoggerBinder singleton
     */
    public static final StaticLoggerBinder getSingleton() {
        return SINGLETON;
    }

    /**
     * Declare the version of the SLF4J API this implementation is compiled against. 
     * The value of this field is modified with each major release. 
     */
    // to avoid constant folding by the compiler, this field must *not* be final
    public static String REQUESTED_API_VERSION = "1.6.99"; // !final

    private static final String loggerFactoryClassStr = SimpleLoggerFactory.class.getName();

    /**
     * The ILoggerFactory instance returned by the {@link #getLoggerFactory}
     * method should always be the same object
     */
    private final ILoggerFactory loggerFactory;

    private StaticLoggerBinder() {
        loggerFactory = new SimpleLoggerFactory();
    }

    public ILoggerFactory getLoggerFactory() {
        return loggerFactory;
    }

    public String getLoggerFactoryClassStr() {
        return loggerFactoryClassStr;
    }
}

4 自己实现一个简单的log

源码:https://github.com/tsingmuhe/log-sample

5 总结

  1. 核心接口和类
  2. 双重检查锁(double checked locking) volatile
  3. LoggerFactory的5种状态
  4. org/slf4j/impl/StaticLoggerBinder.class的作用及简单实现
  5. 类加载器
  6. 如何实现一个简单的log

转载于:https://my.oschina.net/sunchp/blog/3034082

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值