SpringBoot中logback的初始化流程

一. 前言

1. 相关日志文档

2. 说明

  • 本文基于使用logback-spring.xml配置文件进行源码解析
    • Spring项目可以使用的配置文件
      • logback-test.groovy
      • logback-test.xml
      • logback.groovy
      • logback.xml
      • logback-test-spring.groovy
      • logback-test-spring.xml
      • logback-spring.groovy
      • logback-spring.xml (本文基于该配置进行讲述)
  • 本文只讲核心代码, 不是主线的代码不展示出来
  • 本文的spring-boot版本为2.7.11-SNAPSHOT
  • 本文涉及到的logback-classic依赖版本为1.2.12
  • 如有疑问可留意或查看官方文档

3. 代码准备

3.1 添加依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.7.11-SNAPSHOT</version>
        </dependency>

依赖说明:

  1. spring-boot-starter-web会自动引入spring-boot 和 logback所需的依赖
  2. 依赖引用关系见下图 (不使用@slf4j注解的话请忽略lombok依赖)
    在这里插入图片描述

3.2 添加logback-spring.xml配置文件

<?xml version="1.0" encoding="UTF-8"?>
<!-- 配置详情: https://blog.csdn.net/weixin_41377777/article/details/120962037 -->
<!-- 拷贝即可使用: 额外需要关注的就三个点, 1.配置spring.application.name可指定文件前缀名 2.修改dev的springProfile中自定义logger的name指定当前项目 3.修改rollingPolicy的MaxHistory确定日志保留数 -->

<configuration>
	<property name="LOG_PATH" value="/apps/logs" />
	<property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %level [%C{36}.%M:%line] tid=%X{traceId}, %msg%n" />
	<springProperty scope="context" name="APP_NAME" source="spring.application.name" defaultValue="defaultAppName"/>

	<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
		<encoder>
			<pattern>${LOG_PATTERN}</pattern>
		</encoder>
	</appender>

	<!--本地环境 -->
	<springProfile name="dev">
		<logger name="com.chenlongji" level="DEBUG" />
		<!--<logger name="org.mybatis" level="DEBUG" />-->

		<root level="INFO">
			<appender-ref ref="CONSOLE" />
		</root>
	</springProfile>

	<!--测试环境 -->
	<springProfile name="test">
		<!-- 备注: 若springProfile仅激活一个, 那appender的name重复也不影响, 因为没激活的profile里面的appender不会进行初始化 -->
		<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
			<!-- layout使用encoder配置应该也行, 如CONSOLE的配置 -->
			<layout class="ch.qos.logback.classic.PatternLayout">
				<pattern>${LOG_PATTERN}</pattern>
			</layout>
			<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
				<FileNamePattern>${LOG_PATH}/${APP_NAME}.%d{yyyy-MM-dd}.log</FileNamePattern>
				<MaxHistory>1</MaxHistory>
			</rollingPolicy>
		</appender>

		<root level="INFO">
			<appender-ref ref="FILE" />
		</root>
	</springProfile>

	<!--生产环境 -->
	<springProfile name="prd">
		<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
			<layout class="ch.qos.logback.classic.PatternLayout">
				<pattern>${LOG_PATTERN}</pattern>
			</layout>
			<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
				<FileNamePattern>${LOG_PATH}/${APP_NAME}.%d{yyyy-MM-dd}.log</FileNamePattern>
				<MaxHistory>7</MaxHistory>
			</rollingPolicy>
		</appender>

		<root level="INFO">
			<appender-ref ref="FILE" />
		</root>
	</springProfile>

</configuration>

说明:

  1. 日志输出格式中的%X为mdc日志链路追踪使用, 这里不展开讲
  2. 确保没有logback.xml等日志配置文件, 否则logback-spring.xml可能不生效
  3. 若想了解配置详情, 点击查看此文章
  4. 若想了解配置对应的源码信息, 请看logback源码浅析

3.3 添加main测试方法

@SpringBootApplication
public class LogbackStudyApplication {

    public static void main(String[] args) {
		// 初始化入口
        ConfigurableApplicationContext context = SpringApplication.run(LogbackStudyApplication.class, args);

        Logger logger = LoggerFactory.getLogger(Test1.class);
        logger.info("我是info级别的日志");

        context.close();
    }

}

