Location>code7788 >text

Analyzing SpringBoot's LoggingSystem from Source Code → How it Binds Logging Components

Popularity:720 ℃/2024-08-22 09:05:20

Happy Moment

I'm in a bad mood today. I want to meet my buddies for a drink.

Me: I'm in a bad mood, give your girlfriend a heads up, come to my house, come over for a drink

Buddy: OK! I'll let her know.

Me: What do you want to eat? I'll order takeout.

Dude: You two set it up, I already sent her over there

Me:? I told you to come over here! Tell her.

Dude: Hahaha, I'm trying to get her to go over there.

成功给我逗笑了

review of previous events

SpringBoot 2.7 Overwhelming Logback 1.3 → not sweet but thirst quenching Realizedspring-boot together withlogback 1. The integration of a two-step

  1. Turning off Spring Boot's LoggingSystem
  2. Configuration files are created with the

From the examples, the integration is successful; however, there are some issues that are not analyzed, such as

  1. ("", "none") is how it works.
  2. How Spring Boot's LoggingSystem Binds to Logging Components
  3. Spring Boot relies on three logging components by default: logback, log4j, and jul. Why is logback enabled by default instead of the other two?

Based on the above three questions, let's go through the source code of Spring Boot; before looking at the source code, I will first take you to review some of the content to facilitate the following source code analysis

  1. Design Patterns of the Observer Pattern → the underlying principle of the event mechanism

    Speaking of the implementation of the observer pattern, as well as the application of the JDK (JDK event model), Spring in the application (event mechanism); you can focus on the Spring case, the use of very simple, summarized in a sentence that is

    Events sent during SpringBoot startup are received by all ApplicationListener (i.e., the onApplicationEvent method is called)

  2. spring-boot-2.0.3 startup source code Part I - SpringApplication constructor method

    People don't read all the way through, focus ongetSpringFactoriesInstancesThe following is relevant to this article, summarized in one sentence

    Find all the META-INF/ file paths under the class path and load all of them into the cache of the SpringFactoriesLoader, then fetch the ApplicationListener type class from the cache and instantiate it

The following is based on the source code analysis of Spring Boot by default, not the source code analysis of the logback 1. integration.

Integrate logback 1. You need to turn off Spring Boot's LoggingSystem, so what's the point of analyzing it?

source code analysis

The question arises, where to start following? I won't beat around the bush. Start withLoggingApplicationListener Starts to follow, first it's in theMETA-INF/ center

spring.factories_LoggingApplicationListener

Secondly it implementsApplicationListener

LoggingApplicationListener类图

Then Spring Boot will instantiate theLoggingApplicationListenerThe events sent during the Spring Boot startup process will come to theLoggingApplicationListener (used form a nominal expression)onApplicationEvent methodologies

@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) {
		onContextClosedEvent((ContextClosedEvent) event);
	}
	else if (event instanceof ApplicationFailedEvent) {
		onApplicationFailedEvent();
	}
}

The Spring Boot startup process is divided into different phases, and in each phase the events of the corresponding phase are sent.LoggingApplicationListener These events are handled differently, so for now we'll just focus on the following events

ApplicationStartingEvent, corresponding handler: onApplicationStartingEvent

ApplicationEnvironmentPreparedEvent, corresponding handler: onApplicationEnvironmentPreparedEvent

ApplicationPreparedEvent, corresponding handler: onApplicationPreparedEvent

onApplicationStartingEvent

private void onApplicationStartingEvent(ApplicationStartingEvent event) {
	 = (().getClassLoader());
	();
}

The method is simple, get the logging system and call itsbeforeInitialize method, we follow up with

public static LoggingSystem get(ClassLoader classLoader) {
	String loggingSystemClassName = (SYSTEM_PROPERTY);
	if ((loggingSystemClassName)) {
		if ((loggingSystemClassName)) {
			return new NoOpLoggingSystem();
		}
		return get(classLoader, loggingSystemClassName);
	}
	LoggingSystem loggingSystem = SYSTEM_FACTORY.getLoggingSystem(classLoader);
	(loggingSystem != null, "No suitable logging system located");
	return loggingSystem;
}

