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
- Turning off Spring Boot's LoggingSystem
- Configuration files are created with the
From the examples, the integration is successful; however, there are some issues that are not analyzed, such as
- ("", "none") is how it works.
- 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?
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
-
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)
-
spring-boot-2.0.3 startup source code Part I - SpringApplication constructor method
People don't read all the way through, focus on
getSpringFactoriesInstances
The following is relevant to this article, summarized in one sentenceFind 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
Secondly it implementsApplicationListener
Then Spring Boot will instantiate theLoggingApplicationListener
The 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
Get from System Properties Is it the same as
("", "none") is how it works.
Corresponding? If the value obtained isnone
The 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 default
loggingSystemClassName
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
-
Get the list of class names of factory classes of type LoggingSystemFactory from SpringFactoriesLoader#cache
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
-
Instantiate these factory classes
-
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
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 its
getLoggingSystem
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 the
LogbackLoggingSystem
instance and return; at this point Spring Boot's LoggingSystem determines that it will be based on alogback
instead oflog4j
And neither is itjul
Issues
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 initializationLoggerContext
Follow-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.null
We'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
Keep following down toLogbackLoggingSystem#initialize
Keep following down toAbstractLoggingSystem#initialize
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
Continue to follow up and come toLogbackLoggingSystem#reinitialize
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
, , ,
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.
We'll find out.Log
、LogFactory
existspring-jcl-5.3.
take over
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 to
LogAdapter#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_SPI
So.LogAdapter#createLog
The type of the return value ofLogAdapter$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
-
onApplicationStartingEvent
Determine the logging system type and create the corresponding
LoggingSystem
The default isLogbackLoggingSystem
-
onApplicationEnvironmentPreparedEvent
Finish loading the logging configuration file and
LoggingSystem
initialization -
Spring Boot's log printing doesn't seem to have anything to do with LoggingSystem? Analyze this in the next post