4. 最终初始化使用的配置文件说明

  • logback默认的配置文件
    • logback-test.groovy
    • logback-test.xml
    • logback.groovy
    • logback.xml
  • springboot默认的logback配置文件
    • logback-test-spring.groovy
    • logback-test-spring.xml
    • logback-spring.groovy
    • logback-spring.xml
  • 最终初始化使用的配置文件说明 (可以使用-Dlogging.config={xxx}指定配置文件)
    • logging.config指定配置文件, 则使用该文件完成logger最终初始化
    • logging.config未指定配置文件且有logback默认的配置文件, 则使用logback默认的配置文件完成logger最终初始化
    • logging.config未指定配置文件且无logback任意的配置文件, 则使用DefaultLogbackConfiguration完成logger最终初始化
    • logging.config未指定配置文件且无logback默认的配置文件且有springboot默认的logback配置文件, 则使用springboot默认的logback配置文件完成logger最终初始化

二. 执行原生logback初始化

前置说明:
springboot项目中是先执行原生的logback初始化, 再执行springboot下logback初始化的

入口

@SpringBootApplication
public class LogbackStudyApplication {
    public static void main(String[] args) {
		// 入口
        ConfigurableApplicationContext context = SpringApplication.run(LogbackStudyApplication.class, args);
    }
}

JVM加载SpringApplication类时, 完成其静态属性logger初始化

public class SpringApplication {
	// 这里的LogFactory全限定名为org.apache.commons.logging.LogFactory, 是spring-jcl.jar下的类
	private static final Log logger = LogFactory.getLog(SpringApplication.class);
}

进入LogFactory的getLog(Class)方法

	public static Log getLog(Class<?> clazz) {
		return getLog(clazz.getName());
	}

进入LogFactory的getLog(String)方法

	public static Log getLog(String name) {
		return LogAdapter.createLog(name);
	}

进入LogAdapter的createLog(String)方法

	public static Log createLog(String name) {
		// logApi枚举值来源于LogAdapter初始化是根据 是否可以类加载到某些类来确定值的. 不展开
		// 添加spring-boot-starter-web依赖后, logApi = SLF4J_LAL
		switch (logApi) {
			case LOG4J:
				return Log4jAdapter.createLog(name);
			case SLF4J_LAL:
				// 创建log对象
				return Slf4jAdapter.createLocationAwareLog(name);
			case SLF4J:
				return Slf4jAdapter.createLog(name);
			default:
				return JavaUtilAdapter.createLog(name);
		}
	}

进入Slf4jAdapter的createLocationAwareLog(String)方法

		public static Log createLocationAwareLog(String name) {
			// 此次的LoggerFactory全限定类名为org.slf4j.LoggerFactory, 是slf4j-api下的类
			// 因为spring-boot-starter-web依赖导入了logback-classic包, 故此次会进入logback原生的初始化流程
			Logger logger = LoggerFactory.getLogger(name);
			return (logger instanceof LocationAwareLogger 
					// 属于logback类型的logger, 包装为Slf4jLocationAwareLog返回
					? new Slf4jLocationAwareLog((LocationAwareLogger) logger) 
					: new Slf4jLog<>(logger));
		}

说明:

  1. 项目添加的spring-boot-starter-web依赖导入了logback-classic包, 后续会进入logback原生的初始化流程
  2. 原生logback初始化会使用logback.xml等不带spring字眼的配置文件, 若无则使用默认配置BasicConfigurator
  3. logback原生的初始化流程入口org.slf4j.LoggerFactory.getLogger(name). 请看logback源码浅析

三. 执行SpringBoot下logback的再次初始化

前置说明:

  1. 完成原生logback初始化后进入SpringApplication的run方法
  2. 然后一直执行到LoggingApplicationListener的initialize(ConfigurableEnvironment, ClassLoader)方法, 我们从这里开始看源码了解springboot下logback又如何再次初始化
  3. springboot代码量较多, 后面注释仅为logback相关的标注

