先来看springboot中apollo的配置:

apollo配置中心使用 python apollo配置不生效_apollo配置中心使用 python

 

 不仔细看或者对apollo配置不熟的人来说是很难看出配置错在哪的,正确配置应该是如下:

apollo配置中心使用 python apollo配置不生效_spring_02

 

 eagerLoad配置错,会导致springboot日志框架初始化时apollo的配置时还未加载成功,导致apollo配置的日志DEBUG级别没起效果。

虽然是团队哪位大佬的一不小心的疏忽,但是还是花了不少时间找问题。其他项目同样配置可以DEBUG级别有效,可以确认apollo的日志配置是正确的,应该是配置加载出了问题。

下面我们就来分析下springboot的日志初始化过程:

首先找到springboot的日志初始化入口:

我们在spring-boot包中找到LoggingApplicationListener这个类,这个是spring的事件监听器,主要代码如下:

@Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ApplicationStartingEvent) {
            onApplicationStartingEvent((ApplicationStartingEvent) event);
        }
        else if (event instanceof ApplicationEnvironmentPreparedEvent) {
            onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
        }
        else if (event instanceof ApplicationPreparedEvent) {
            onApplicationPreparedEvent((ApplicationPreparedEvent) event);
        }
        else if (event instanceof ContextClosedEvent
                && ((ContextClosedEvent) event).getApplicationContext().getParent() == null) {
            onContextClosedEvent();
        }
        else if (event instanceof ApplicationFailedEvent) {
            onApplicationFailedEvent();
        }
    }

 

日志初始化主要是onApplicationEnvironmentPreparedEvent事件,也就是环境准备好事件,代表着所以配置已经加载,比如放apollo的配置。

private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
        if (this.loggingSystem == null) {
            this.loggingSystem = LoggingSystem.get(event.getSpringApplication().getClassLoader());
        }
        initialize(event.getEnvironment(), event.getSpringApplication().getClassLoader());
    }

 

然后看initialize方法:

protected void initialize(ConfigurableEnvironment environment, ClassLoader classLoader) {
        new LoggingSystemProperties(environment).apply();
        this.logFile = LogFile.get(environment);
        if (this.logFile != null) {
            this.logFile.applyToSystemProperties();
        }
//初始化前的日志级别
        initializeEarlyLoggingLevel(environment);
//初始化
        initializeSystem(environment, this.loggingSystem, this.logFile);
//初始化后的日志级别
        initializeFinalLoggingLevels(environment, this.loggingSystem);
        registerShutdownHookIfNecessary(environment, this.loggingSystem);
    }

 

我们主要看下initializeSystem方法,这个方法也主要是调用system.initialize方法,这里我们使用的是logback,所以system是LogbackLoggingSystem。

下面我们看LogbackLoggingSystem的initialize方法:

@Override
    public void initialize(LoggingInitializationContext initializationContext, String configLocation, LogFile logFile) {
        LoggerContext loggerContext = getLoggerContext();
        if (isAlreadyInitialized(loggerContext)) {
            return;
        }
        super.initialize(initializationContext, configLocation, logFile);
        loggerContext.getTurboFilterList().remove(FILTER);
        markAsInitialized(loggerContext);
        if (StringUtils.hasText(System.getProperty(CONFIGURATION_FILE_PROPERTY))) {
            getLogger(LogbackLoggingSystem.class.getName()).warn("Ignoring '" + CONFIGURATION_FILE_PROPERTY
                    + "' system property. " + "Please use 'logging.config' instead.");
        }
    }

 

这个方面里面核心是super.initialize,调用父类的initialize方法,父类是AbstractLoggingSystem,这些代码都是放在spring-boot包的logging下。

apollo配置中心使用 python apollo配置不生效_初始化_03

 

 

然后我们看AbstractLoggingSystem的initialize方法:

@Override
    public void initialize(LoggingInitializationContext initializationContext, String configLocation, LogFile logFile) {
        if (StringUtils.hasLength(configLocation)) {
            initializeWithSpecificConfig(initializationContext, configLocation, logFile);
            return;
        }
        initializeWithConventions(initializationContext, logFile);
    }
configLocation是没设置值的,所以会进入initializeWithConventions方法:
private void initializeWithConventions(LoggingInitializationContext initializationContext, LogFile logFile) {
//获取logback自己支持的配置,搜索logback-test.groovy,logback-test.xml,logback.groovy,logback.xml这些文件
        String config = getSelfInitializationConfig();
        if (config != null && logFile == null) {
            // self initialization has occurred, reinitialize in case of property changes
            reinitialize(initializationContext);
            return;
        }
        if (config == null) {
//获取spring支持的配置,搜索logback-test-spring.groovy,logback-test-spring.xml,logback-spring.groovy,logback-spring.xml这些文件
            config = getSpringInitializationConfig();
        }
        if (config != null) {
//搜索到logback的配置文件,就就在,这里面主要是解析logback的过程,就不分析了。
            loadConfiguration(initializationContext, config, logFile);
            return;
        }
        loadDefaults(initializationContext, logFile);
    }

 

然后我们回到最开始分析的initializeFinalLoggingLevels方法:

private void initializeFinalLoggingLevels(ConfigurableEnvironment environment, LoggingSystem system) {
        if (this.springBootLogging != null) {
            initializeLogLevel(system, this.springBootLogging);
        }
        setLogLevels(system, environment);
    }

 

setLogLevels这个方法是设置日志级别,我们继续往下看:
protected void setLogLevels(LoggingSystem system, Environment environment) {
        if (!(environment instanceof ConfigurableEnvironment)) {
            return;
        }
//获取到所以的配置
        Binder binder = Binder.get(environment);
        Map<String, String[]> groups = getGroups();
        binder.bind(LOGGING_GROUP, STRING_STRINGS_MAP.withExistingValue(groups));
//从配置中获取所以logging.level开头的配置,并转换为map
        Map<String, String> levels = binder.bind(LOGGING_LEVEL, STRING_STRING_MAP).orElseGet(Collections::emptyMap);
        levels.forEach((name, level) -> {
            String[] groupedNames = groups.get(name);
            if (ObjectUtils.isEmpty(groupedNames)) {
//遍历logging.level,设置每个配置的级别
                setLogLevel(system, name, level);
            }
            else {
                setLogLevel(system, groupedNames, level);
            }
        });
    }

 

map结构如下:

"org.springframework.cloud.sleuth" -> "INFO"
"com.xxx" -> "DEBUG"
"org.springframework" -> "INFO"

当初找到DEBUG不起效,就是在这个方法中发现levels根本没值,正常的里面是有如上的map结果,然后就感觉配置没读到,继续查看environment对象,果然apollo的配置是没读到,确定apollo的饥饿加载有问题,最后对比springboot的apollo配置锁定配置出错。