This is the third day of my participation in the August More text Challenge. For details, see: August More Text Challenge

Function is introduced

JCL stands for Jakarta Commons Logging, which is Apache’s generic Logging API, while the Spring-JCL module basically means Spring’s own Logging API, but it uses Apache’s underlying mechanism.

A concrete analysis

log.java

Only the log interface is defined, and the implementation is dynamically found at runtime. Interestingly, for backward compatibility, the Spring team has kept the package name the same as before, rather than naming it after Spring.

// Note the package name here
package org.apache.commons.logging;

public interface Log {

   /** Whether to enable a certain level of logs */
   boolean isFatalEnabled(a);

   boolean isErrorEnabled(a);

   boolean isWarnEnabled(a);

   boolean isInfoEnabled(a);

   boolean isDebugEnabled(a);

   boolean isTraceEnabled(a);

    /** Provides different levels of logging methods */
   void fatal(Object message);

   void fatal(Object message, Throwable t);

   void error(Object message);

   void error(Object message, Throwable t);

   void warn(Object message);

   void warn(Object message, Throwable t);

   void info(Object message);

   void info(Object message, Throwable t);

   void debug(Object message);

   void debug(Object message, Throwable t);

   void trace(Object message);

   void trace(Object message, Throwable t);

Copy the code

LogFactor.java

The implementation is done using the LogFactory, and performance issues are also described, and the following logging is recommended

    if (log.isDebugEnabled()) {
        ... do something expensive ...
        log.debug(theResult);
    }
Copy the code

Ok, let’s move on to the implementation of the LogFactory class. The jCL-Ovre-SLF4J bridge tool provides two ways to implement the LogFactory class.

public abstract class LogFactory {

   /** instantiate */ based on the class name
   public static Log getLog(Class
        clazz) {
      return getLog(clazz.getName());
   }

    /** Instantiate */ according to the string
   public static Log getLog(String name) {
      return LogAdapter.createLog(name);
   }
Copy the code

However, the class is abstract, which is ultimately beneficial for spi mechanism to instantiate, that is, an abstract factory class LogFactory is used to load and parse configuration files and instantiate the logging framework.

LogAdapter.java

The LogAdapter is the core part, which is responsible for creating and instantiating the above log interface. When instantiating the LogAdapter, it will automatically call class. forName to find the Class, load it into the JVM, and execute the corresponding static code block. Then load the LogAdapter using the ClassLoader and use the ClassNotFoundException to determine which logging framework to use. The default is java.util.logging, as shown below


static {
   if (isPresent(LOG4J_SPI)) {
      if (isPresent(LOG4J_SLF4J_PROVIDER) && isPresent(SLF4J_SPI)) {
         logApi = LogApi.SLF4J_LAL;
      }
      else{ logApi = LogApi.LOG4J; }}else if (isPresent(SLF4J_SPI)) {
      logApi = LogApi.SLF4J_LAL;
   }
   else if (isPresent(SLF4J_API)) {
      logApi = LogApi.SLF4J;
   }
   else{ logApi = LogApi.JUL; }}private static boolean isPresent(String className) {
   try {
      Class.forName(className, false, LogAdapter.class.getClassLoader());
      return true;
   }
   catch (ClassNotFoundException ex) {
      return false; }}Copy the code

So how does it determine which implementation class to use? Let’s go ahead and analyze

// Created by an inner class in the class
public static Log createLog(String name) {
   switch (logApi) {
      case LOG4J:
      LoggerContextFactory finds ContextSelector based on LoggerContextFactory
      ThreadLocal
      
         return Log4jAdapter.createLog(name);
      case SLF4J_LAL:
      // Get the LocationAwareLogger from the singleton mode and then reinstantiate Slf4jLog
         return Slf4jAdapter.createLocationAwareLog(name);
      case SLF4J:
      // Instantiate the LoggerFactory from slF4J
         return Slf4jAdapter.createLog(name);
      default:
      // Instantiate Logger subclass RootLogger according to JDK Global LogManager
         return JavaUtilAdapter.createLog(name);
   }
Copy the code

conclusion

🆗, the spring-JCL module is resolved. In general, the knowledge points are as follows

  • Design pattern: Adapter pattern

Then delegate logging operations to the specific logging framework in the adaptation class. This function enables different interfaces to communicate

public interface Log {}
private static class Log4jLog implements Log.Serializable {}
private static class Slf4jLog<T extends Logger> implements Log.Serializable {}
private static class JavaUtilLog implements Log.Serializable {}

public abstract class LogFactory {}
public class LogFactoryService extends LogFactory {}

final class LogAdapter {}
Copy the code
  • Design patterns: Policy patterns

It’s not exactly a strategic model, but it’s an idea. The main use of the class name to find the corresponding context, as the policy changes, and return the parent class, to solve the condition of too many branches

public static Log createLog(String name) {
   switch (logApi) {
      case LOG4J:
         return Log4jAdapter.createLog(name);
      case SLF4J_LAL:
         return Slf4jAdapter.createLocationAwareLog(name);
      case SLF4J:
         return Slf4jAdapter.createLog(name);
      default:
         return JavaUtilAdapter.createLog(name);
   }
Copy the code
  • Technical point: ThreadLocal

Use ThreadLocal

in LoggerContext class to get the LoggerContext object

  • Dependency: Rule of least

This module only introduces two apis, achieves the minimum principle of introduction dependency, and the corresponding description information, which is worth the author to learn

description = "Spring Commons Logging Bridge"

dependencies {
   optional("org.apache.logging.log4j:log4j-api")
   optional("org.slf4j:slf4j-api")}Copy the code