进入LoggingApplicationListener的initialize(ConfigurableEnvironment, ClassLoader)方法

	protected void initialize(ConfigurableEnvironment environment, ClassLoader classLoader) {
		getLoggingSystemProperties(environment).apply();
		this.logFile = LogFile.get(environment);
		if (this.logFile != null) {
			this.logFile.applyToSystemProperties();
		}
		this.loggerGroups = new LoggerGroups(DEFAULT_GROUP_LOGGERS);
		initializeEarlyLoggingLevel(environment);
		// 初始化日志系统
		initializeSystem(environment, this.loggingSystem, this.logFile);
		initializeFinalLoggingLevels(environment, this.loggingSystem);
		registerShutdownHookIfNecessary(environment, this.loggingSystem);
	}

进入LoggingApplicationListener的initializeSystem(ConfigurableEnvironment, LoggingSystem, LogFile)方法

	private void initializeSystem(ConfigurableEnvironment environment, LoggingSystem system, LogFile logFile) {
		// 获取环境变量中的logging.config指定的配置文件路径
		// 注: logback中使用logback.configurationFile指定配置文件路径(springboot下该值最终是无效的)
		String logConfig = StringUtils.trimWhitespace(environment.getProperty(CONFIG_PROPERTY));
		try {
			LoggingInitializationContext initializationContext = new LoggingInitializationContext(environment);
			
			if (ignoreLogConfig(logConfig)) {
				// 核心代码: logging.config没有指定配置文件, 则后续再获取符合要求的配置文件
				system.initialize(initializationContext, null, logFile);
			}
			else {
				// 核心代码: logging.config指定了配置文件, 则使用指定的
				system.initialize(initializationContext, logConfig, logFile);
			}
		}
		catch (Exception ex) {
			//...
		}
	}

进入LogbackLoggingSystem的initialize(LoggingInitializationContext, String, LogFile)方法

	public void initialize(LoggingInitializationContext initializationContext, String configLocation, LogFile logFile) {
		LoggerContext loggerContext = getLoggerContext();
		// 判断logger上下文中, 是否已经执行了springboot下的logback初始化. 这里是使用LoggingSystem的全限定类名占位标志的
		if (isAlreadyInitialized(loggerContext)) {
			return;
		}
		// 核心代码: 初始化日志系统
		super.initialize(initializationContext, configLocation, logFile);
		loggerContext.getTurboFilterList().remove(FILTER);
		// 完成springboot下的logback初始化后, 设置logger上下文springboot的logback已初始化标志(LoggingSystem的全限定类名占位)
		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.");
		}
	}

进入AbstractLoggingSystem的initialize(LoggingInitializationContext, String, LogFile)方法

	public void initialize(LoggingInitializationContext initializationContext, String configLocation, LogFile logFile) {
		// logging.config指定了配置文件, 则执行initializeWithSpecificConfig方法. 两个方法都在loadConfiguration(LoggingInitializationContext,String,LogFile)中汇合
		if (StringUtils.hasLength(configLocation)) {
			initializeWithSpecificConfig(initializationContext, configLocation, logFile);
			return;
		}
		// 未指定配置文件, 则执行initializeWithConventions方法. 两个方法都在loadConfiguration(LoggingInitializationContext,String,LogFile)中汇合
		initializeWithConventions(initializationContext, logFile);
	}

说明:

  1. initializeWithConventions方法读取默认的配置文件后, 会和initializeWithSpecificConfig在loadConfiguration(LoggingInitializationContext,String,LogFile)中汇合
  2. 这里我们加上没有指定配置文件, 我们继续从initializeWithConventions方法看源码

进入AbstractLoggingSystem的initializeWithConventions(LoggingInitializationContext, LogFile)方法

	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) {
			// 使用原有的logback默认配置文件再次初始化(使用同一个配置文件再次执行了原生的logback初始化一次). 发生属性更改的属性会被重新初始化. 这里不展开
			reinitialize(initializationContext);
			return;
		}
		if (config == null) {
			// 获取springboot特有的logback配置文件
			config = getSpringInitializationConfig();
		}
		if (config != null) {
			// 核心代码: 再次执行初始化
			loadConfiguration(initializationContext, config, logFile);
			return;
		}
		// 整个系统都没找到一个适合的配置文件, 则使用springboot默认的DefaultLogbackConfiguration配置类完成初始化. 不展开
		loadDefaults(initializationContext, logFile);
	}

