阿里Java开发手册日志规约的思考和理解(一)

待分析的日志规约

  1. 【强制】应用中不可直接使用日志系统(Log4j、Logback)中的API,而应依赖使用日志框架SLF4J中的API,使用门面模式的日志框架,有利于维护和各个类的日志处理方式统一。
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    private static final Logger logger = LoggerFactory.getLogger(Abc.class);

SLF4J如何保证统一的日志处理方式

SLF4J中的Logger是一个接口即完全抽象,所以SLF4J(Simple Logging Facade For Java)是一个门面模式的日志框架,而不是具体的日志系统。而抽象可以带来很多好处,比如:
1、具体项目中根据需求来使用哪种日志系统,也有可能项目在使用过程中因为一些特定的日志功能会切换到其它日志系统。
2、作为库的输出方,方便客户在整个项目中实现统一的日志处理方式。
3、作为项目的开发者,如果使用抽象,也能实现在自己项目中编写的各个类的日志处理统一。

从SLF4J的全称来看,它是一种外观模式。为什么这么说呢,因为使用了统一的Logger接口去操纵日志接口,避免了对日志系统各个模块单独的调用。

本文的讲解用的日志系统是log4j。那么使用slf4j和log4j,我们需要引入如下三个库。其中org.slf4j是抽象库,完全不依赖具体实现库。slf4j-log4j12为log4j和slf4j之间的桥梁。log4j为具体日志系统。
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.25</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.25</version>
</dependency>
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.15</version>
</dependency>

那么具体看看SLF4J是如何做到的呢?在具体查看源代码前,或许会有如下跟我一样的想法。
1、SLF4J的logger需要直接或间接指向具体日志系统的logger。
2、LoggerFactory是如何应用工厂模式的。
3、LoggerFactory.getLogger如何保证不会创建来至多个日志系统的logger,各个日志系统的logger是否都由同一工厂创建。
4、由于slf4j库不会引用具体的日志系统,那么LoggerFactory.getLogger是如何创建出不同日志系统的logger的呢。
5、不同日志系统往往会由不同公司开发,所以日志接口会与SLF4J存在偏差。

从查看源代码找出了我的相关顾虑:
1、LoggerFactory没有继承额外的类和实现接口,所以LoggerFactory.getLogger是一个工厂方法,且是简单工厂。
2、调试中LoggerFactory.getLogger调用了bind方法,且StaticLoggerBinder.getSingleton()会获得针对具体日志系统的logger工厂类。这完全得益于JVM的类加载机制,JVM会加载仅且一个StaticLoggerBinder类,且这个类在slf4j-log4j12中。可见JVM类加载的威力。

private static final void bind() {
    String msg;
    try {
      Set e = null;
      if(!isAndroid()) {
        e = findPossibleStaticLoggerBinderPathSet();
        reportMultipleBindingAmbiguity(e);
      }

      StaticLoggerBinder.getSingleton();
      INITIALIZATION_STATE = 3;
      reportActualBinding(e);
      fixSubstituteLoggers();
      replayEvents();
      SUBST_FACTORY.clear();
    } catch (NoClassDefFoundError var2) {

3、为了适配SLF4J logger接口,StaticLoggerBinder会创建具体日志系统的适配器。这里用到了适配器模式。

如何处理项目依赖的第三方库中依赖的各种日志系统

SLF4J中使用了桥接模式来转换各种日志系统到对SLF4J抽象的使用,以便在项目中达到统一日志系统的目的。同时它也能迅速把已有项目切换到对SLF4J抽象的轨道中来。详见:https://www.slf4j.org/legacy.html

为了转换第三方库依赖的日志系统到对SLF4J抽象,以Log4J为例,如下:
<dependency>
    <groupId>me.yixt</groupId>
    <<artifactId>learning</artifactId>
    <version>1.0-SNAPSHOT</version>
    <exclusions>
        <exclusion>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>log4j-over-slf4j</artifactId>
    <version>1.7.25</version>
</dependency>

注意使用时需要先移除对log4j的引用,然后再引用log4j-over-slf4j,所以log4j-over-slf4j包含了log4j的接口。

为什么说它是桥接模式呢?因为原来引用的日志系统可以和桥接后的日志系统独立变化。
在log4j-over-slf4j中,Logger会继承Category,而Category正好组合了对SLF4J Logger的引用。

public class Category {
  private static final String CATEGORY_FQCN = Category.class.getName();
  private String name;
  protected Logger slf4jLogger;
  private LocationAwareLogger locationAwareLogger;
  private static Marker FATAL_MARKER = MarkerFactory.getMarker("FATAL");

  Category(String name) {
    this.name = name;
    this.slf4jLogger = LoggerFactory.getLogger(name);
    if(this.slf4jLogger instanceof LocationAwareLogger) {
      this.locationAwareLogger = (LocationAwareLogger)this.slf4jLogger;
    }

  }

遗留问题

1、若项目中使用了SLF4J,且有多个日志系统的存在,因JVM可能加载了不是期望的日志系统,如何处理这种问题。
2、是否可以想出不同于SLF4J的抽象日志框架。

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

推荐阅读更多精彩内容