复杂Spring项目中SLF4J最佳使用姿势

一、Java日志体系概述

图1-1 Java日志体系概况

图1-1 展示了Java日志体系的三个主要部分:

  1. 日志门面接口(SLF4J、JCL)
  2. 一系列绑定和桥接
  3. 具体的日志实现

先来两个例子:

  • Spring项目中通过Commons Logging直接使用log4j


    图1-2 通过Commons Logging使用log4j

    本例中由于spring-core中原生依赖commons-logging,所以只需要添加log4j和log4j的配置文件就能work(当然引入其它依赖还会有解冲突的工作要做)。

  • Spring项目中通过SLF4J使用log4j


    图1-3 Spring中通过SLF4J使用log4j

    此例中比之前多引入了3个jar包并且还排除了commons-logging依赖。如果对此有疑问,请看后面的详细解说。

二、为什么要使用SLF4J

图2-1 列举几点简单的原因

三、SLF4J的最佳实践

现在越来越多项目使用SLF4J为什么要使用SLF4j)作为一个日志门面,而不是使用古老的Commons Logging。这给复杂的Maven构建带来了新的挑战,因为SLF4J仅当依赖被正确配置时才能正常工作。为了搞清楚为什么如此,让我们来看看组成SLF4J中不同的组件。

  • slf4j-api 包含了SLF4J API,比如,使用SLF4J的一个应用或者库直接依赖的所有classes。
  • 一系列绑定,它们(slf4j-log4j12,slf4j-jdk14和slf4j-jcl)基于一个已存在的日志框架来实现了SLF4J API,或者为SLF4J开发原生实现(slf4j-nop和slf4j-simple)。
    绑定的原理是因为slf4j-api中的LogFactory通过StaticLoggerBinder.getSingleton()获取具体实现logger,一个绑定包实现了org/slf4j/impl/StaticLoggerBinder.class,所以它们在编译时刻就绑定在一起,而绑定包中的StaticLoggerBinder类会绑定对应的实现。这部分看看源码就一目了然了。
  • 一系列桥接,它们把已存在的日志门面适配到SLF4J(jul-to-slf4j)。或者仿效已存在的日志门面或者实现(jcl-over-slf4j和log4j-over-slf4j)。
    桥接的原理是把对应的门面日志接口重新实现了一遍,包名、类名、接口都一样,只是具体实现它委托给SLF4J了。

SLF4J要在一个Maven构建的项目中正确工作,下列条件必须满足:

  1. 项目必须依赖slf4j-api。如果项目本身使用SLF4J,而且并不依赖任何绑定或者桥接,那么这应该是一个直接依赖。如果SLF4J被项目中的一个或者多个依赖使用,但是并不被项目本身使用,那么可以优先让Maven的依赖管理(dependency management)系统把它作为一个传递依赖给包含进来。
  2. 如果一个项目生成一个可执行的构建(有Main-Class的JAR,WAR,EAR或者二进制distribution),那么它必须有一个依赖,并且和仅一个绑定。一个binding在运行时总是必须的,但是多个bindings将会导致不可预期的行为。
  3. 项目可以有任意个SLF4J桥接依赖,这不包括被binding使用的API的桥接。比如,如果slf4j-log4j12被作为一个binding,那么项目不能依赖log4j-over-slf4j。否则应用可能因为无限循环而崩溃。
  4. 如果项目依赖一个桥接,emulate一个已存在的日志API,那么它一定不能同时依赖你这个API。比如,如果jcl-over-slf4j被使用,那么项目一定不能依赖commons-logging。否则行为将不可预测。
  5. 依赖不能混用SLF4J 1.4.x的构建和1.5.x的构建,因为它们相互不兼容。
    应当注意规则2仅适用可执行构建。一个生成一个库构建的项目不应该依赖任何SLF4J binding,除了在test scope。原因是在编译或者运行scope依赖一个指定的SLF4J binding将会在下游项目中impose一个特殊的日志实现。在每个库都遵循这个实践的完美世界里,将会非常容易地验证以上5个条件:在从SLF4J 1.5.x到每一个生成可执行构建的Maven项目中,期望的binding(任何必要的桥接)上简单添加一个依赖就足够了。
    但是世界并不完美,并且有很多第三方库并没有依赖特定的SLF4J bindings和日志实现。如果一个项目开始依赖这种库的依赖,如果没有采取对策,事情很容易失控。对于有很多依赖的项目这尤其明显,它几乎总会让以上5条规则不再被满足。

Maven并没有一些必要的特性,这些特性强制这些条件实现校验,并且强制它们具备一些准则和人工干预。另一方面,当系统地应用时有一个非常简单和高效的策略:

  • 确保在你控制下的项目中,总是遵循上诉描述的策略。
  • 对于不遵循这些最佳实践的第三方库,在对应的依赖声明上排除依赖来移除对SLF4J binding上的传递依赖。需要注意,如果在一个库在多模块Maven项目的多种模块中被使用,那么在父POM中的dependency management section里声明这些排除会很方便,这就不需要再依赖中重复排除。

四、SLF4J官方说明

  1. SLF4J与每种具体日志框架的绑定规则。


    图4-1 slf4j官方逻辑图
  2. 把Commons logging,log4j和java.util.logging桥接到SLF4J,底层使用logback的case。


    图4-2 桥接到SLF4J,并使用logback日志框架

    比如我们项目中依赖了spring-core,因为它原生依赖commons-logging,所以需要用jcl-over-slf4j.jar桥接包来代替commons-logging把具体实现委托给slf4j。jcl-over-slf4j和commons-logging拥有相同的包名、类名、接口,所以项目中要排除掉commons-logging。

  3. 把commons-logging和java.util.logging桥接到SLF4J,底层用log4j


    图4-3 桥接到SLF4J,底层用log4j
  4. 把commons-logging和log4j桥接到SLF4J,底层用java.util.logging


    图4-4 桥接到SLF4J,底层用java.util.logging

总结:
在Spring中,我们通常会依赖很多jar,它们使用不同的日志门面接口或者不同的日志框架,只要遵循第三节中的5点规则,加上排除冲突的依赖就能正常使用日志啦。

附录,推荐排除冲突的两种方式:

  1. 如果有多个子模块依赖相同的包,那么放在<dependencyManagement> 中来管理:
    <dependencyManagement>
    <dependency>
    <groupid>com.acme</groupid>
    <artifactid>java-coffee-machine</artifactid>
    <version>7.5.194.3</version>
    <exclusions>
    <exclusion>
    <groupid>org.slf4j</groupid>
    <artifactid>slf4j-log4j12</artifactid>
    </exclusion>
    </exclusions>
    </dependency>
    </dependencyManagement>
  2. 在自己公司的maven仓库中放一个空包,scope=provided,这样可以全局排除依赖:
    <dependency>
    <groupId>commons-logging</groupId>
    <artifactId>commons-logging</artifactId>
    <version>${empty.version}</version>
    <scope>provided</scope>
    </dependency>

参考资料:

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

推荐阅读更多精彩内容