Hit a breakpoint and debug it, and you'll see.SYSTEM_PROPERTY The value of

system_property

Get from System Properties Is it the same as

("", "none") is how it works.

Corresponding? If the value obtained isnoneThe direct return of theNoOpLoggingSystem an actual example

/**
 * {@link LoggingSystem} that does nothing.
 */
static class NoOpLoggingSystem extends LoggingSystem {

	@Override
	public void beforeInitialize() {

	}

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

	}

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

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

}

All empty implementations, equivalent to turning off Spring Boot's LoggingSystem; It can also be set to other values, but requires a corresponding implementation. By defaultloggingSystemClassName The value ofnull It skips the if and comes to theSYSTEM_FACTORY.getLoggingSystem(classLoader);

@Override
public LoggingSystem getLoggingSystem(ClassLoader classLoader) {
	List<LoggingSystemFactory> delegates = ( != null) ? (classLoader) : null;
	if (delegates != null) {
		for (LoggingSystemFactory delegate : delegates) {
			LoggingSystem loggingSystem = (classLoader);
			if (loggingSystem != null) {
				return loggingSystem;
			}
		}
	}
	return null;
}

Here it is recommended to use breakpoint debugging to follow the source code by pressing theF7 And then we'll come toLoggingSystemFactory#fromSpringFactories

/**
 * Return a {@link LoggingSystemFactory} backed by {@code }.
 * @return a {@link LoggingSystemFactory} instance
 */
static LoggingSystemFactory fromSpringFactories() {
	return new DelegatingLoggingSystemFactory(
			(classLoader) -> (, classLoader));
}

Does this look familiar? (If it doesn't look familiar, go look:spring-boot-2.0.3 startup source code Part I - SpringApplication constructor method) At this point it will do three things

  1. Get the list of class names of factory classes of type LoggingSystemFactory from SpringFactoriesLoader#cache

    spring.factories_LoggingSystemFactory

    has previously been loaded into SpringFactoriesLoader#cache, so it is fetched from the cache at this point; look at the order of the three implementation classes, the top

  2. Instantiate these factory classes

  3. Sort these factory class instances in ascending order by @Order

    The @Order value is the same for all three factory classes, and is@Order(Ordered.LOWEST_PRECEDENCE), so the order remains the same. Still at the forefront

    LoggingSystemFactory列表

return toDelegatingLoggingSystemFactory#getLoggingSystem, iterating over each of these factory class instances yields theLoggingSystem returns immediately without traversing subsequent factory instances; the first one traversed is theThe call to itsgetLoggingSystem methodologies

private static final boolean PRESENT = ("",
		());

@Override
public LoggingSystem getLoggingSystem(ClassLoader classLoader) {
	if (PRESENT) {
		return new LogbackLoggingSystem(classLoader);
	}
	return null;
}

exists (i.e., a logback dependency exists), it is straightforward to create theLogbackLoggingSystem instance and return; at this point Spring Boot's LoggingSystem determines that it will be based on alogbackinstead oflog4jAnd neither is itjulIssues

How Spring Boot's LoggingSystem Binds to Logging Components

Spring Boot relies on three logging components by default: logback, log4j, and jul. Why is logback enabled by default instead of the other two?

Is it clear that the LoggingSystem is identified as LogbackLoggingSystem after returning to theLoggingApplicationListener#onApplicationStartingEvent The second line of the method that calls theLogbackLoggingSystem#beforeInitialize methodologies

@Override
public void beforeInitialize() {
	LoggerContext loggerContext = getLoggerContext();
	if (isAlreadyInitialized(loggerContext)) {
		return;
	}
	();
	().add(FILTER);
}

Main initializationLoggerContextFollow-upgetLoggerContext()

private LoggerContext getLoggerContext() {
	ILoggerFactory factory = ().getLoggerFactory();
	(, factory,
			() -> (
					"LoggerFactory is not a Logback LoggerContext but Logback is on "
							+ "the classpath. Either remove Logback or the competing "
							+ "implementation (%s loaded from %s). If you are using "
							+ "WebLogic you will need to add 'org.slf4j' to "
							+ "prefer-application-packages in WEB-INF/",
					(), getLocation(factory)));
	return (LoggerContext) factory;
}

StaticLoggerBinder Does it look familiar? Look at its full class name:org.inlogback-classic-1.2. Under , andlogback 1. There is no such class.

So spring-boot does not support logback by default 1.

To summarize.onApplicationStartingEvent The method identifies the logging system asLogbackLoggingSystem

onApplicationEnvironmentPreparedEvent

private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
	SpringApplication springApplication = ();
	if ( == null) {
		 = (());
	}
	initialize((), ());
}