说明: springboot初始logback过程汇总
logback默认的配置文件[logback-test.groovy, logback-test.xml, logback.groovy, logback.xml]
springboot默认的logback配置文件[logback-test-spring.groovy, logback-test-spring.xml, logback-spring.groovy, logback-spring.xml]
初始化过程汇总

  1. 系统无logback相关的任意配置文件. 则先使用logback默认配置类BasicConfigurator初始化一次, 再使用springboot的默认配置类DefaultLogbackConfiguration初始化一次
  2. 系统指定了配置文件但无logback默认配置文件. 则先使用logback默认配置类BasicConfigurator初始化一次, 再使用指定的配置文件初始化一次
  3. 系统指定了配置文件但有logback默认配置文件. 则先使用logback默认配置文件初始化一次, 再使用指定的配置文件初始化一次
  4. 系统未指定配置文件且存在logback默认配置文件. 则使用logback默认配置文件初始化两次
  5. 系统未指定配置文件且不存在logback默认配置文件且存在springboot的logback配置文件. 则先使用logback默认配置类BasicConfigurator初始化一次, 再使用springboot的logback配置文件初始化一次

最终汇总

  1. logging.config指定配置文件, 则使用该文件完成logger最终初始化
  2. logging.config未指定配置文件且有logback默认的配置文件, 则使用logback默认的配置文件完成logger最终初始化
  3. logging.config未指定配置文件且无logback任意的配置文件, 则使用DefaultLogbackConfiguration完成logger最终初始化
  4. logging.config未指定配置文件且无logback默认的配置文件且有springboot默认的logback配置文件, 则使用springboot默认的logback配置文件完成logger最终初始化

进入AbstractLoggingSystem的getSpringInitializationConfig()方法

	protected String getSpringInitializationConfig() {
		// getSpringConfigLocations()找出springboot默认的logback配置文件列表
		// findConfig()找到系统中存在第一个 springboot默认的logback配置文件列表内 的配置文件. 该方法不展开
		return findConfig(getSpringConfigLocations());
	}

进入AbstractLoggingSystem的getSpringConfigLocations()方法

	protected String[] getSpringConfigLocations() {
		// 获取当前日志系统默认的配置文件. 当前为logback, 默认的配置文件有logback-test.groovy, logback-test.xml, logback.groovy, logback.xml
		String[] locations = getStandardConfigLocations();
		// 遍历拼接上"-spring.", springboot默认的logback配置文件列表如下
		// [logback-test-spring.groovy, logback-test-spring.xml, logback-spring.groovy, logback-spring.xml]
		for (int i = 0; i < locations.length; i++) {
			String extension = StringUtils.getFilenameExtension(locations[i]);
			locations[i] = locations[i].substring(0, locations[i].length() - extension.length() - 1) + "-spring."
					+ extension;
		}
		return locations;
	}

进入LogbackLoggingSystem的loadConfiguration(LoggingInitializationContext, String,LogFile)方法

	protected void loadConfiguration(LoggingInitializationContext initializationContext, String location,
			LogFile logFile) {
		super.loadConfiguration(initializationContext, location, logFile);
		LoggerContext loggerContext = getLoggerContext();
		stopAndReset(loggerContext);
		try {
			// 通过配置文件重新初始化
			configureByResourceUrl(initializationContext, loggerContext, ResourceUtils.getURL(location));
		}
		catch (Exception ex) {
			throw new IllegalStateException("Could not initialize Logback logging from " + location, ex);
		}
		// ...
	}

进入LogbackLoggingSystem的configureByResourceUrl(LoggingInitializationContext, LoggerContext,URL)方法

	private void configureByResourceUrl(LoggingInitializationContext initializationContext, LoggerContext loggerContext,
			URL url) throws JoranException {
		if (XML_ENABLED && url.toString().endsWith("xml")) {
			// 使用SpringBootJoranConfigurator完成初始化
			JoranConfigurator configurator = new SpringBootJoranConfigurator(initializationContext);
			configurator.setContext(loggerContext);
			configurator.doConfigure(url);
		}
		else {
			new ContextInitializer(loggerContext).configureByResource(url);
		}
	}

说明:

  1. 后面执行的就是SpringBootJoranConfigurator.doConfigure()方法, 与logback初始化流程几乎一致了.
  2. logback初始化流程请看logback源码浅析
  3. 下面的[SpringBootJoranConfigurator与JoranConfigurator初始化的差异] 继续将springboot中的一些和logback初始化不同的东西

