使用 Nacos 作为注册中心日志不打印

1 背景

项目使用 log4j2 作为日志框架, 配置一切正常, 程序启动日志的打印也没有问题, 但是引入了 nacos 作为注册中心后, 日志在打印了一句 WARN No Root logger was configured, creating default ERROR-level Root logger with Console appender 后日志都不打印了。

引入的依赖

<dependency>
    <groupId>org.springframework.cloudgroupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
    <version>0.9.0.RELEASEversion>
dependency>

2 原因

在项目的启动过程中, nacos-discovery 会尝试去读系统内的日志配置文件, 然后刷新配置, 但是如果找不到的话, 会使用自身内部的默认配置, 但是默认的日志配置没有设置日志的 Root 的信息。

3 原因分析

  1. 在 com.alibaba.nacos.client.utils.LogUtils 中有一段逻辑如下:
AbstractNacosLogging nacosLogging;
try {
    Class.forName("ch.qos.logback.classic.Logger");
    nacosLogging = new LogbackNacosLogging();
    isLogback = true;
} catch (ClassNotFoundException e) {
    nacosLogging = new Log4J2NacosLogging();
}
nacosLogging.loadConfiguration();

// 从代码可以知道LogUtils, 会尝试加载 `ch.qos.logback.classic.Logger` 这个类, 加载成功的话, 说明项目使用的是 logback 日志框架, 加载不到的话, 就是使用 log4j2(我的项目中使用的是 log4j2) 
  1. 进入到 Log4J2NacosLogging 的构造函数
public class Log4J2NacosLogging extends AbstractNacosLogging {

	public Log4J2NacosLogging() {
		// NACOS_LOG4J2_LOCATION ===> "classpath:nacos-log4j2.xml"
        String location = getLocation(NACOS_LOG4J2_LOCATION);
        // location 有值, 将值放到 locationList, 用于后续的日志加载
        if (!StringUtils.isBlank(location)) {
            locationList.add(location);
        }
    }

    // 父类的方法, 为了方便, 写在这里
    protected String getLocation(String defaultLocation) {

    	// 获取系统属性 NACOS_LOGGING_CONFIG_PROPERTY 的值, 值不为空 返回 NACOS_LOGGING_CONFIG_PROPERTY 的值
        String location = System.getProperty(NACOS_LOGGING_CONFIG_PROPERTY);
        if (StringUtils.isBlank(location)) {
        	// NACOS_LOGGING_CONFIG_PROPERTY 的值为空, 根据是否需要返回默认值, 来返回 null 或者  defaultLocation
            if (isDefaultConfigEnabled()) {
            	// 返回 defaultLocation 也就是 classpath:nacos-log4j2.xml, 这个配置文件在 nacos-client.jar 这个 jar 包里面可以找到
                return defaultLocation;
            }
            return null;
        }
        return location;
    }

    private boolean isDefaultConfigEnabled() {
    	// 判断是否设置了系统属性 NACOS_LOGGING_DEFAULT_CONFIG_ENABLED_PROPERTY, 如果设置了, 同时值为 false, 则返回false, 否则为 true
        String property = System.getProperty(NACOS_LOGGING_DEFAULT_CONFIG_ENABLED_PROPERTY);
        // The default value is true.
        return property == null || BooleanUtils.toBoolean(property);
    }
}
  1. 进入到 Log4J2NacosLoggingloadConfiguration 方法
public class Log4J2NacosLogging extends AbstractNacosLogging {

	public void loadConfiguration() {
		// 构造函数中处理的 locationList, 如果为空, 直接返回了
        if (locationList.isEmpty()) {
            return;
        }

        // 获取到当前系统能支持的配置文件的路径
        List<String> configList = findConfig(getCurrentlySupportedConfigLocations());
        if (configList != null) {
            locationList.addAll(configList);
        }

		final List<AbstractConfiguration> configurations = new ArrayList<AbstractConfiguration>();
		LoggerContext loggerContext = (LoggerContext)LogManager.getContext(false);
		for (String location : locationList) {
			try {
				// 把配置文件转为配置
                Configuration configuration = loadConfiguration(loggerContext, location);
                if (configuration instanceof AbstractConfiguration) {
                    configurations.add((AbstractConfiguration)configuration);
                }
            } catch (Exception e) {
                throw new IllegalStateException("Could not initialize Log4J2 Nacos logging from " + location, e);
            }
		}
		CompositeConfiguration compositeConfiguration = new CompositeConfiguration(configurations);
		// 重新加载日志配置
        loggerContext.start(compositeConfiguration);
    }


    private String[] getCurrentlySupportedConfigLocations() {

        List<String> supportedConfigLocations = new ArrayList<String>();

        // YAML_PARSER_CLASS_NAME ===> com.fasterxml.jackson.dataformat.yaml.YAMLParser
        if (ClassUtils.isPresent(YAML_PARSER_CLASS_NAME)) {
            Collections.addAll(supportedConfigLocations, "log4j2.yaml", "log4j2.yml", "log4j2-test.yaml", "log4j2-test.yml");
        }

        // JSON_PARSER_CLASS_NAME ===> com.fasterxml.jackson.databind.ObjectMapper
        if (ClassUtils.isPresent(JSON_PARSER_CLASS_NAME)) {
            Collections.addAll(supportedConfigLocations, "log4j2.json", "log4j2.jsn", "log4j2-test.json", "log4j2-test.jsn");
        }
        supportedConfigLocations.add("log4j2.xml");
        supportedConfigLocations.add("log4j2-test.xml");
        // 把当前系统支持的配置文件的格式, 以数组的格式返回回去
        return supportedConfigLocations.toArray(new String[supportedConfigLocations.size()]);
    }


    private List<String> findConfig(String[] locations) {
    	// 把上面 getCurrentlySupportedConfigLocations 获取到的 配置文件格式拼接成程序能找到的路径
        final String configLocationStr = this.strSubstitutor.replace(PropertiesUtil.getProperties().getStringProperty(CONFIGURATION_FILE_PROPERTY));
        if (configLocationStr != null) {
            return Arrays.asList(configLocationStr.split(", "));
        }
        for (String location : locations) {
            ClassLoader defaultClassLoader = ClassUtils.getDefaultClassLoader();
            if (defaultClassLoader != null && defaultClassLoader.getResource(location) != null) {
                List<String> list = new ArrayList<String>();
                list.add("classpath:" + location);
                return list;
            }
        }
        return null;
    }
}

从上面看下来, 报错的原因是, nacos 会重新加载日志的配置, 但是需要的配置文件都没有, 后续的默认的配置文件 classpath:nacos-log4j2.xml 也没有设置日志的 Root, 所以报错了。

4 解决

  1. isDefaultConfigEnabled 方法提示了, 只需要在项目启动的时候设置一下 System.setProperty("nacos.logging.default.config.enabled", "false"); 就行了

  2. getCurrentlySupportedConfigLocations 方法提示了, 只需要把你的日志配置文件的修改为里面的其中的一种格式就行了 (因为基于 springboot 搭建的, 所以我的日志命名为了 log4j2-spring.xml 所以读取不到)

你可能感兴趣的:(开发问题记录,SpringBoot,spring,boot)