Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >聊聊springboot的LogbackLoggingSystem

聊聊springboot的LogbackLoggingSystem

原创
作者头像
code4it
发布于 2023-11-02 01:20:23
发布于 2023-11-02 01:20:23
27000
代码可运行
举报
文章被收录于专栏:码匠的流水账码匠的流水账
运行总次数:0
代码可运行

本文主要研究一下springboot的LogbackLoggingSystem

LoggingSystem

org/springframework/boot/logging/LoggingSystem.java

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public abstract class LoggingSystem {

	public abstract void beforeInitialize();

	public void initialize(LoggingInitializationContext initializationContext, String configLocation, LogFile logFile) {
	}

	public void cleanUp() {
	}

	public Runnable getShutdownHandler() {
		return null;
	}

	public Set<LogLevel> getSupportedLogLevels() {
		return EnumSet.allOf(LogLevel.class);
	}

	public void setLogLevel(String loggerName, LogLevel level) {
		throw new UnsupportedOperationException("Unable to set log level");
	}

	public List<LoggerConfiguration> getLoggerConfigurations() {
		throw new UnsupportedOperationException("Unable to get logger configurations");
	}

	public LoggerConfiguration getLoggerConfiguration(String loggerName) {
		throw new UnsupportedOperationException("Unable to get logger configuration");
	}				
}

LoggingSystem定义了beforeInitialize抽象方法,需要子类实现,,同时还提供了setLogLevel、getLoggerConfigurations、getLoggerConfiguration,默认是抛出UnsupportedOperationException

NoOpLoggingSystem

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
	static class NoOpLoggingSystem extends LoggingSystem {

		@Override
		public void beforeInitialize() {

		}

		@Override
		public void setLogLevel(String loggerName, LogLevel level) {

		}

		@Override
		public List<LoggerConfiguration> getLoggerConfigurations() {
			return Collections.emptyList();
		}

		@Override
		public LoggerConfiguration getLoggerConfiguration(String loggerName) {
			return null;
		}

	}

NoOpLoggingSystem继承了LoggingSystem,其方法都是空操作

AbstractLoggingSystem

org/springframework/boot/logging/AbstractLoggingSystem.java

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public abstract class AbstractLoggingSystem extends LoggingSystem {

	protected static final Comparator<LoggerConfiguration> CONFIGURATION_COMPARATOR = new LoggerConfigurationComparator(
			ROOT_LOGGER_NAME);

	private final ClassLoader classLoader;

	public AbstractLoggingSystem(ClassLoader classLoader) {
		this.classLoader = classLoader;
	}

	@Override
	public void beforeInitialize() {
	}

	@Override
	public void initialize(LoggingInitializationContext initializationContext, String configLocation, LogFile logFile) {
		if (StringUtils.hasLength(configLocation)) {
			initializeWithSpecificConfig(initializationContext, configLocation, logFile);
			return;
		}
		initializeWithConventions(initializationContext, logFile);
	}

	/**
	 * Load sensible defaults for the logging system.
	 * @param initializationContext the logging initialization context
	 * @param logFile the file to load or {@code null} if no log file is to be written
	 */
	protected abstract void loadDefaults(LoggingInitializationContext initializationContext, LogFile logFile);

	/**
	 * Load a specific configuration.
	 * @param initializationContext the logging initialization context
	 * @param location the location of the configuration to load (never {@code null})
	 * @param logFile the file to load or {@code null} if no log file is to be written
	 */
	protected abstract void loadConfiguration(LoggingInitializationContext initializationContext, String location,
			LogFile logFile);

	/**
	 * Reinitialize the logging system if required. Called when
	 * {@link #getSelfInitializationConfig()} is used and the log file hasn't changed. May
	 * be used to reload configuration (for example to pick up additional System
	 * properties).
	 * @param initializationContext the logging initialization context
	 */
	protected void reinitialize(LoggingInitializationContext initializationContext) {
	}

	//......
}	

AbstractLoggingSystem继承了LoggingSystem,它主要是重写了initialize方法,若存在configLocation配置则执行initializeWithSpecificConfig,否则执行initializeWithConventions;它同时还定义了loadDefaults、loadConfiguration抽象方法需要子类实现