It's obvious.loggingSystem let sb. do sth.nullWe're talking directly toinitialize methodologies

protected void initialize(ConfigurableEnvironment environment, ClassLoader classLoader) {
	getLoggingSystemProperties(environment).apply();
	 = (environment);
	if ( != null) {
		();
	}
	// Log Grouping,Not a concern
	 = new LoggerGroups(DEFAULT_GROUP_LOGGERS);
	// Setting Early Logging Levels,principaldebugcap (a poem)traceThe choice between
	initializeEarlyLoggingLevel(environment);
	// Initializing the logging system
	initializeSystem(environment, , );
	// Setting the final log level
	initializeFinalLoggingLevels(environment, );
	registerShutdownHookIfNecessary(environment, );
}

We'll just focus on that for now.initializeSystem methodologies

initializeSystempng

Keep following down toLogbackLoggingSystem#initialize

LogbackLoggingSystem

Keep following down toAbstractLoggingSystem#initialize

AbstractLoggingSystem

Keep following down toAbstractLoggingSystem#initializeWithConventions

private void initializeWithConventions(LoggingInitializationContext initializationContext, LogFile logFile) {
	String config = getSelfInitializationConfig();
	if (config != null && logFile == null) {
		// self initialization has occurred, reinitialize in case of property changes
		reinitialize(initializationContext);
		return;
	}
	if (config == null) {
		config = getSpringInitializationConfig();
	}
	if (config != null) {
		loadConfiguration(initializationContext, config, logFile);
		return;
	}
	loadDefaults(initializationContext, logFile);
}

included among thesegetSelfInitializationConfig() right fromclasspath Look for them one by one.

, , ,

These four files, once found, are returned directly; since finding theSo come to the first if

initializeWithConventions-reinitialize

Continue to follow up and come toLogbackLoggingSystem#reinitialize

LogbackLoggingSystem

commander-in-chief (military) configuration for loading; so far, Spring Boot's LoggingSystem and Logback binding is complete, are you clear?

Let's get back to theAbstractLoggingSystem#initializeWithConventions Ifclasspath arrive at (a decision, conclusion etc)

, , ,

None of these four files will come toconfig = getSpringInitializationConfig();I'm going to follow it gradually.AbstractLoggingSystem#getSpringConfigLocations

protected String[] getSpringConfigLocations() {
	String[] locations = getStandardConfigLocations();
	for (int i = 0; i < ; i++) {
		String extension = (locations[i]);
		locations[i] = locations[i].substring(0, locations[i].length() - () - 1) + "-spring."
				+ extension;
	}
	return locations;
}

This is a method that everyone can understand, right?locations value of

, , ,

Iterate through them one by one, and then splice them to get the final

, , ,

getSpringConfigLocations

likewise fromclasspath It looks for them one by one, and returns them as soon as it finds them; that's why our logging configuration file is the The reason why it also works. We can prioritize Spring Boot's logging profiles

> > > > > > >

To summarize.onApplicationEnvironmentPreparedEvent Completed initialization of the logging system (loading of logging configuration files)

onApplicationPreparedEvent

