Location>code7788 >text

The binding changes between SLF4J2. and Logback1. are still significant, so don't mess around with them!

Popularity:781 ℃/2024-08-02 09:06:21

Happy Moment

I was talking to my sister today.

Me: I'm in love with a female colleague in our company, she's so pretty, I'm moved, what should I do?

Sis: You can't like a girl just by looking at her appearance

Me: I know, it depends on what's inside her.

Sis: You're overthinking it, but also look at your appearance

还要看自己的外表

Background

existSpringBoot 2.7 Overwhelming Logback 1.3 → not sweet but thirst quenching That part of the schematic analysis, I'm interested inLogback The expression is a euphemism for

委婉表述

Then I thought, as a software developer, how can I be so undisciplined, it's really not right, in order to express my most sincere apologies, please allow me to punish myself with three slaps!

罗永浩打脸

To make up for it, I'm going to take you through the rest of it.Logback 1.3.14 part of the source code. ReferencesUnderstanding slf4j bindings from the source code, and logback's loading of configuration filesThe same is based on two issues

  1. How SLF4J is bound to Logback
  2. How Logback loads configuration files

to start the analysis. Before I analyze it, let me help you with a point of doubt that you may have encountered

How come the version of SLF4J that Logback 1.3.14 depends on is 1.7.36?

Suppose our content is as follows

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="/POM/4.0.0"
         xmlns:xsi="http:///2001/XMLSchema-instance"
         xsi:schemaLocation="/POM/4.0.0 /xsd/maven-4.0.">
    <modelVersion>4.0.0</modelVersion>

    <groupId></groupId>
    <artifactId>spring-boot-2_7_18</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId></groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.18</version>
    </parent>

    <properties>
        <>8</>
        <>8</>
        <>UTF-8</>
        <>1.3.14</>
    </properties>

    <dependencies>
        <dependency>
            <groupId></groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <artifactId>spring-boot-starter-logging</artifactId>
                    <groupId></groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId></groupId>
            <artifactId>logback-classic</artifactId>
            <version>${}</version>
        </dependency>
    </dependencies>
</project>

But we'll see that logback has the following dependency tree

slf4j乱入

Whether it's logbackofficial post

slf4j与logback官配

Or the dependencies in the logback 1.3.14 pom file

logback-parent_slf4j

slf4j-api The versions are all2.logback 1.3.14 relies on theslf4j-api 2.0.7) ,slf4j-api 1.7.36 Where did that come from?

This is due to the introduction of the parent dependency

<parent>
	<groupId></groupId>
	<artifactId>spring-boot-starter-parent</artifactId>
	<version>2.7.18</version>
</parent>

(indicates contrast)spring-boot-starter-parent parent dependency

<parent>
	<groupId></groupId>
	<artifactId>spring-boot-dependencies</artifactId>
	<version>2.7.18</version>
</parent>

existspring-boot-dependencies The slf4j version is specified

spring-boot-dependencies_slf4j1.7.36

Then why not?logback-parent-1.3. hit the nail on the head effective, but ratherspring-boot-dependencies-2.7. hit the nail on the head What about entry into force? This involvesmaven Dependency prioritization, interested parties can go to the relevant information, this article will not be expanded, because the deviation from our original goal is getting farther and farther away

So how will theslf4j adapt (a story to another medium)2.0.7The two ways to do this are as follows

  1. If notspring-bootThen remove the parent dependency.spring-boot-starter-parent

    This is equivalent to bringing in slf4j from logback, which introduces the version that logback depends on

  2. In our pom file, specify the<>2.0.7</>

    Here again, it's about prioritizing maven dependencies, which are higher in our own pom file

Either way, get the version right anyway!

slf4j_2.0.7

SLF4J Binding Logback

Preparing the test code

public class LogbackTest {

    private static Logger LOGGER = ();

    public static void main(String[] args)
    {
        ("......info");
    }
}

You should know where to start with the source code, right? You don't have a choice, you have to choose.getLogger methodologies

bind前奏

Recommended for everyonedebug The way to follow, otherwise it is easy to lose; come to theorg.#bind method here to complete theslf4j There are two things about the bind method that we need to analyze for ourselves.

bind方法
  1. findServiceProviders

    static List<SLF4JServiceProvider> findServiceProviders() {
    	// retain behaviour similar to that of 1.7 series and earlier. More specifically, use the class loader that
    	// loaded the present class to search for services
    	final ClassLoader classLoaderOfLoggerFactory = ();
    	ServiceLoader<SLF4JServiceProvider> serviceLoader = getServiceLoader(classLoaderOfLoggerFactory);
    	List<SLF4JServiceProvider> providerList = new ArrayList<>();
    	Iterator<SLF4JServiceProvider> iterator = ();
    	while (()) {
    		safelyInstantiate(providerList, iterator);
    	}
    	return providerList;
    }
    

    Does it feel a little familiar? Let's review.JDK SPIIs it dawning on you? I'm gonna go.classpath lowerMETA-INF/services Look in the catalogorg..SLF4JServiceProvider file

    spi

    Then read the contents of it and instantiate the

    LogbackServiceProvider

    Got it here.ProviderIt is notLogger

  2. initialize

    initialize

    Let's take a look.defaultLoggerContext types of LoggerContext

    public class LoggerContext extends ContextBase implements ILoggerFactory, LifeCycle

    The second point is related to the Logback loading configuration file, and we'll look at it in more detail later, so let's just look at the first point for now.

    LoggerContext

    Watch this.Logger types of

    public final class Logger
    implements org., LocationAwareLogger, LoggingEventAware, AppenderAttachable, Serializable

    Realizedorg.It's the same asslf4j It's connected.

    Next out of the stack and back to the

    public static ILoggerFactory getILoggerFactory() {
    	return getProvider().getLoggerFactory();
    }
    

    getProvider() It's already been analyzed, so let's look at the nextgetLoggerFactory()

        public ILoggerFactory getLoggerFactory() {
            return defaultLoggerContext;
    
    //        if (!initialized) {
    //            return defaultLoggerContext;
    //        
    //
    //        if (() == null) {
    //            throw new IllegalStateException("contextSelector cannot be null. See also " + NULL_CS_URL);
    //        }
    //        return ().getLoggerContext();
        }
    

    It's very simple, just go straight back todefaultLoggerContext, defaultLoggerContext in the previousinitialize It's already been covered, so if you've forgotten, go back to the top and take a look.

    through (a gap)getILoggerFactory() Continue out of the stack to

    public static Logger getLogger(String name) {
    	ILoggerFactory iLoggerFactory = getILoggerFactory();
    	return (name);
    }
    

    Here.iLoggerFactory Is that what it is?defaultLoggerContext? Read next.(name)

    This method is slightly longer, but not difficult, there is just a cache design, I won't expand on it, you guys go through it on your own