Slf4JLoggingSystem

org/springframework/boot/logging/Slf4JLoggingSystem.java

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public abstract class Slf4JLoggingSystem extends AbstractLoggingSystem {

	private static final String BRIDGE_HANDLER = "org.slf4j.bridge.SLF4JBridgeHandler";

	public Slf4JLoggingSystem(ClassLoader classLoader) {
		super(classLoader);
	}

	@Override
	public void beforeInitialize() {
		super.beforeInitialize();
		configureJdkLoggingBridgeHandler();
	}

	@Override
	public void cleanUp() {
		if (isBridgeHandlerAvailable()) {
			removeJdkLoggingBridgeHandler();
		}
	}

	@Override
	protected void loadConfiguration(LoggingInitializationContext initializationContext, String location,
			LogFile logFile) {
		Assert.notNull(location, "Location must not be null");
		if (initializationContext != null) {
			applySystemProperties(initializationContext.getEnvironment(), logFile);
		}
	}

	//......
}	

Slf4JLoggingSystem继承了AbstractLoggingSystem,它覆盖了beforeInitialize,新增configureJdkLoggingBridgeHandler;其cleanUp方法在isBridgeHandlerAvailable的时候执行removeJdkLoggingBridgeHandler;其loadConfiguration在initializationContext不为null的时候执行applySystemProperties

configureJdkLoggingBridgeHandler

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
	private void configureJdkLoggingBridgeHandler() {
		try {
			if (isBridgeJulIntoSlf4j()) {
				removeJdkLoggingBridgeHandler();
				SLF4JBridgeHandler.install();
			}
		}
		catch (Throwable ex) {
			// Ignore. No java.util.logging bridge is installed.
		}
	}

configureJdkLoggingBridgeHandler主要是判断是否将JUL绑定到SLF4J,是的话则removeJdkLoggingBridgeHandler,然后执行SLF4JBridgeHandler.install()

removeJdkLoggingBridgeHandler

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
	private void removeJdkLoggingBridgeHandler() {
		try {
			removeDefaultRootHandler();
			SLF4JBridgeHandler.uninstall();
		}
		catch (Throwable ex) {
			// Ignore and continue
		}
	}

	private void removeDefaultRootHandler() {
		try {
			Logger rootLogger = LogManager.getLogManager().getLogger("");
			Handler[] handlers = rootLogger.getHandlers();
			if (handlers.length == 1 && handlers[0] instanceof ConsoleHandler) {
				rootLogger.removeHandler(handlers[0]);
			}
		}
		catch (Throwable ex) {
			// Ignore and continue
		}
	}

removeJdkLoggingBridgeHandler主要是执行removeDefaultRootHandler,以及SLF4JBridgeHandler.uninstall()

applySystemProperties

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
	protected final void applySystemProperties(Environment environment, LogFile logFile) {
		new LoggingSystemProperties(environment).apply(logFile);
	}

	public void apply(LogFile logFile) {
		PropertyResolver resolver = getPropertyResolver();
		setSystemProperty(resolver, EXCEPTION_CONVERSION_WORD, "exception-conversion-word");
		setSystemProperty(PID_KEY, new ApplicationPid().toString());
		setSystemProperty(resolver, CONSOLE_LOG_PATTERN, "pattern.console");
		setSystemProperty(resolver, FILE_LOG_PATTERN, "pattern.file");
		setSystemProperty(resolver, FILE_CLEAN_HISTORY_ON_START, "file.clean-history-on-start");
		setSystemProperty(resolver, FILE_MAX_HISTORY, "file.max-history");
		setSystemProperty(resolver, FILE_MAX_SIZE, "file.max-size");
		setSystemProperty(resolver, FILE_TOTAL_SIZE_CAP, "file.total-size-cap");
		setSystemProperty(resolver, LOG_LEVEL_PATTERN, "pattern.level");
		setSystemProperty(resolver, LOG_DATEFORMAT_PATTERN, "pattern.dateformat");
		setSystemProperty(resolver, ROLLING_FILE_NAME_PATTERN, "pattern.rolling-file-name");
		if (logFile != null) {
			logFile.applyToSystemProperties();
		}
	}	