四. SpringBootJoranConfigurator与JoranConfigurator初始化的差异

1. 继承关系

在这里插入图片描述

说明:
SpringBootJoranConfigurator为JoranConfigurator的子类

2. 了解SpringBootJoranConfigurator类

class SpringBootJoranConfigurator extends JoranConfigurator {
	// springboot的logger初始化上下文对象, 存储了environment环境信息
	private LoggingInitializationContext initializationContext;

	SpringBootJoranConfigurator(LoggingInitializationContext initializationContext) {
		this.initializationContext = initializationContext;
	}

	/**
	 * 添加实例规则
	 */
	@Override
	public void addInstanceRules(RuleStore rs) {
		super.addInstanceRules(rs);
		Environment environment = this.initializationContext.getEnvironment();
		// 添加解析<configuration>下<springProperty>的Action
		rs.addRule(new ElementSelector("configuration/springProperty"), new SpringPropertyAction(environment));
		// 添加解析* <springProfile>的Action
		rs.addRule(new ElementSelector("*/springProfile"), new SpringProfileAction(environment));
		// 添加* <springProfile>下的Action. 该Action为无操作Action, 由SpringProfileAction去处理<springProfile>下的标签内容
		rs.addRule(new ElementSelector("*/springProfile/*"), new NOPAction());
	}
}

3. SpringBoot特有的SpringPropertyAction

class SpringPropertyAction extends Action {
	private static final String SOURCE_ATTRIBUTE = "source";
	private static final String DEFAULT_VALUE_ATTRIBUTE = "defaultValue";
	private final Environment environment;
	SpringPropertyAction(Environment environment) {
		this.environment = environment;
	}

	@Override
	public void begin(InterpretationContext context, String elementName, Attributes attributes) throws ActionException {
		String name = attributes.getValue(NAME_ATTRIBUTE);
		String source = attributes.getValue(SOURCE_ATTRIBUTE);
		Scope scope = ActionUtil.stringToScope(attributes.getValue(SCOPE_ATTRIBUTE));
		String defaultValue = attributes.getValue(DEFAULT_VALUE_ATTRIBUTE);
		if (OptionHelper.isEmpty(name) || OptionHelper.isEmpty(source)) {
			addError("The \"name\" and \"source\" attributes of <springProperty> must be set");
		}
		// 读取<springProperty>的属性值, 缓存到指定的作用域中待用. 不展开
		ActionUtil.setProperty(context, name, getValue(source, defaultValue), scope);
	}

	private String getValue(String source, String defaultValue) {
		// 获取环境中的属性值
		if (this.environment == null) {
			addWarn("No Spring Environment available to resolve " + source);
			return defaultValue;
		}
		return this.environment.getProperty(source, defaultValue);
	}

	@Override
	public void end(InterpretationContext context, String name) throws ActionException {
	}
}

4. SpringBoot特有的SpringProfileAction

class SpringProfileAction1 extends Action implements InPlayListener {
	// 环境信息
	private final Environment environment;
	// 深度. 用于拦截嵌套的<springProfile>错误标签
	private int depth = 0;
	// 记录当前的<springProfile>是否满足运行的环境(即springProfile的name是否包含 项目的spring.profiles.active)
	private boolean acceptsProfile;
	// 事件暂存区. 寄存<springProfile>及内嵌标签事件的 缓存事件列表
	private List<SaxEvent> events;

	SpringProfileAction1(Environment environment) {
		this.environment = environment;
	}

	@Override
	public void begin(InterpretationContext ic, String name, Attributes attributes) throws ActionException {
		// 深度加一, 标志开始解析<springProfile>
		this.depth++;
		// 只能处理外层的<springProfile>, 嵌套直接结束(示例: <springProfile><springProfile>...<springProfile/><springProfile/>)
		if (this.depth != 1) {
			return;
		}
		// 当前action入栈, 待后续使用
		ic.pushObject(this);
		// 记录 当前的<springProfile>是否满足运行的环境情况
		this.acceptsProfile = acceptsProfiles(ic, attributes);
		this.events = new ArrayList<>();
		// 将当前的SpringProfileAction加入InterpretationContext的监听器列表中listenerList. 该监听器用于添加寄存的内嵌标签事件. 不展开
		ic.addInPlayListener(this);
	}