summarize

  1. Binding of SLF4JServiceProvider by means of SPI ()
  2. LogbackServiceProvider (used form a nominal expression) initialize method instantiates the defaultLoggerContext( implement org.)
  3. pass (a bill or inspection etc) defaultLoggerContext gain logger( implements org.)
  4. org. binding completed

Logback Load Configuration File

As already mentioned.#initializeLoggerContext Finish loading the configuration file

private void initializeLoggerContext() {
	try {
		try {
			new ContextInitializer(defaultLoggerContext).autoConfig();
		} catch (JoranException je) {
			("Failed to auto configure default logger context", je);
		}
		// LOGBACK-292
		if (!(defaultLoggerContext)) {
			(defaultLoggerContext);
		}
		// (defaultLoggerContext, KEY);

	} catch (Exception t) { // see LOGBACK-1159
		("Failed to instantiate [" + () + "]", t);
	}
}

At a glance, the next step is straightforwardautoConfigIf you follow 2 steps, you will come to the following method

public void autoConfig(ClassLoader classLoader) throws JoranException {

	// see /qos-ch/logback/issues/715
	classLoader = (classLoader);

	String versionStr = ();
	if (versionStr == null) {
		versionStr = ;
	}
	().add(new InfoStatus(CoreConstants.LOGBACK_CLASSIC_VERSION_MESSAGE + versionStr, loggerContext));
	(loggerContext);


	// invoke custom configurators
	List<Configurator> configuratorList = (, classLoader);
	(rankComparator);
	if (()) {
		("No custom configurators were discovered as a service.");
	} else {
		printConfiguratorOrder(configuratorList);
	}

	for (Configurator c : configuratorList) {
		if (invokeConfigure(c) == .DO_NOT_INVOKE_NEXT_IF_ANY)
			return;
	}

	// invoke internal configurators
	for (String configuratorClassName : INTERNAL_CONFIGURATOR_CLASSNAME_LIST) {
		("Trying to configure with "+configuratorClassName);
		Configurator c = instantiateConfiguratorByClassName(configuratorClassName, classLoader);
		if(c == null)
			continue;
		if (invokeConfigure(c) == .DO_NOT_INVOKE_NEXT_IF_ANY)
			return;
	}
}

The first part reads custom configurations, which can be ignored since we don't have any custom configurations, and goes straight to the

// invoke internal configurators
for (String configuratorClassName : INTERNAL_CONFIGURATOR_CLASSNAME_LIST) {
	("Trying to configure with "+configuratorClassName);
	Configurator c = instantiateConfiguratorByClassName(configuratorClassName, classLoader);
	if(c == null)
		continue;
	if (invokeConfigure(c) == .DO_NOT_INVOKE_NEXT_IF_ANY)
		return;
}

INTERNAL_CONFIGURATOR_CLASSNAME_LIST It reads as follows

String[] INTERNAL_CONFIGURATOR_CLASSNAME_LIST = {"",
            "", ""}

The for loop is once theinvoke on it, then it returns directly, so it'sINTERNAL_CONFIGURATOR_CLASSNAME_LIST Element by element, front to backinvoke, which ends directly once it succeeds; bydebug We'll find out.DefaultJoranConfigurator invoke, itsperformMultiStepConfigurationFileSearch method to find the configuration file

performMultiStepConfigurationFileSearch

Priority, in descending order, will start withclasspath Look for three files under

  1. quest
  2. quest
  3. quest

Once it is found, it is returned directly and does not continue to look for it; we use the

without using the other two files, so the effective

Look back again.Background The laxity inLogback 1.3.14 The loading of configuration files is similar to theLogback 1.1.7 It's basically the same, just less of reading; but then again, theSLF4J together withLogback The binding process is still very much in flux, and everyone can talk to theUnderstanding slf4j bindings from the source code, and logback's loading of configuration files careful comparison

愣着干啥,鼓掌

summarize

  1. SLF4J 2. bindings with Logback 1. using theSPI machine

  2. Logback 1. default profile priority

    > >

    Priority from highest to lowest Once one is read, this is used directly and does not continue down the list

    the reason whySpringBoot 2.7 Overwhelming Logback 1.3 → not sweet but thirst quenching included in

    配置文件必须是

    The configuration file must be Not rigorous enough, what else could it be, you should know?

    头给你敲破
  3. choose as far as possibleofficial post Depend on the version, don't have a head iron, don't have a head iron, don't have a head iron, don't have a head iron!