Why slF4J

There are so many logging frameworks out there! Log4j, logback, java.util.logging,log4j2, there may be other new and better logging boxes in the future, or you may develop your own logging component based on your company’s needs. So the question is, if I’m using Log4J and I’m on a project team using Logback, do I have to go through the API documentation again (although most logging frameworks are pretty much the same). But it adds to the cost of learning.

Slf4j solves this problem.

What is the slf4j

Slf4j is an abstract set of logging interfaces that defines a unified specification and, in fact, does not provide a concrete implementation of its own. Create an empty project, pom.xml as follows

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

    <groupId>org.example</groupId>
    <artifactId>logtest</artifactId>
    <version>1.0 the SNAPSHOT</version>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.25</version>
        </dependency>
    </dependencies>

</project>
Copy the code

The test class:

import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class TestLog {
    @Test
    public void testLog(a) {
        Logger logger = LoggerFactory.getLogger(TestLog.class);
        logger.error("Yan"); }}Copy the code

Test method testLog() :

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

    <groupId>org.example</groupId>
    <artifactId>logtest</artifactId>
    <version>1.0 the SNAPSHOT</version>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.25</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
        </dependency>
    </dependencies>

</project>
Copy the code

Run the test case again

Let’s try multiple logging implementation frameworks together again

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

    <groupId>org.example</groupId>
    <artifactId>logtest</artifactId>
    <version>1.0 the SNAPSHOT</version>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.25</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>1.7.25</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.21</version>
        </dependency>
    </dependencies>
</project>
Copy the code

The running results are as follows:

The advantage of this design pattern, called facade pattern, is that whatever logging framework we use in the implementation layer, we use the org.slf4J.logger.java interface methods, shielding the differences between different logging frameworks and saving unnecessary learning costs.

How does SLF4J do this

Let’s take a look at the loggerFactory.getLogger (TestLog.class) in the test method

private final static void bind(a) {
        try {
            Set<URL> staticLoggerBinderPathSet = null;
            // skip check under android, see also
            // http://jira.qos.ch/browse/SLF4J-328
            if(! isAndroid()) { staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet(); reportMultipleBindingAmbiguity(staticLoggerBinderPathSet); }// the next line does the binding
            StaticLoggerBinder.getSingleton();
            INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
            reportActualBinding(staticLoggerBinderPathSet);
            fixSubstituteLoggers();
            replayEvents();
            // release all resources in SUBST_FACTORY
            SUBST_FACTORY.clear();
        } catch (NoClassDefFoundError ncde) {
            String msg = ncde.getMessage();
            if (messageContainsOrgSlf4jImplStaticLoggerBinder(msg)) {
                INITIALIZATION_STATE = NOP_FALLBACK_INITIALIZATION;
                Util.report("Failed to load class \"org.slf4j.impl.StaticLoggerBinder\".");
                Util.report("Defaulting to no-operation (NOP) logger implementation");
                Util.report("See " + NO_STATICLOGGERBINDER_URL + " for further details.");
            } else {
                failedBinding(ncde);
                throwncde; }}catch (java.lang.NoSuchMethodError nsme) {
            String msg = nsme.getMessage();
            if(msg ! =null && msg.contains("org.slf4j.impl.StaticLoggerBinder.getSingleton()")) {
                INITIALIZATION_STATE = FAILED_INITIALIZATION;
                Util.report("slf4j-api 1.6.x (or later) is incompatible with this binding.");
                Util.report("Your binding is version 1.5.5 or earlier.");
                Util.report("Upgrade your binding to version 1.6.x.");
            }
            throw nsme;
        } catch (Exception e) {
            failedBinding(e);
            throw new IllegalStateException("Unexpected initialization failure", e); }}Copy the code

See first line 7 findPossibleStaticLoggerBinderPathSet () method, it will get many times before StaticLoggerBinder, namely slf4j and real connection between the logging implementation link, since not only introduced a log implementation framework, So we get a Set. Click in and have a look

org/slf4j/impl/StaticLoggerBinder.class
StaticLoggerBinder.getSingleton()
NoClassDefFoundError

SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
Copy the code

If multiple log implementation framework is introduced, in reportMultipleBindingAmbiguity (staticLoggerBinderPathSet) this step will print out the following information:

SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in[jar: file: / C: / Users / 72037717 / m2 / repository/ch/qos/logback/logback - classic / 1.2.3 / logback - classic - 1.2.3. Jar! /org/slf4j/impl/StaticLoggerBinder.class] SLF4J: Found bindingin[jar: file: / C: / Users / 72037717 / m2 / repository/org/slf4j/slf4j - simple / 1.7.25 / slf4j - simple - 1.7.25. Jar! /org/slf4j/impl/StaticLoggerBinder.class] SLF4J: Found bindingin[jar: file: / C: / Users / 72037717 / m2 / repository/org/slf4j/slf4j - log4j12 1.7.21 / slf4j - log4j12-1.7.21. Jar! /org/slf4j/impl/StaticLoggerBinder.class] SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
Copy the code

In the next * * reportActualBinding (staticLoggerBinderPathSet) * * will determine the real use of the logging implementation framework

Log4j in SLF4J concrete implementation

Log4j is not quite the same as Logback. Since Apache Log4j itself is not considered compatible with implementing the SLF4J framework, the slf4J-log4j12 package appears as an adapter connecting the two. This is a typical adapter pattern.