SLF4J is a Simple Logging Facade for Java, which is a facade (appearance) interface or an abstraction of various logging frameworks, such as java.util.logging, logback, log4j, etc. with this product, we can not care about the specific implementation, that is to say, we can switch the logging framework at any time.
The latest version of slf4j 1.8.0-beta2 is used here
Easy to use
Sample code https://github.com/minorpoet/logging-slf4j
- Add dependency
dependencies { compile group: 'org.slf4j', name: 'slf4j-api', version: '1.8.0-beta2' }
- Use
package pri.holysu.logging.sl4j; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class HelloWorld { public static void main(String[] args) { Logger logger = LoggerFactory.getLogger(HelloWorld.class); logger.info("hello world"); } }
-
Function
slf4j-nobind
It is found that the error is reported, indicating that we have not found the implementation of slf4j
How? Plus one
Add a logback to build.gradle dependency
dependencies { compile group: 'org.slf4j', name: 'slf4j-api', version: '1.8.0-beta2' compile group: 'ch.qos.logback', name: 'logback-classic', version: '1.3.0-alpha4' }
Then run it again and find it's ok
slf4j-logback
However, I haven't made any settings. How can I choose the log framework of logback?
Let's see how to use it. Logger logger = LoggerFactory.getLogger(HelloWorld.class);
The static method of this log factory org.slf4j.LoggerFactory.getLogger
public static Logger getLogger(Class<?> clazz) { // Get Logger by class name Logger logger = getLogger(clazz.getName()); // If "check log name matching" is set on in the configuration file, an error will be reported when the matching fails 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; }
Take a look at getLogger
public static Logger getLogger(String name) { // Get log factory ILoggerFactory iLoggerFactory = getILoggerFactory(); return iLoggerFactory.getLogger(name); } public static ILoggerFactory getILoggerFactory() { // Get the log factory from the log framework provider (Implementation) return getProvider().getLoggerFactory(); }
Find here, getProvider. Literally, it should be the place we are looking for
static SLF4JServiceProvider getProvider() { // When the status is uninitialized, this is the double checked lock mode if (INITIALIZATION_STATE == UNINITIALIZED) { synchronized (LoggerFactory.class) { if (INITIALIZATION_STATE == UNINITIALIZED) { // Initializ ing with status set to INITIALIZATION_STATE = ONGOING_INITIALIZATION; // Execute initialization logic performInitialization(); } } } switch (INITIALIZATION_STATE) { case SUCCESSFUL_INITIALIZATION: return PROVIDER; case NOP_FALLBACK_INITIALIZATION: return NOP_FALLBACK_FACTORY; case FAILED_INITIALIZATION: throw new IllegalStateException(UNSUCCESSFUL_INIT_MSG); case ONGOING_INITIALIZATION: // support re-entrant behavior. // See also http://jira.qos.ch/browse/SLF4J-97 return SUBST_PROVIDER; } throw new IllegalStateException("Unreachable code"); }
Specific initialization process:
private final static void performInitialization() { // Binding log framework, this is the core bind(); if (INITIALIZATION_STATE == SUCCESSFUL_INITIALIZATION) { // After initialization, check the health of the log framework versionSanityCheck(); } } private final static void bind() { try { // Implementation of loading slf4j List<SLF4JServiceProvider> providersList = findServiceProviders(); reportMultipleBindingAmbiguity(providersList); if (providersList != null && !providersList.isEmpty()) { // Get only the first implementation PROVIDER = providersList.get(0); PROVIDER.initialize(); INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION; reportActualBinding(providersList); fixSubstituteLoggers(); replayEvents(); // release all resources in SUBST_FACTORY SUBST_PROVIDER.getSubstituteLoggerFactory().clear(); } else { // When the logback package was not introduced at the beginning, this error was reported INITIALIZATION_STATE = NOP_FALLBACK_INITIALIZATION; Util.report("No SLF4J providers were found."); Util.report("Defaulting to no-operation (NOP) logger implementation"); Util.report("See " + NO_PROVIDERS_URL + " for further details."); Set<URL> staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet(); reportIgnoredStaticLoggerBinders(staticLoggerBinderPathSet); } } catch (Exception e) { failedBinding(e); throw new IllegalStateException("Unexpected initialization failure", e); } }
The specific logic of findServiceProviders is very clear, and the specified type is loaded through class loader
private static List<SLF4JServiceProvider> findServiceProviders() { // Load the tool class through the ServiceLoader interface, and load the class of slf4jsserviceprovider type ServiceLoader<SLF4JServiceProvider> serviceLoader = ServiceLoader.load(SLF4JServiceProvider.class); // Add to the list, and through PROVIDER = providersList.get(0) in bind() above, we can see that even if there are multiple implementation classes, only the first one loaded will be used List<SLF4JServiceProvider> providerList = new ArrayList<SLF4JServiceProvider>(); for (SLF4JServiceProvider provider : serviceLoader) { providerList.add(provider); } return providerList; } // Load a class of type service through the classloader of the current thread public static <S> ServiceLoader<S> load(Class<S> service) { ClassLoader cl = Thread.currentThread().getContextClassLoader(); return ServiceLoader.load(service, cl); }
org.slf4j.LoggerFactory.getLogger avoids the tedious things like ILogger logger = new LoggerImp(); or bean declaration. As long as the implementation exists in the classpath, we only need one format when we need to record logs, regardless of the implementation differences of various log frameworks. This is the charm of the specification~
If you use lombok, it will be more concise, such as
package pri.holysu.logging.sl4j; import lombok.extern.slf4j.Slf4j; @Slf4j public class LombokSlf4j { public static void main(String[] args) { log.info("logger feteched from lombok"); } }
Among them, log is added automatically by lombok, isn't it convenient?