为什么在我们spring boot项目中已经有了logback.xml文件的情况下,nacos client.jar包中自带的nacos-logback.xml(nacos-client 1.4.2版本验证)依然会生效?
问题的关键在于类com.alibaba.nacos.client.logging.logback.LogbackNacosLogging
public class LogbackNacosLogging extends AbstractNacosLogging {
private static final String NACOS_LOGBACK_LOCATION = "classpath:nacos-logback.xml";
@Override
public void loadConfiguration() {
String location = getLocation(NACOS_LOGBACK_LOCATION);
if (StringUtils.isBlank(location)) {
return;
}
try {
// 这是加载自定义配置文件的关键
// 第一步获取一个LoggerContext
// 第二步使用LoggerContext加载location位置的配置文件
LoggerContext loggerContext = (LoggerContext) StaticLoggerBinder.getSingleton().getLoggerFactory();
new ContextInitializer(loggerContext).configureByResource(ResourceUtils.getResourceUrl(location));
} catch (Exception e) {
throw new IllegalStateException("Could not initialize Logback Nacos logging from " + location, e);
}
}
}
new ContextInitializer(loggerContext).configureByResource(ResourceUtils.getResourceUrl(location));
这行代码就是使用loggerContext加载location位置的配置文件
那么location指向哪里呢?
看这行代码
String location = getLocation(NACOS_LOGBACK_LOCATION);
private static final String NACOS_LOGGING_CONFIG_PROPERTY = "nacos.logging.config";
private static final String NACOS_LOGGING_DEFAULT_CONFIG_ENABLED_PROPERTY = "nacos.logging.default.config.enabled";
protected String getLocation(String defaultLocation) {
// 从system property中获取location的值
String location = System.getProperty(NACOS_LOGGING_CONFIG_PROPERTY);
// 如果没有从system property中获取到值,则直接使用默认配置
if (StringUtils.isBlank(location)) {
if (isDefaultConfigEnabled()) {
return defaultLocation;
}
return null;
}
return location;
}
首先从system property中获取值,如果没有获取到再使用defaultLocation,而defaultLocation的值为classpath:nacos-logback.xml
这就解释了为什么nacos-logback.xml配置文件会被解析了。
这时候好奇的同学又会问了,那这段代码凭什么会执行?
直接执行代码是在nacos client中的类
com.alibaba.nacos.client.utils.LogUtils ,这段在LogUtils类的静态代码块中,只要LogUtils被jvm加载,这段代码就会执行
static {
NacosLogging.getInstance().loadConfiguration();
NAMING_LOGGER = getLogger("com.alibaba.nacos.client.naming");
}
com.alibaba.nacos.client.config.NacosConfigService使用了LogUtils,NacosConfigService被加载LogUtils就会被加载
private static final Logger LOGGER = LogUtils.logger(NacosConfigService.class);
com.alibaba.nacos.api.config.ConfigFactory使用了Class.forName加载了NacosConfigService
public static ConfigService createConfigService(Properties properties) throws
NacosException {
try {
// 使用Class.forName加载
Class<?> driverImplClass = Class.forName("com.alibaba.nacos.client.config.NacosConfigService");
Constructor constructor = driverImplClass.getConstructor(Properties.class);
ConfigService vendorImpl = (ConfigService) constructor.newInstance(properties);
return vendorImpl;
} catch (Throwable e) {
throw new NacosException(NacosException.CLIENT_INVALID_PARAM, e);
}
}
com.alibaba.nacos.api.NacosFactory调用了ConfigFactory.createConfigService()方法
public static ConfigService createConfigService(Properties properties) throws
NacosException {
return ConfigFactory.createConfigService(properties);
}
com.alibaba.cloud.nacos.NacosConfigManager类调用了NacosFactory的createConfigService
public NacosConfigManager(NacosConfigProperties nacosConfigProperties) {
this.nacosConfigProperties = nacosConfigProperties;
// Compatible with older code in NacosConfigProperties,It will be deleted in the
// future.
createConfigService(nacosConfigProperties);
}
com.alibaba.cloud.nacos.NacosConfigBootstrapConfiguration中生成了NacosConfigManager
@Bean
@ConditionalOnMissingBean
public NacosConfigManager nacosConfigManager(
NacosConfigProperties nacosConfigProperties) {
return new NacosConfigManager(nacosConfigProperties);
}
而NacosConfigBootstrapConfiguration类则是,spring-cloud-starter-alibaba-nacos-config这个依赖通过spring的spi引入的
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
com.alibaba.cloud.nacos.NacosConfigBootstrapConfiguration
通过上面的一系列分析过程就能清楚了解nacos-logback.xml这个日志配置文件为什么会生效。其实这个加载过程跟My SQL驱动的加载过程有点像,区别是My SQL驱动是通过java的spi来加载,而前述的加载过程是通过spring的spi来完成的。