private void onApplicationPreparedEvent(ApplicationPreparedEvent event) {
	ConfigurableApplicationContext applicationContext = ();
	ConfigurableListableBeanFactory beanFactory = ();
	if (!(LOGGING_SYSTEM_BEAN_NAME)) {
		(LOGGING_SYSTEM_BEAN_NAME, );
	}
	if ( != null && !(LOG_FILE_BEAN_NAME)) {
		(LOG_FILE_BEAN_NAME, );
	}
	if ( != null && !(LOGGER_GROUPS_BEAN_NAME)) {
		(LOGGER_GROUPS_BEAN_NAME, );
	}
	if (!(LOGGING_LIFECYCLE_BEAN_NAME) && () == null) {
		(LOGGING_LIFECYCLE_BEAN_NAME, new Lifecycle());
	}
}

The code isn't complicated, it just registers a couple ofBean to the Spring container, where theloggingSystem is the one we're more concerned with for now, and by default its type is:LogbackLoggingSystem

Log Printing

Once Spring Boot's LoggingSystem has finished binding to Logback, how does it use and then print logs? Is it also like the

业务日志打印样例

To use it like that? That's absolutely impossible!

绝对不可能

What does this have to do with Spring Boot's LoggingSystem? Let's look at the use of logging in Spring Boot.SpringApplication 179 lines are used.

SpringApplicaton179行

We'll find out.LogLogFactory existspring-jcl-5.3. take over

spring-jcl_LogFactory

spring-jcl similar to slf4j, is also a log facade, this article does not expand

supplementary I'm going to follow you all the way toLogAdapter#createLog

public static Log createLog(String name) {
	switch (logApi) {
		case LOG4J:
			return (name);
		case SLF4J_LAL:
			return (name);
		case SLF4J:
			return (name);
		default:
			// Defensively use lazy-initializing adapter class here as well since the
			//  module is not present by default on JDK 9. We are requiring
			// its presence if neither Log4j nor SLF4J is available; however, in the
			// case of Log4j or SLF4J, we are trying to prevent early initialization
			// of the JavaUtilLog adapter - . by a JVM in debug mode - when eagerly
			// trying to parse the bytecode for all the cases of this switch clause.
			return (name);
	}
}

logApi value is obtained as follows

private static final String LOG4J_SPI = ".";

private static final String LOG4J_SLF4J_PROVIDER = ".slf4j.SLF4JProvider";

private static final String SLF4J_SPI = "org.";

private static final String SLF4J_API = "org.";

private static final LogApi logApi;

static {
	if (isPresent(LOG4J_SPI)) {
		if (isPresent(LOG4J_SLF4J_PROVIDER) && isPresent(SLF4J_SPI)) {
			// log4j-to-slf4j bridge -> we'll rather go with the SLF4J SPI;
			// however, we still prefer Log4j over the plain SLF4J API since
			// the latter does not have location awareness support.
			logApi = LogApi.SLF4J_LAL;
		}
		else {
			// Use Log4j  directly, including location awareness support
			logApi = LogApi.LOG4J;
		}
	}
	else if (isPresent(SLF4J_SPI)) {
		// Full SLF4J SPI including location awareness support
		logApi = LogApi.SLF4J_LAL;
	}
	else if (isPresent(SLF4J_API)) {
		// Minimal SLF4J API without location awareness support
		logApi = LogApi.SLF4J;
	}
	else {
		//  as default
		logApi = ;
	}
}

Find the classes under the class path one by one according to the priority, and return them directly when found; Spring Boot uses SLF4J + Logback by default, so thelogApi The value ofSLF4J_SPISo.LogAdapter#createLog The type of the return value ofLogAdapter$Slf4jLocationAwareLog

LogAdapter$Slf4jLocationAwareLog

Equivalent to the completion of thespring-jcl until (a time)slf4j So, Spring Boot logging is still SLF4J + Logback? What does it have to do with Spring Boot's LoggingSystem? Stay tuned for the next

未完待续

summarize

  1. onApplicationStartingEvent

    Determine the logging system type and create the correspondingLoggingSystemThe default isLogbackLoggingSystem

  2. onApplicationEnvironmentPreparedEvent

    Finish loading the logging configuration file andLoggingSystem initialization

  3. Spring Boot's log printing doesn't seem to have anything to do with LoggingSystem? Analyze this in the next post