美文网首页log4j
SLF4J的实现原理

SLF4J的实现原理

作者: 晴天哥_王志 | 来源:发表于2021-02-10 15:53 被阅读0次

    系列

    SLF4J介绍

     slf4j,simple logging facade for java的缩写,翻译为java的简单日志外观。slf4j是一个开源项目,它提供我们一个一致的API来使用不同的日志框架(如java.util.logging,logback,log4j等)。slf4j使用户可以在运行时嵌入他们想使用的日志框架。从名字中可以看出,它其实使用的是facade设计模式来实现的。

     使用slf4j,只有一个强制性的依赖,就是slf4j-api-x.x.x.jar,我们在编写代码的时候,只会使用这个jar包里的API,应用程序在运行时去类路径下查找绑定的具体日志框架,并使用该绑定的日志框架进行实际的日志操作,如果在应用程序的类路径下面没有找到合适的绑定的话,slf4j默认使用一个没有任何操作的实现。

     slf4j bindings(以slf4j-api的1.7.7版本为例)slf4j为不同的日志框架提供了不同的绑定,在slf4j提供的分发包里包含了一些绑定。

    • slf4j-log4j12-x.x.x.jar是使用org.apache.log4j.Logger提供的驱动。
    • slf4j-jdk14-x.x.x.jar是使用java.util.logging提供的驱动。
    • slf4j-simple-x.x.x.jar直接绑定System.err。
    • slf4j-jcl-x.x.x.jar是使用commons-logging提供的驱动。
    • logback-classic-x.x.x.jar是使用logback提供的驱动。

    SLF4J基本用法

    <dependencies>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.30</version>
        </dependency>
    
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.30</version>
        </dependency>
    </dependencies>
    
    • 使用slf4j需要依赖slf4j-api和其他日志框架(这里以slf4j-log4j12-x.x.x.jar)框架为例。
    public class Demo {
        public static void main(String[] args) {
            LoggerFactory.getLogger(Demo.class).error("+++++++");
        }
    }
    
    • 使用slf4j通过LoggerFactory.getLogger的api获取Logger对象进而打印日志即可。

    SLF4J核心逻辑介绍

    SLF4J的原理图

    • 应用application通过slf4j桥接器桥接不同的日志框架,通过slf4j api进行日志打印的操作。
    • 从本质上来讲slf4j主要是通过不同的桥接器对接了不同的日志框架,具体的日志打印仍然由日志框架去实现。

    SLF4J的工作流程

     SLF4J通过slf4j-api访问桥接器slf4j-log4j12生成LoggerFactory对象,通过LoggerFactory对象的getLogger返回桥接器的LoggerAdapter对象,通过LoggerAdapter的info方法输出日志,整体的执行过程如下图。

    slf4j时序图原理.png
    • SLF4J的工作流程核心包含获取Logger对象流程和打印日志流程。
    • SLFTJ通过slf4j-api调用log4j桥接器slf4j-log4j12的StaticLoggerBinder生成LoggerFactory对象。
    • SLFTJ通过slf4j-api的getLogger方法访问桥接器生产Logger对象。
    • SLFTJ通过桥接器的Logger对象实现日志的格式化和打印。

    SLF4J的流程源码

    public final class LoggerFactory {
        public static ILoggerFactory getILoggerFactory() {
    
            if (INITIALIZATION_STATE == UNINITIALIZED) {
                synchronized (LoggerFactory.class) {
                    if (INITIALIZATION_STATE == UNINITIALIZED) {
                        INITIALIZATION_STATE = ONGOING_INITIALIZATION;
                        // 执行LoggerFactory的初始化过程
                        performInitialization();
                    }
                }
            }
            switch (INITIALIZATION_STATE) {
            case SUCCESSFUL_INITIALIZATION:
                // 返回获取的LoggerFactory对象
                return StaticLoggerBinder.getSingleton().getLoggerFactory();
            }
            throw new IllegalStateException("Unreachable code");
        }
    }
    
    • getILoggerFactory通过performInitialization完成桥接器LoggerFactory的初始化过程。
    • 通过桥接器的StaticLoggerBinder.getSingleton().getLoggerFactory()返回LoggerFactory对象。

    public final class LoggerFactory {
    
        private final static void performInitialization() {
            // 核心的bind过程
            bind();
        }
    
        private final static void bind() {
            try {
                Set<URL> staticLoggerBinderPathSet = null;
    
                if (!isAndroid()) {
                    // 查询所有的StaticLoggerBinder的类
                    staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
                    reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);
                }
                // 通过静态方法调用构造LoggerFactory对象
                StaticLoggerBinder.getSingleton();
    
            } catch (Exception e) {
            } 
        }
    
        static Set<URL> findPossibleStaticLoggerBinderPathSet() {
            Set<URL> staticLoggerBinderPathSet = new LinkedHashSet<URL>();
            try {
                ClassLoader loggerFactoryClassLoader = LoggerFactory.class.getClassLoader();
                Enumeration<URL> paths;
                // STATIC_LOGGER_BINDER_PATH = "org/slf4j/impl/StaticLoggerBinder.class"
                if (loggerFactoryClassLoader == null) {
                    paths = ClassLoader.getSystemResources(STATIC_LOGGER_BINDER_PATH);
                } else {
                    paths = loggerFactoryClassLoader.getResources(STATIC_LOGGER_BINDER_PATH);
                }
                // 查找所有的StaticLoggerBinder类对象
                while (paths.hasMoreElements()) {
                    URL path = paths.nextElement();
                    staticLoggerBinderPathSet.add(path);
                }
            } catch (IOException ioe) {
            }
            return staticLoggerBinderPathSet;
        }
    
    • performInitialization的核心逻辑是初始化slf4j桥接器的LoggerFactory对象。
    • findPossibleStaticLoggerBinderPathSet加载所有桥接器的StaticLoggerBinder的类对象。
    • slf4j的必须配置桥接器,存在多个桥接器的情况下会选择其中一个桥接器进行工作。
    • slf4j的核心依赖slf4j-api和slf4j-log412桥接器,两者必须同时使用。

    SLF4J设计模式

    public class StaticLoggerBinder implements LoggerFactoryBinder {
    
      private static final StaticLoggerBinder SINGLETON = new StaticLoggerBinder();
    
      public static final StaticLoggerBinder getSingleton() {
        return SINGLETON;
      }
    
      private StaticLoggerBinder() {
        loggerFactory = new Log4jLoggerFactory();
        try {
          @SuppressWarnings("unused")
          Level level = Level.TRACE;
        } catch (NoSuchFieldError nsfe) {
        }
      }
    
      public ILoggerFactory getLoggerFactory() {
        return loggerFactory;
      }
    }
    
    • StaticLoggerBinder作为适配器的核心类,核心思想是通过单例模式返回面向接口的对象ILoggerFactory。
    • 其中个人认为可以参考的点在于单例的设计模式和面向接口的编程模式,SLF4J-API定义ILoggerFactory接口,各适配类具体实现。
    public interface ILoggerFactory {
      public Logger getLogger(String name);
    }
    
    
    public class Log4jLoggerFactory implements ILoggerFactory {
    
      ConcurrentMap<String, Logger> loggerMap;
    
      public Log4jLoggerFactory() {
        loggerMap = new ConcurrentHashMap<String, Logger>();
      }
    
      public Logger getLogger(String name) {
        Logger slf4jLogger = loggerMap.get(name);
        if (slf4jLogger != null) {
          return slf4jLogger;
        } else {
          org.apache.log4j.Logger log4jLogger;
          if(name.equalsIgnoreCase(Logger.ROOT_LOGGER_NAME))
            log4jLogger = LogManager.getRootLogger();
          else
            log4jLogger = LogManager.getLogger(name);
    
          Logger newInstance = new Log4jLoggerAdapter(log4jLogger);
          Logger oldInstance = loggerMap.putIfAbsent(name, newInstance);
          return oldInstance == null ? newInstance : oldInstance;
        }
      }
    }
    
    • Log4jLoggerFactory实现接口ILoggerFactory并提供getLogger来返回Logger对象。
    • getLogger内部通过适配不同日志框架的获取Logger的方法,以slf4j-log4j12为例适配log4j,内部封装LogManager.getLogger(name)来获取Logger对象。

    相关文章

      网友评论

        本文标题:SLF4J的实现原理

        本文链接:https://www.haomeiwen.com/subject/yjdxxltx.html