applySystemProperties通过LoggingSystemProperties设置了系统属性方便后续log配置文件使用

LogbackLoggingSystem

org/springframework/boot/logging/logback/LogbackLoggingSystem.java

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class LogbackLoggingSystem extends Slf4JLoggingSystem {

	private static final String CONFIGURATION_FILE_PROPERTY = "logback.configurationFile";

	private static final LogLevels<Level> LEVELS = new LogLevels<>();

	static {
		LEVELS.map(LogLevel.TRACE, Level.TRACE);
		LEVELS.map(LogLevel.TRACE, Level.ALL);
		LEVELS.map(LogLevel.DEBUG, Level.DEBUG);
		LEVELS.map(LogLevel.INFO, Level.INFO);
		LEVELS.map(LogLevel.WARN, Level.WARN);
		LEVELS.map(LogLevel.ERROR, Level.ERROR);
		LEVELS.map(LogLevel.FATAL, Level.ERROR);
		LEVELS.map(LogLevel.OFF, Level.OFF);
	}

	private static final TurboFilter FILTER = new TurboFilter() {

		@Override
		public FilterReply decide(Marker marker, ch.qos.logback.classic.Logger logger, Level level, String format,
				Object[] params, Throwable t) {
			return FilterReply.DENY;
		}

	};

	public LogbackLoggingSystem(ClassLoader classLoader) {
		super(classLoader);
	}

	@Override
	protected String[] getStandardConfigLocations() {
		return new String[] { "logback-test.groovy", "logback-test.xml", "logback.groovy", "logback.xml" };
	}

	//......
}	

LogbackLoggingSystem继承了Slf4JLoggingSystem,其getStandardConfigLocations返回logback-test.groovy, logback-test.xml, logback.groovy, logback.xml

beforeInitialize

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
	@Override
	public void beforeInitialize() {
		LoggerContext loggerContext = getLoggerContext();
		if (isAlreadyInitialized(loggerContext)) {
			return;
		}
		super.beforeInitialize();
		loggerContext.getTurboFilterList().add(FILTER);
	}

beforeInitialize方法主要是添加了TurboFilter

initialize

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
	@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.");
		}
	}

initialize方法执行super.initialize(initializationContext, configLocation, logFile),然后markAsInitialized(loggerContext)

loadDefaults

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
	@Override
	protected void loadDefaults(LoggingInitializationContext initializationContext, LogFile logFile) {
		LoggerContext context = getLoggerContext();
		stopAndReset(context);
		boolean debug = Boolean.getBoolean("logback.debug");
		if (debug) {
			StatusListenerConfigHelper.addOnConsoleListenerInstance(context, new OnConsoleStatusListener());
		}
		LogbackConfigurator configurator = debug ? new DebugLogbackConfigurator(context)
				: new LogbackConfigurator(context);
		Environment environment = initializationContext.getEnvironment();
		context.putProperty(LoggingSystemProperties.LOG_LEVEL_PATTERN,
				environment.resolvePlaceholders("${logging.pattern.level:${LOG_LEVEL_PATTERN:%5p}}"));
		context.putProperty(LoggingSystemProperties.LOG_DATEFORMAT_PATTERN, environment.resolvePlaceholders(
				"${logging.pattern.dateformat:${LOG_DATEFORMAT_PATTERN:yyyy-MM-dd HH:mm:ss.SSS}}"));
		context.putProperty(LoggingSystemProperties.ROLLING_FILE_NAME_PATTERN, environment
				.resolvePlaceholders("${logging.pattern.rolling-file-name:${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz}"));
		new DefaultLogbackConfiguration(initializationContext, logFile).apply(configurator);
		context.setPackagingDataEnabled(true);
	}

loadDefaults方法通过LOG_LEVEL_PATTERN、LOG_DATEFORMAT_PATTERN、ROLLING_FILE_NAME_PATTERN以及LogbackConfigurator来初始化DefaultLogbackConfiguration

