日志框架那点破事——slf4j

背景就不介绍了,总之是那些over,to,slf4j+log4j,slf4j+log4j2,commons-logging,一言以蔽之,何等乱七八糟!

看资料的时候觉得两张图不错,扒过来的,链接在最后参考文章中


1.png
2.png

slf4j

那先来看slf4j,slf4j就是个日志的接口,没有提供任何实现

Logger

Logger类是入口方法,是个interface,具体的日志实现框架会实现这些接口

ILoggerFactory

获取Logger的接口类,具体的日志实现框架需实现getLogger()方法

LoggerFactory

静态绑定具体日志实现框架,提过Logger对象

NOPLogger

slf4j自带的Logger的实现类

流程

流程上都是先通过LoggerFactory获取Logger,然后通过Logger记录日志

获取Logger

通过静态方法getLogger(Class clazz)获取

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;
}

getLogger(Class clazz)再通过getLogger(String name)使用ILoggerFactory.getILoggerFactory()方法获取ILoggerFactory实例,就如上面说的,ILoggerFactory是用来获取Logger对象的接口类

public static ILoggerFactory getILoggerFactory() {
    if (INITIALIZATION_STATE == UNINITIALIZED) {
        synchronized (LoggerFactory.class) {
            if (INITIALIZATION_STATE == UNINITIALIZED) {
                INITIALIZATION_STATE = ONGOING_INITIALIZATION;
                performInitialization();
            }
        }
    }
    switch (INITIALIZATION_STATE) {
    case SUCCESSFUL_INITIALIZATION:
        return StaticLoggerBinder.getSingleton().getLoggerFactory();
    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_FACTORY;
    }
    throw new IllegalStateException("Unreachable code");
}

这里的初始化有5个状态
UNINITIALIZED 未初始化
ONGOING_INITIALIZATION 正在被初始化
FAILED_INITIALIZATION 初始化失败
SUCCESSFUL_INITIALIZATION 初始化成功
NOP_FALLBACK_INITIALIZATION 空状态,表示无底层日志实现框架时的结果

  • 注:注意到了一个细节,这里用了一个DCL,理论上DCL会有JVM指令重排的问题(参考文章一),再看了一眼INITIALIZATION_STATE的定义,hh,多虑了。

    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;
    

    }

INITIALIZATION_STATE初始化值是UNINITIALIZED,这里标记为ONGOING_INITIALIZATION后,进入bind流程。findPossibleStaticLoggerBinderPathSet()方法寻找StaticLoggerBinder.class 文件,StaticLoggerBinder类实现了LoggerFactoryBinder接口的getLoggerFactory(),pom未添加日志实现依赖包的话是找不到StaticLoggerBinder.class 文件的

当找不到StaticLoggerBinder.class 文件时,则走到NoClassDefFoundError异常,INITIALIZATION_STATE = NOP_FALLBACK_INITIALIZATION,则会用LoggerFactory下的NOPLoggerFactory对象,而NOPLoggerFactory基本是个空实现(所以最后输出也是空)

  • 注:StaticLoggerBinder.class 在 slf-api.jar 中是不存在的,并没有打包进去,但是在slf-api-source.jar 是存在 org.slf4j.impl.StaticLoggerBinder这个类的,因为需要保证 slf4j 本身编译不报错

真正实现在log4j-api-2.x上,通过log4j-slf4j-impl-2.x把slf4j桥接到log4j2上(我自己的项目),第三方日志实现框架提供logger对象和log.trace()等等方法

参考文章:
1、https://www.infoq.cn/article/double-checked-locking-with-delay-initialization
2、https://www.itcodemonkey.com/article/1725.html
3、http://techblog.ppdai.com/2018/07/04/20180704/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容