logback源码阅读(一)获取ILoggerFactory、Logger

什么是slf4j

说到logback,必会提及slf4j。slf4j的全称是 : The Simple Logging Facade for Java,是java的一个日志门面,堪称Facade设计模式的最佳代言人! slf4j制定了一套标准接口,从而实现在[日志]这个领域里一统江湖。主要做两件事:

  1. 实现了Logger获取的通用api: LoggreFactory.getLogger(), 该方法基本上确定了获取L ogger的基础套路,约定了实现sIf4j的日志框架需要实现org.slf4j.impl.StaticLoggerBinder,完成与slf4j的集成。
  2. 定义了ILoggerFactory, Logger等系列接口,该接口由具体日志框架实现。

值得强调的是:flf4j是接口,提供API,而 java.util.logging,logback,log4j等才是真正的日志框架

在这里插入图片描述

logback

Logback是由log4j创始人设计的另一个开源日志组件,官方网站: http://logback.qos.ch 是log4j的改良版本,具有更快的实现:
Logback的内核重写了,在一些关键执行路径上性能提升10倍以上。而且logback不仅性能提升了,初始化内存加载也更小了。

logback 初始化

当一个类使用了@Slf4j注解,lombok是会自动为该类生成一个logger字段的,下面是Slf4j注解的注释

Example:
   @Slf4j
   public class LogExample {
   }
   
will generate:
   public class LogExample {
       private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogExample.class);
   }

我们随便打开一个使用了Slf4j注解的类,查看Structure,可以看到的确生成了对应的logger
在这里插入图片描述
那么,我们就从LoggerFactory.getLogger(this.getClass())这个方法入手,开始一探究竟,本文源码为slf4f-api-1.7.36

getLogger()

public static Logger getLogger(Class<?> clazz) {
        Logger logger = getLogger(clazz.getName());
        if (DETECT_LOGGER_NAME_MISMATCH) {
            Class<?> autoComputedCallingClass = Util.getCallingClass();
            if (autoComputedCallingClass != null && nonMatchingClasses(clazz, autoComputedCallingClass)) {
                Util.report(String.format("Detected logger name mismatch. Given name: \"%s\"; computed name: \"%s\".", logger.getName(),
                                autoComputedCallingClass.getName()));
                Util.report("See " + LOGGER_NAME_MISMATCH_URL + " for an explanation");
            }
        }
        return logger;
    }

可以看到,调用getLogger获取logger,如果 clazz 参数与 SLF4J 内部计算的调用者名称不同,则会打印记录器名称不匹配警告,但前提是 slf4j.detectLoggerNameMismatch 系统属性设置为 true。默认情况下,此属性未设置,即使在记录器名称不匹配的情况下也不会打印任何警告。

继续查看getLogger方法

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

每种日志框架都实现了LoggerFactory 这里就是根据集成的日志框架获得对应的factory,接下来看getILoggerFactory方法如何实现

public static ILoggerFactory getILoggerFactory() {
		//没有出初始化则加锁初始化并标记为正在初始化状态
        if (INITIALIZATION_STATE == UNINITIALIZED) {
            synchronized (LoggerFactory.class) {
                if (INITIALIZATION_STATE == UNINITIALIZED) {
                    INITIALIZATION_STATE = ONGOING_INITIALIZATION;
                    //执行初始化
                    performInitialization();
                }
            }
        }
        switch (INITIALIZATION_STATE) {
        case SUCCESSFUL_INITIALIZATION://初始化成功,返回loggerFactory
            return StaticLoggerBinder.getSingleton().getLoggerFactory();
        case NOP_FALLBACK_INITIALIZATION://没有找到StaticLoggerBinder则返回NOPLoggerFactory
            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
            //如果 正在初始化,返回替代工厂SubstituteLoggerFactory,日志一般也是委托给NOPLogger
            return SUBST_FACTORY;
        }
        throw new IllegalStateException("Unreachable code");
    }
	static final int UNINITIALIZED = 0;//未初始化
    static final int ONGOING_INITIALIZATION = 1;//正在初始化
    static final int FAILED_INITIALIZATION = 2;//初始化失败,没有实现LoggerFactoryBinder 相关方法,一般是版本过低
    static final int SUCCESSFUL_INITIALIZATION = 3;//初始化成功
    static final int NOP_FALLBACK_INITIALIZATION = 4;//找不到StaticLoggerBinder