	private boolean acceptsProfiles(InterpretationContext ic, Attributes attributes) {
		if (this.environment == null) {
			return false;
		}
		// 获取<springProfile>属性name, 按","分隔. 不展开
		String[] profileNames = StringUtils
			.trimArrayElements(StringUtils.commaDelimitedListToStringArray(attributes.getValue(NAME_ATTRIBUTE)));
		if (profileNames.length == 0) {
			return false;
		}
		// 获取profileNames的真实值. 不展开
		for (int i = 0; i < profileNames.length; i++) {
			profileNames[i] = OptionHelper.substVars(profileNames[i], ic, this.context);
		}
		// 判断当前的<springProfile>是否满足运行的环境. 不展开
		return this.environment.acceptsProfiles(Profiles.of(profileNames));
	}

	@Override
	public void end(InterpretationContext ic, String name) throws ActionException {
		// 深度减一, 标志结束解析<springProfile>
		this.depth--;
		// 嵌套<springProfile>标签, 直接结束
		if (this.depth != 0) {
			return;
		}
		// 移除掉SpringProfileAction这个监听器
		ic.removeInPlayListener(this);
		// 校验当前action是否为栈顶对象, 是则弹出
		verifyAndPop(ic);
		// 重要代码: 当前<springProfile>有效, 将<springProfile>的内嵌标签事件 丢回正在处理eventList列表中. 插入的位置为下一个执行事件前面
		if (this.acceptsProfile) {
			addEventsToPlayer(ic);
		}
	}

	private void verifyAndPop(InterpretationContext ic) {
		// 校验当前action是否为栈顶对象, 是则弹出. 不展开
		Object o = ic.peekObject();
		Assert.state(o != null, "Unexpected null object on stack");
		Assert.isInstanceOf(SpringProfileAction1.class, o, "logback stack error");
		Assert.state(o == this, "ProfileAction different than current one on stack");
		ic.popObject();
	}

	private void addEventsToPlayer(InterpretationContext ic) {
		// 移除<springProfile>和<springProfile/>事件
		Interpreter interpreter = ic.getJoranInterpreter();
		this.events.remove(0);
		this.events.remove(this.events.size() - 1);
		// 将<springProfile>内嵌事件丢回正在处理eventList列表中. 插入的位置为下一个执行事件前面
		interpreter.getEventPlayer().addEventsDynamically(this.events, 1);
	}

	@Override
	public void inPlay(SaxEvent event) {
		// 使用SpringProfileAction该监听器添加事件到事件寄存器events中. 
		// 调用处: 在EventPlayer的play方法中调用, 将当前的事件添加到各个监听器中去. 不展开
		this.events.add(event);
	}
}

说明:

  1. 正常配置下, 最多只有一个springProfile标签有效(配置的springProfile的name包含了spring.profiles.active)
  2. springProfile内的标签事件不是立即解析的, 是待springProfile标签结束后再处理内嵌的标签事件(springProfile有效的情况下)
  3. 假设当前spring.profiles.active=dev, 则解析标签顺序示例如下图
    在这里插入图片描述
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
你好!对于logback.xml配置在Spring Boot不生效的问题,有几个可能的原因和解决方法: 1. 确认logback.xml位置:确保logback.xml文件位于正确的位置。默认情况下,它应该位于src/main/resources目录下。 2. 检查日志级别:在logback.xml,确保你设置了适当的日志级别。例如,如果你只想看到ERROR级别的日志信息,那么你的配置应该类似于: ```xml <root level="ERROR"> <appender-ref ref="CONSOLE"/> </root> ``` 3. 检查logger配置:确保你正确配置了logger节点。例如,如果你想为特定的包设置日志级别,你可以添加如下配置: ```xml <logger name="com.example.package" level="DEBUG"/> ``` 确保包名和级别都正确,并且没有其他的logger节点覆盖了它。 4. 检查appender配置:确保你正确配置了appender节点。例如,如果你想将日志输出到控制台,你可以添加如下配置: ```xml <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender> ``` 确保你选择了适合你的需求的appender类型,并且配置项正确。 5. 清除缓存并重新启动:有时候,logback.xml的更改可能需要清除缓存并重新启动应用程序才能生效。尝试清除你的构建目录,然后重新构建和启动应用程序。 希望这些解决方法能帮助你解决问题!如果还有其他问题,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值