日志SLF4J解惑

0x00 前言

做为后端开发,日志多是咱们最经常使用的功能之一了。平时你们也可能常常碰见日志冲突,常见的overflow报错,今天为你们详解一下,这其中的原理以及问题所在。本文涉及 jar 包有:log4j,log4j-over-slf4j,slf4j-api,slf4j-log4j12 等等。java

0x01 背景

不知道你们在平时开发中,是否常常碰见如下几个问题:后端

  • log4j,logback等等日志包冲突,而后再慢慢排除,不胜其烦。
  • 使用了 slf4j ,可是又遇到 StackOverflwError 错误。
  • 好不容易解决了StackOverflwError,可能又遇到了相似如图所示的错误:
  • ......

0x02 概述

什么是 SLF4J 呢?简而言之,他就是一个日志的门面,市场上的日志系统很是多, SLF4J 想作的一件事情,就是将这多种的日志系统包装起来,提供统一的 API 供调用,从而解决日志的列国争霸的状况。api

0x03 SLF4J 调用流程

在解决以上问题以前,让咱们先对日志系统作一个大概的理解。到底什么是 log4j,什么是 slf4j-api,为何有一个 slf4j-log4j12,又怎么有个log4j-over-slf4j,看上去头晕眼花,绕来绕去。先祭上一张官网图:app

相信你们都见过这张图,可是未必全都理解图上说的是什么意思。因此呢,咱们先不讲图,先看看最上面的一排文字,SLF4J bound to xxxx,xxxx总共涉及:

  1. null
  2. logback-classic
  3. log4j
  4. java.util.logging
  5. simple
  6. no-operation

第1个顾名思义,不绑定,就是没有日志实现。第5,6能够看出来,是 SLF4J 本身的实现,这里就忽略不讲。因此着重讲一下二、三、4。他们有个共同的特色,就是他们都是日志的真正实现库,他们和SLF4J没有关系,你可使用 logback 打印日志,也可使用 log4j 打日志。jvm

第一层, application 就是应用层。日志

看看图中第二层,通通都是 SLF4J API。那么这一层是干什么用的呢?这一层就是一个门面层,而和门面层息息相关的 jar 包是什么呢?对,就是图中的 slf4j-api.jar 。这个jar包中,提供了日志调用的全部接口,应用都是直接调用这个 jar 包中的接口进行日志调用。code

第三层有点不一样。第一列,无实现,第2,5,6都是原生的一个实现,而3,4都有一个适配层。这里说说 logback。logback 他为何不须要适配层呢,由于他就是按照了 SLF4J 接口去实现的一个日志库,至关于亲儿子,天然不须要适配层。因此,logback-classic.jar 和 logback-core.jar 就是 logback 的底层实现。而3,4就不一样了,他们的接口或多或少会有差别,调用方式也各不相同,因此,须要一个适配层。因此 slf4j-log412.jar 和 slf4j-jdk14.jar 包的做用就是一个适配,这里是一个桥接,应用经过 slf4j-api 的接口调用过来时,桥接类实际会调用其底层的实现,达到一个桥接的过程。因此,slf4j-log412.jar,slf4j-jdk14.jar,slf4j-simple.jar,slf4j-nop.jar 是同一类,就是把-右边的实现进行一个桥接。cdn

第四层,能够看到第2,3对应的实现,log4j.jar 和 jvm 就是最后的实现。blog

至此,作一个小总结:接口

  1. slf4j-api.jar 是上层的门面,里面提供接口供调用。
  2. logback-classic.jar, logback-core.jar, log4j.jar, 是同一类别,属于底层实现库。
  3. slf4j-log412.jar,slf4j-jdk14.jar,slf4j-simple.jar,slf4j-nop.jar,能够当作 slf4j-xxxx.jar,属于同一个类别,就是对 - 后面的库作一个桥接,更简单的理解,从左到右读:把 - 左边的调用用右边的库实现。

此时再看看上面那张图,是否已经所有理解了呢?

0x04 SLF4J 转换流程

若是看完上面,不以为 SLF4J 有什么好处,就来看看下面这张图:

那这张图又是什么意思呢?这就要说到 SLF4J 的一个强大之处了。设想一下如下一种状况:新建了一个工程,引入的第一个库使用的是 java.util.logging 日志库,引入的第二个库使用的是 log4j 日志库,而你本身的工程,老板规定,必需要用 logback 。你怎么办呢?这个时候, SLF4J 就出场了。他能帮你把全部日志归拢到你所指定的一种日志实现。就是说,他能够把 jul 日志实现转成 logback,还能把 log4j 实现转成 logback。那他是怎么作的呢?回过头来看图吧。

着重讲解左上角这一部分,其余的相似。

先看 application,这个是应用,能够看到,他也遇到了我说到的问题。他的依赖里面有使用 log4j 的,有用 commons logging 的,有用 java.util.logging 的。因此此时须要作一个替换,分别是经过 jcl-over-slf4j.jar 替换掉 commons-logging.jar,log4j-over-slf4j.jar 替换掉 log4j.jar, jul-to-slf4j.jar 包中安装 SLF4JBrindgeHandler 解决。替换掉以后,就把全部日志调用转接到 slf4j-api 上了,而后 api 接口再调用底层实现,图上是 logback。文中说的替换是什么意思呢?就是把原日志实现库排除掉,引入 xxx-over-slf4j.jar 。

那么,xxx-over-slf4j.jar 是什么原理呢?先给你们看这张图:

左边是 log4j.jar 的包结构,右边是 log4j-over-slf4j.jar 的包结构。发现猫腻了吗?他们的目录结构如出一辙!因此用 log4j-over-slf4j 能够替换掉 log4j!且编译不会出错。log4j-over-slf4j.jar 实现了基本上全部 log4j 会被调用的 api 接口。因此替换以后,不会报错,编译也能经过,而底层实现却全转到 slf4j 这里去了。这里就是一个狸猫换太子的把戏。

再看其余两个图,底层分别是 log4j, jvm 实现。都是讲其余库 over 一下到 slf4j 。

特别注意一下, logback 不须要转,为何?由于他是亲儿子。天生就带这些。

因此再小总结一下:

  • jcl-over-slf4j.jar, log4j-over-slf4j.jar, jul-to-slf4j.jar,这种形式相似 xxx-over-slf4j.jar 的,就是将 - 前的太子用 slf4j 的狸猫代替。而 xxx-to-slf4j.jar 比较特殊,这是由于 xxx 这个包没法被替换掉,好比 java.util.logging,系统的库,没法替换,因此只能采用别的手段。此类别的实现读者有兴趣能够去看看,本文再也不分析。

0x05 常见问题解决

StackOverflow 错误

为何会出现这个问题呢?控制台输出上通常会比较清楚,就是你既使用了桥接库,又使用了over库(狸猫)。好比:

试想一下:你先用 over 库把 log4j 转成了 slfj4 调用。紧接着,你又把 slf4j 适配到 log4j 上。这就构成了一个死循环,确定是会出现堆栈溢出的问题。因此,一个工程里面,只能保留一个日志实现库,还有配套的桥接库,加上其余日志的 over 库,才是正确之道。

异常参数错误

上面提到的这个错误:

大体能够看出来吧,他缺乏一个真正的实现。看你选择使用什么,就把什么库补充上去。加上 logback,去掉 slf4j-log4j 或者加上 log4j 均可以解决这个问题。

其余问题都比较相似,若是看懂了上文的介绍,应该能够着手解决此类问题了。

0x06 不算总结的总结

了解了日志的原理,之后妈妈不再担忧日志冲突了!

相关文章
相关标签/搜索