我们最关心的应该是performInitialization();

private final static void performInitialization() {
        bind();
        if (INITIALIZATION_STATE == SUCCESSFUL_INITIALIZATION) {
            versionSanityCheck();
        }
    }

bind()

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()) {
                staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
                reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);
            }
            // the next line does the binding
            StaticLoggerBinder.getSingleton();
            INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
            //打印出实际的日志绑定器 ContextSelectorStaticBinder
            reportActualBinding(staticLoggerBinderPathSet);
        } 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);
        } finally {
            postBindCleanUp();
        }
    }
  1. 非android平台时找到可能的静态日志绑定器的路径集合findPossibleStaticLoggerBinderPathSet()(在classpath查找 org/slf4j/impl/StaticLoggerBinder.class)
  2. 如果日志有多个绑定器,打印到控制台reportMultipleBindingAmbiguity
    如果是android平台,忽略
    依次打印出多个日志绑定器,并给出文档提示
  3. 获得唯一的静态日志绑定器StaticLoggerBinder.getSingleton()
    绑定器内部持有LoggerContext和ContextSelectorStaticBinder
  4. 设置初始化状态为成功
  5. 打印出实际的日志绑定器 ContextSelectorStaticBinder

其中1.2源码比较简单不在展开,重点是第三步

public static StaticLoggerBinder getSingleton() {
        return SINGLETON;
    }

静态代码块触发init方法

static {
        SINGLETON.init();
    }
    
void init() {
        try {
            try {
            //委托ContextInitializer初始化LoggerContext
                new ContextInitializer(defaultLoggerContext).autoConfig();
            } catch (JoranException je) {
                Util.report("Failed to auto configure default logger context", je);
            }
            // logback-292
            //如果没有配置状态监听器,则打印出警告
            if (!StatusUtil.contextHasStatusListener(defaultLoggerContext)) {
                StatusPrinter.printInCaseOfErrorsOrWarnings(defaultLoggerContext);
            }
            //上下文选择绑定器初始化
            contextSelectorBinder.init(defaultLoggerContext, KEY);
            initialized = true;
        } catch (Exception t) { // see LOGBACK-1159
            Util.report("Failed to instantiate [" + LoggerContext.class.getName() + "]", t);
        }
    }

ContextInitializer.autoConfig()

public void autoConfig() throws JoranException {
         //如果没有,安装状态监听器
         StatusListenerConfigHelper.installIfAsked(loggerContext);
      
        //找到默认的配置文件或者URL,一次按照系统属性logback.configurationFile查找,按照logback-test.xml,logback.groovy,logback.xml得到配置文件
        URL url = findURLOfDefaultConfigurationFile(true);
        if (url != null) {
        //如果找到了,configureByResource(url)
            configureByResource(url);
        } else {//否则,按照spi的方式找到Configurator的实现类,设置上下文,进行配置
            Configurator c = EnvUtil.loadFromServiceLoader(Configurator.class);
            if (c != null) {
                try {
                    c.setContext(loggerContext);
                    c.configure(loggerContext);
                } catch (Exception e) {
                    throw new LogbackException(String.format("Failed to initialize Configurator: %s using ServiceLoader", c != null ? c.getClass()
                                    .getCanonicalName() : "null"), e);
                }
            } else {//如果spi方式拿不到,则使用缺省的BasicConfigurator(里面只配置了一个控制台)设置上下文,进行配置
                BasicConfigurator basicConfigurator = new BasicConfigurator();
                basicConfigurator.setContext(loggerContext);
                basicConfigurator.configure(loggerContext);
            }
        }
    }
  1. 找到默认的配置文件或者URL,依次按照系统属性logback.configurationFile查找,按照logback-test.xml,logback.xml得到配置文件。在这里插入代码片
  2. 如果找到了,configureByResource(url)进行配置
  3. 按照spi的方式找到Configurator的实现类,设置上下文,进行配置
  4. 如果spi方式拿不到,则使用缺省的BasicConfigurator(里面只配置了一个控制台)设置上下文,进行配置
final public static String AUTOCONFIG_FILE = "logback.xml";
final public static String TEST_AUTOCONFIG_FILE = "logback-test.xml";
final public static String CONFIG_FILE_PROPERTY = "logback.configurationFile";

至此,初始化成功,将初始化状态标记为初始化成功,打印日志

versionSanityCheck

