SLF4J is a log framework abstraction layer, which binds specific log frameworks, such as Log4J, Logback, Java Logging API, etc. SLF4J also has its own default implementation, but we still mainly use SLF4J as the abstraction layer of the log framework.
To use SLF4J, you must include a dependency on "org.slf4j:slf4j-api".
A simple review of the facade pattern
slf4j is a typical application of facade mode, so before talking about slf4j, let’s briefly review the facade mode.
Facade mode, whose core is that the communication with a subsystem must be carried out through a unified appearance object, making the subsystem easier to use. The structure of the storefront pattern is used to represent a picture:
The core of the facade mode is Facade, that is, the facade object, and the core of the facade object is several points:
Generally speaking, just reviewing the store mode is enough here, and you will start learning about SLF4J next.
Why do we use slf4j
Why do we use slf4j? For example:
We use logback in our own system
Our system uses A.jar, and the log system used in A.jar is log4j
Our system uses B.jar again, and the log system used in B.jar is slf4j-simple
In this way, our system has to support and maintain three log frameworks: logback, log4j, and slf4j-simple at the same time, which is very inconvenient.
The way to solve this problem is to introduce an adaptation layer, which determines which log system to use, and the caller only needs to do is to print the log without caring about how to print the log. slf4j or commons-logging is this adaptation layer, and slf4j is the object of this paper research.
From the above description, we must clearly know one thing: slf4j is just a logging standard, not a specific implementation of the logging system. It is very important to understand this sentence. Slf4j only mentions doing two things:
slf4j-simple and logback are both specific implementations of slf4j. log4j does not directly implement slf4j, but there is a special first-layer bridge slf4j-log4j12 to implement slf4j.
In order to better understand slf4j, we first look at the examples and then read the source code. I believe readers will have a deeper understanding of slf4j.
slf4j application example
As mentioned above, the direct/indirect implementation of slf4j includes slf4j-simple, logback, slf4j-log4j12. Let's first define a pom.xml and introduce the relevant jar package:
<!-- Original text: Cangjie in May http://www.cnblogs.com/xrq730/p/8619156.html --><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.xrq.log</groupId> <artifactId>log-test</artifactId> <version>1.0.0</version> <packaging>jar</packaging> <name>log-test</name> <url>http://maven.apache.org</url> <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>
Write a simple Java code:
@Testpublic void testSlf4j() { Logger logger = LoggerFactory.getLogger(Object.class); logger.error("123"); }Next, we first comment out lines 30 to 49 of pom.xml above, that is, we do not introduce any slf4j implementation class and run the Test method. Let's take a look at the output of the console as:
Seeing the output without any logs, this verifies our view: slf4j does not provide a specific implementation of logs, and only slf4j cannot print logs.
Then open the logback-classic annotation and run the Test method. Let's take a look at the output of the console as:
I saw that we just need to introduce a specific implementation class of slf4j, and we can use the log framework to output the log.
Finally, we do a test. We open all logs, introduce logback-classic, slf4j-simple, log4j, run the Test method, and the console output is:
The difference from the above is that you can output logs, but some alarm logs will be output, prompting us to introduce multiple slf4j implementations at the same time, and then select one of them as the log system we use.
From the example, we can draw an important conclusion, that is, the role of slf4j: as long as all code uses the facade object slf4j, we don’t need to care about its specific implementation. In the end, one specific implementation can be used in all places, and replacement and maintenance are very convenient.
slf4j implementation principle
I have looked at the slf4j example above, and I will study the implementation of slf4j below. We will only focus on the key code.
The usage of slf4j is a sentence that remains unchanged all year round, "Logger logger = LoggerFactory.getLogger(Object.class);", which shows that this is to use LoggerFactory to get the specific implementation of a Logger interface provided by slf4j. The getLogger method of LoggerFactory is implemented as:
public static Logger getLogger(Class<?> clazz) { Logger logger = getLogger(clazz.getName()); if (DETECT_LOGGER_NAME_MISMATCH) { Class<?> autoComputedCallingClass = Util.getCallingClass(); if (autoComputedCallingClass != null && nonMatchingClasses(clazz, autoComputedCallingClass)) { Util.report(String.format("Detected logger name mismatch. Given name: /"%s/"; computed name: /"%s/".", logger.getName(), autoComputedCallingClass.getName())); Util.report("See " + LOGGER_NAME_MISMATCH_URL + " for an explanation"); } } return logger;}Start with the code from line 2 and follow the bind() method to LoggerFactory:
private final static void bind() { 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); throw ncde; } } 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); }}Line 7 of this place is a key, look at the code:
static Set<URL> findPossibleStaticLoggerBinderPathSet() { // use Set instead of list in order to deal with bug #138 // LinkedHashSet appropriate here because it preserves insertion order // during iteration Set<URL> staticLoggerBinderPathSet = new LinkedHashSet<URL>(); try { ClassLoader loggerFactoryClassLoader = LoggerFactory.class.getClassLoader(); Enumeration<URL> paths; if (loggerFactoryClassLoader == null) { paths = ClassLoader.getSystemResources(STATIC_LOGGER_BINDER_PATH); } else { paths = loggerFactoryClassLoader.getResources(STATIC_LOGGER_BINDER_PATH); } while (paths.hasMoreElements()) { URL path = paths.nextElement(); staticLoggerBinderPathSet.add(path); } } catch (IOException ioe) { Util.report("Error getting resources from path", ioe); } return staticLoggerBinderPathSet;}The key point of this place is actually the code on line 12. When gettingLogger, you will go to classpath to find STATIC_LOGGER_BINDER_PATH. The STATIC_LOGGER_BINDER_PATH value is "org/slf4j/impl/StaticLoggerBinder.class", that is, all slf4j implementations, under the provided jar package path, there must be "org/slf4j/impl/StaticLoggerBinder.class". We can take a look:
We cannot avoid introducing multiple slf4j implementations in the system at the same time, so the receiving place is a Set. You should note that there will be warnings when the above part introduces logback, slf4j-simple, and log4j at the same time as the demonstration:
This is because there are three "org/slf4j/impl/StaticLoggerBinder.class" that exists. At this time, the reportMultipleBindingAmbiguity method console output statement:
private static void reportMultipleBindingAmbiguity(Set<URL> bindPathSet) { if (isAmbiguousStaticLoggerBinderPathSet(binderPathSet)) { Util.report("Class path contains multiple SLF4J bindings."); for (URL path : bindPathSet) { Util.report("Found binding in [" + path + "]"); } Util.report("See " + MULTIPLE_BINDINGS_URL + " for an explanation."); }}Then netizens may ask, what should I do if there are three "org/slf4j/impl/StaticLoggerBinder.class" at the same time? First of all, it is determined that this will not cause an error in startup, and secondly, during compilation, the compiler will select one of the StaticLoggerBinder.class for binding.
Finally, StaticLoggerBinder is relatively simple. Different StaticLoggerBinders have different implementations of getLoggerFactory. After getting ILoggerFactory, call getLogger and get the specific Logger. You can use Logger for log output.
The above is all the content of this article. I hope it will be helpful to everyone's learning and I hope everyone will support Wulin.com more.