loadConfiguration

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
	@Override
	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);
		}
		List<Status> statuses = loggerContext.getStatusManager().getCopyOfStatusList();
		StringBuilder errors = new StringBuilder();
		for (Status status : statuses) {
			if (status.getLevel() == Status.ERROR) {
				errors.append((errors.length() > 0) ? String.format("%n") : "");
				errors.append(status.toString());
			}
		}
		if (errors.length() > 0) {
			throw new IllegalStateException(String.format("Logback configuration error detected: %n%s", errors));
		}
	}

	private void configureByResourceUrl(LoggingInitializationContext initializationContext, LoggerContext loggerContext,
			URL url) throws JoranException {
		if (url.toString().endsWith("xml")) {
			JoranConfigurator configurator = new SpringBootJoranConfigurator(initializationContext);
			configurator.setContext(loggerContext);
			configurator.doConfigure(url);
		}
		else {
			new ContextInitializer(loggerContext).configureByResource(url);
		}
	}	

loadConfiguration方法主要是执行configureByResourceUrl,该方法通过SpringBootJoranConfigurator或者ContextInitializer的configureByResource进行配置

小结

springboot定义了LoggingSystem、AbstractLoggingSystem、Slf4JLoggingSystem,依次继承,而LogbackLoggingSystem则继承Slf4JLoggingSystem,它主要是定义了要加载的默认的配置文件logback-test.groovy, logback-test.xml, logback.groovy, logback.xml,以及loadDefaults方法,通过LOG_LEVEL_PATTERN、LOG_DATEFORMAT_PATTERN、ROLLING_FILE_NAME_PATTERN以及LogbackConfigurator来初始化DefaultLogbackConfiguration。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
聊聊springboot的LogbackLoggingSystem
org/springframework/boot/logging/LoggingSystem.java
code4it
2023/11/03
1990
聊聊springboot的LogbackLoggingSystem
SpringBoot日志源码解析:日志监听器的执行
LoggingApplicationListener 的主要作用是配置LoggingSystem, 如果 环境 包含 loggingconfig 属性,LoggingApplicationListener 将用于引导 日志记录系统,否则使用默认配置。
愿天堂没有BUG
2022/10/28
9890
SpringBoot日志源码解析:日志监听器的执行
SpringBoot3.x日志生产最佳实践原来是这样!
SpringBoot对日志的配置和加载进行了封装,让我们可以很方便地使用一些日志框架,只需要定义对应日志框架的配置文件,如LogBack、Log4j、Log4j2等,代码内部便可以直接使用。
JavaEdge
2023/08/15
2.4K0
SpringBoot3.x日志生产最佳实践原来是这样!
springboot 默认日志配置源码
版本 2.2.x 日志系统初始化流程 应用启动时,ApplicationStartingEvent触发,屏蔽所有日志 ApplicationEnvironmentPreparedEvent触发,清空所有日志配置,重新初始化日志系统 配置变更,EnvironmentChangeEvent触发,重新设置日志级别 源码 org.springframework.cloud.bootstrap.LoggingSystemShutdownListener public class LoggingSystemShutdo
路过君
2020/09/07
1K0
springboot中log加载流程
springboot容器启动的时候, 会在不同的阶段发送Event事件. 在LoggingApplicationListener中代码如下:
eeaters
2024/11/11
1401
springboot中log加载流程
SpringBoot 系列-日志详解
默认情况下,如果使用 “starters”,则使用 Logback 进行日志记录。还包括适当的 Logback 路由,以确保使用 Java Util 日志记录、Commons 日志记录、Log4J 或 SLF4J 的依赖库都能正常工作。
安徽开发者圈
2020/03/02
1.9K0
SpringBoot 中的日志原来是这么工作的
我们以Springboot集成Log4j2为例,详细说明Springboot框架下Log4j2是如何工作的,你可能会担心,如果是使用Logback日志框架该怎么办呢?
程序员蜗牛
2024/04/12
4760
SpringBoot 中的日志原来是这么工作的
从源码分析 SpringBoot 的 LoggingSystem → 它是如何绑定日志组件的
SpringBoot2.7 霸王硬上弓 Logback1.3 → 不甜但解渴 实现了 spring-boot 2.x.x 与 logback 1.3.x 的集成,分两步
青石路
2024/08/23
1950
从源码分析 SpringBoot 的 LoggingSystem → 它是如何绑定日志组件的
springboot通过javaconfig实现logback配置
继承springboot默认的LogbackLoggingSystem,并修改默认日志配置实现方法
路过君
2020/09/07
9700
记一次logback配置文件未生效问题排查
最近在公司新建了一个JAVA微服务,采用的是springboot框架,logback作为日志模块的实现。在搭建的的过程中想起之前在文档中看到springboot支持用logback-spring.xml作为定制的logback配置文件。在这个文件中可以使用spring的定制化标签,比如可以根据当前生效的profile对日志文件进行配置,从而省去配置多份日志文件并在profile中指定具体当前生效的配置。在阅读了一下教程之后,我在resources目录下新建了logback-spring.xml的配置文件,内容如下:
眯眯眼的猫头鹰
2022/03/23
5.7K0
记一次logback配置文件未生效问题排查
聊聊springboot的logback配置
org/springframework/boot/logging/logback/defaults.xml
code4it
2023/10/31
7680
聊聊springboot的logging.group
org/springframework/boot/actuate/logging/LoggersEndpoint.java
code4it
2023/12/02
1840
日志框架Log4j的学习小记
Java项目的框架基本就是slf4j,slf4j提供了一套规范,也就是门面,而至于后边是如何实现的只要按照人家定义的接口去做就行了。常见的日志框架又springboot自带的logback,还有异步的日志框架log4j,当然还有一些大佬自己做日志框架的。这里作者大概的看了一下日志框架的代码。稍微讲解一下日志框架是怎么做的。
写一点笔记
2022/08/11
2970
日志框架Log4j的学习小记
SpringBoot 用的 spring-jcl 打印日志,与 LoggingSystem 有鸡毛关系?
从源码分析 SpringBoot 的 LoggingSystem → 它是如何绑定日志组件的 从源码的角度讲述了 Spring Boot 的 LoggingSystem 与日志组件的绑定,默认情况下绑定的是 Logback;但当我们具体去看 Spring Boot 的日志打印,却发现用的是 spring-jcl ,通过它适配了 slf4j,真正的日志打印还得依赖具体的日志组件,默认情况下使用的是 logback;那这么说来,Spring Boot 的日志打印与 Spring Boot 的 LoggingSystem 貌似没关系呀?
青石路
2024/08/24
1150
SpringBoot 用的 spring-jcl 打印日志,与 LoggingSystem 有鸡毛关系?
SpringBoot2.7 霸王硬上弓 Logback1.3 → 不甜但解渴
SpringBoot2.7还是任性的,就是不支持Logback1.3,你能奈他何 讲了很多,总结下来就两点
青石路
2024/07/31
5560
SpringBoot2.7 霸王硬上弓 Logback1.3 → 不甜但解渴
Spring Boot 2动态修改日志级别
作为程序猿,定位问题是我们的日常工作,而日志是我们定位问题非常重要的依据。传统方式定位问题时,往往是如下步骤:
张乘辉
2019/06/14
1.3K0
Spring Boot 2动态修改日志级别
SpringBoot集成日志logback.groovy报错: Groovy classes are not available on the class path. ABORTING INITIAL
SpringBoot集成日志logback.groovy报错: Groovy classes are not available on the class path. ABORTING INITIALIZATION.
一个会写诗的程序员
2018/08/20
8230
Spring Boot 2动态修改日志级别
作为程序猿,定位问题是我们的日常工作,而日志是我们定位问题非常重要的依据。传统方式定位问题时,往往是如下步骤:
乱敲代码
2019/06/03
1.4K0
13.7 SpringBoot集成日志系统logback的几个问题问题1: Logging system failed to initialize using configuration from
让人感到疑惑的是,SpringBoot居然没有对application.properties配置文件value末端作空格trim处理。
一个会写诗的程序员
2018/08/20
8.8K0
聊聊springboot的SpringPropertyAction
org/springframework/boot/logging/logback/SpringBootJoranConfigurator.java
code4it
2023/11/03
2150
推荐阅读
相关推荐
聊聊springboot的LogbackLoggingSystem
更多 >
领券
💥开发者 MCP广场重磅上线!
精选全网热门MCP server,让你的AI更好用 🚀
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验