得到StaticLoggerBinder的版本,并进行判断是否合适。
LoggerFactory放了允许使用的StaticLoggerBinder的版本,如果不合适,会答应出警告

private final static void versionSanityCheck() {
        try {
            String requested = StaticLoggerBinder.REQUESTED_API_VERSION;

            boolean match = false;
            for (String aAPI_COMPATIBILITY_LIST : API_COMPATIBILITY_LIST) {
                if (requested.startsWith(aAPI_COMPATIBILITY_LIST)) {
                    match = true;
                }
            }
            if (!match) {
                Util.report("The requested version " + requested + " by your slf4j binding is not compatible with "
                                + Arrays.asList(API_COMPATIBILITY_LIST).toString());
                Util.report("See " + VERSION_MISMATCH + " for further details.");
            }
        } catch (java.lang.NoSuchFieldError nsfe) {
            // given our large user base and SLF4J's commitment to backward
            // compatibility, we cannot cry here. Only for implementations
            // which willingly declare a REQUESTED_API_VERSION field do we
            // emit compatibility warnings.
        } catch (Throwable e) {
            // we should never reach here
            Util.report("Unexpected problem occured during version sanity check", e);
        }
    }

getLoggerFactory()

performInitialization方法执行完毕,继续执行StaticLoggerBinder.getSingleton().getLoggerFactory()

public ILoggerFactory getLoggerFactory() {
        if (!initialized) {
            return defaultLoggerContext;
        }

        if (contextSelectorBinder.getContextSelector() == null) {
            throw new IllegalStateException("contextSelector cannot be null. See also " + NULL_CS_URL);
        }
        return contextSelectorBinder.getContextSelector().getLoggerContext();
    }

如果没有初始化过,则返回private LoggerContext defaultLoggerContext = new LoggerContext();
,初始化我们之前刚讲过如下
在这里插入图片描述

最后返回loggerContext,其继承了ILoggerFactory,至此得到ILoggerFactory,接下来是获取logger。

public final Logger getLogger(final String name) {

        if (name == null) {
            throw new IllegalArgumentException("name argument cannot be null");
        }

        // if we are asking for the root logger, then let us return it without
        // wasting time
        if (Logger.ROOT_LOGGER_NAME.equalsIgnoreCase(name)) {
            return root;
        }

        int i = 0;
        Logger logger = root;

        // check if the desired logger exists, if it does, return it
        // without further ado.
        Logger childLogger = (Logger) loggerCache.get(name);
        // if we have the child, then let us return it without wasting time
        if (childLogger != null) {
            return childLogger;
        }

        // if the desired logger does not exist, them create all the loggers
        // in between as well (if they don't already exist)
        String childName;
        while (true) {
            int h = LoggerNameUtil.getSeparatorIndexOf(name, i);
            if (h == -1) {
                childName = name;
            } else {
                childName = name.substring(0, h);
            }
            // move i left of the last point
            i = h + 1;
            synchronized (logger) {
                childLogger = logger.getChildByName(childName);
                if (childLogger == null) {
                    childLogger = logger.createChildByName(childName);
                    loggerCache.put(childName, childLogger);
                    incSize();
                }
            }
            logger = childLogger;
            if (h == -1) {
                return childLogger;
            }
        }
    }
  1. 如果name是root不分大小写,直接返回根logger
  2. 如果logger已经创建过,直接从缓存获取并返回
  3. 如果没有创建过,则创建logger,并维护父子关系,放入缓存

代码很简单,主要看一下createChildByName方法

Logger createChildByName(final String childName) {
        int i_index = LoggerNameUtil.getSeparatorIndexOf(childName, this.name.length() + 1);
        if (i_index != -1) {
            throw new IllegalArgumentException("For logger [" + this.name + "] child name [" + childName
                            + " passed as parameter, may not include '.' after index" + (this.name.length() + 1));
        }

        if (childrenList == null) {
            childrenList = new CopyOnWriteArrayList<Logger>();
        }
        Logger childLogger;
        childLogger = new Logger(childName, this, this.loggerContext);
        childrenList.add(childLogger);
        childLogger.effectiveLevelInt = this.effectiveLevelInt;
        return childLogger;
    }

写的也很明白,创建新的子logger并设置日志生效级别

举个例子:当我们把@slf4f放在一个com.ethan.test.demo的类上,那么上述代码会同时生成的logger为:
com,com.ethan,com.ethan.test,com.ethan.test.demo

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值