日志框架那点破事——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/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,546评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,224评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,911评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,737评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,753评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,598评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,338评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,249评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,696评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,888评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,013评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,731评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,348评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,929评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,048评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,203评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,960评论 2 355