从一个Logger获取说起

public static Logger getLogger(Class<?> clazz) {
    //获取Logger
    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;
}

在跟进Logger logger = getLogger(clazz.getName());方法

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

这个方法就是初始化LoggerContext,如果没有初始化则调用performInitialization进行初始化,初始化了就直接返回

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:
        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");
}

再看看performInitialization()方法,里面就一个绑定和校验

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

比较核心的方法bind():

private final static void bind() {
       try {
           Set<URL> staticLoggerBinderPathSet = null;
           //这里通过findPossibleStaticLoggerBinderPathSet查找类路径下的org/slf4j/impl/StaticLoggerBinder.class
           //如果有多个或者没有则记录错误信息
           if (!isAndroid()) {
               staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
               reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);
           }
           //这个是直接引入StaticLoggerBinder方式进行加注,这个初始化的时候会在static代码块中执行init方式加载配置
           //如果Jar包中没有这个类则会报错NoClassDefFoundError,多个的话Jvm优先选择先被加载的那个实现。
           StaticLoggerBinder.getSingleton();
           //初始化成功
           INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
           //显示真实选择的实现类
           reportActualBinding(staticLoggerBinderPathSet);
           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);
       }
   }

StaticLoggerBinder.getSingleton();看看具体做了些什么

//静态代码块初始化
static {
        SINGLETON.init();
}

void init() {
      try {
          try {
              //执行真正的配置自动选择与加载
              new ContextInitializer(defaultLoggerContext).autoConfig();
          } catch (JoranException je) {
              Util.report("Failed to auto configure default logger context", je);
          }
          //这里判断如果没有监听状态信息的话就 查看状态列表有没有warn以上的日志,有就打印到控制台
          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);
      }
}

看看 new ContextInitializer(defaultLoggerContext).autoConfig();到底是怎么做的

public void autoConfig() throws JoranException {
    StatusListenerConfigHelper.installIfAsked(loggerContext);
    //找到具体的配置文件 logback-test.xml>logback.groovy>logback.xml
    URL url = findURLOfDefaultConfigurationFile(true);
    if (url != null) {
        //执行装载配置
        configureByResource(url);
    } else {
        //如果没有上面的xml、groovy配置到执行则查询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 {
            //确实没有用户自定义配置,使用默认配置
            BasicConfigurator basicConfigurator = new BasicConfigurator();
            basicConfigurator.setContext(loggerContext);
            basicConfigurator.configure(loggerContext);
        }
    }
}

配置完成之后回到init方法,回去执行contextSelectorBinder.init(defaultLoggerContext, KEY);

这里面主要是解决日志分离问题,通过VM参数指定logback.ContextSelector来配置ContextSelector实现日志分离

public void init(LoggerContext defaultLoggerContext, Object key) throws ClassNotFoundException, NoSuchMethodException, InstantiationException,
                IllegalAccessException, InvocationTargetException {
    if (this.key == null) {
        this.key = key;
    } else if (this.key != key) {
        throw new IllegalAccessException("Only certain classes can access this method.");
    }

    String contextSelectorStr = OptionHelper.getSystemProperty(ClassicConstants.LOGBACK_CONTEXT_SELECTOR);
    if (contextSelectorStr == null) {
        contextSelector = new DefaultContextSelector(defaultLoggerContext);
    } else if (contextSelectorStr.equals("JNDI")) {
        // if jndi is specified, let's use the appropriate class
        contextSelector = new ContextJNDISelector(defaultLoggerContext);
    } else {
        contextSelector = dynamicalContextSelector(defaultLoggerContext, contextSelectorStr);
    }
}

到这里就初始化完成了。状态也变为了SUCCESSFUL_INITIALIZATION,performInitialization方法执行完成。返回了StaticLoggerBinder.getSingleton().getLoggerFactory();

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:
            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");
}

返回了指定的logback自定义ILoggerFactory的实现类LoggerContext

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();
}

在执行了LoggerContext.getLogger()方法,可以看到他在创建Logger的时候会根据.分割的,从左到右依次查找,如果不存在就创建并保存,存在直接返回

@Override
public final Logger getLogger(final String name) {
    if (name == null) {
        throw new IllegalArgumentException("name argument cannot be null");
    }
    //名字为ROOT直接返回 根LOGGER
    if (Logger.ROOT_LOGGER_NAME.equalsIgnoreCase(name)) {
        return root;
    }
    int i = 0;
    Logger logger = root;
    //查询已经创建了的Logger
    Logger childLogger = (Logger) loggerCache.get(name);
    // 存在直接返回
    if (childLogger != null) {
        return childLogger;
    }

    //不存在则按照规则创建,配置日志等级,继承祖先配置
    String childName;
    while (true) {
        //通过 . 分割Logger等级 划分父子节点
        int h = LoggerNameUtil.getSeparatorIndexOf(name, i);
        if (h == -1) {
            childName = name;
        } else {
            childName = name.substring(0, h);
        }
        // 记录当前的分割位置(祖先节点到哪里了)
        i = h + 1;
        synchronized (logger) {
            //这里是遍历子节点,拿到Logger,只找一级
            childLogger = logger.getChildByName(childName);
            //没有子节点了,创建Logger
            if (childLogger == null) {
                childLogger = logger.createChildByName(childName);
                //添加缓存
                loggerCache.put(childName, childLogger);
                incSize();
            }
        }
        //返回logger
        logger = childLogger;
        if (h == -1) {
            return childLogger;
        }
    }
}

到这里就拿到了Logger进行了打印日志了,但是注意日志记录加载过程中,会经过turbofilter进行初步过滤,再经过RegularFilter进行过滤Appender,如果Appender都通过了再通过layout格式化日志,在经过Encoder把日志写入输出流显示。

评论

博客
分类
标签
归档
关于