先看一张图:

是不是有点晕, 晕就对了。这个仅仅是 slf4j 的情况,实际上, 我们不仅要接触到 slf4j ,有时候还会接触其他的日志系统。且看下文分解。

1 直接使用各个日志系统

1.1 直接使用log4j

最开始的时候, 我们都是使用log4j, 怎么使用呢? 先引入jar,log4j-1.x.x  jar

maven是这样的:

        <dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>

然后配置: 配置文件主要是 log4j.properties, 具体略

然后代码中使用

static org.apache.log4j.Logger logger = org.apache.log4j.LogManager.getLogger(TestLog.class);

1.2 直接使用JCL,即commons-logging

有时候我们也使用Apache的 commons-logging ,也就是 Jakarta Commons Logging,简称 JCL。commons-logging其实也是一个日志 接口,不是一个日志控件,没有日志功能,它只是统一了JDK Logging与Log4j的API,并把日志功能交给JDK Loggings或者是log4j 。

commons-logging能够选择使用Log4j还是JDK Logging,但是他不依赖Log4j,JDK Logging的API。如果项目的classpath中包含了log4j的类库,就会使用log4j,否则就使用JDK Logging。使用commons-logging能够灵活的选择使用那些日志方式,而且不需要修改源代码。

怎么使用呢? 首先需要一个 commons-logging-1.2.jar, 然后引入 log4j的类库,或者不引入直接JDK Logging(这里的 log4j的类库 应该是 指log4j-1.x , 对于 log4j2 不知道是否支持 )

参考 :https://blog.csdn.net/u011794238/article/details/50747953

maven是这样的:

        <dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>

配置:主要是 commons-logging.properties,具体略

关于 commons-logging ,其实也是一个 slf4j 类似的日志接口系统,至于其原理,请参考其他的博客,

1.3 直接使用log4j2

后面出了个log4j2,log4j2是怎么使用的? 还是先引入jar : log4j-api-2.x.x.jar和log4j-core-2.x.x.jar

maven是这样的:

<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.8.2</version>
</dependency>

没错,引入一个 log4j-core 即可, log4j-core  会自动引入 log4j-api,另外我发现这么一个jar:

        <dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j</artifactId>
<version>2.8.2</version>
</dependency>

仔细看一下,这个仅仅是一个pom 不是jar,也就是 log4j-core 的parent 。

然后配置: 配置文件主要是 log4j2.xml ( 有时候也可以是 log4j.xml, 或其他名字 ), 具体略

然后代码中使用

static org.apache.logging.log4j.Logger logger = org.apache.logging.log4j.LogManager.getLogger(TestLog.class);

注意看到 log4j 、log4j2 的差别还是比较大的, log4j需要一个 jar,log4j-1.x.x  jar ,而且是 1.x.x 的格式, 最高好像也就是1.7.25 , 我是没有看到 log4j-2.x的 jar 的。 但是到了 log4j2,jar 的命名发生了很大的变化: log4j-api-2.x.x.jar和log4j-core-2.x.x.jar ,没有 log4j-2.x .jar , 而且也不是一个jar ,是分开了2个。

1.4 直接使用logback

后面又来了一个 logback, logback 是天生就和slf 紧密结合在一起的,无法分开。(观察发现logback也没有提供任何的 Logger 实现,logback-core 的功能主要就是实现了很大的 appender ,pattern 等)一般我们需要引入logback-classic , 它直接依赖了 logback-core、slf4j-api , 所以,我们需要3个jar 。

maven是这样的:

<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>

然后配置:  配置文件主要是 logback.xml , 具体略

然后代码中使用,(只能通过slf4j 的api )

static Logger logger = LoggerFactory.getLogger(TestLog.class);

1.5 直接使用JUL ,也就是jdk-logging

直接用 jul 的人应该很少了吧。不过这里还是要说明一下。具体 怎么使用呢?首先,它是jdk 自带的,不需要引入jar:

然后配置: 默认配置文件是logging.properties,logging.properties 一般位于 jdk或者jre 的lib 目录, 一个 jdk或者jre 只有一个配置。具体略

然后代码中使用

static java.util.logging.Logger logger = java.util.logging.Logger.getLogger(TestLog.class.getName());

logger.info("in");

logger.severe("severe");

logger.fine("fine");

可以看到它的 Logger  是  java.util.logging 包(简称 JUL)下面的, 是jdk 自带的。 不过需要注意的是 JUL的Logger 提供的方法和其他的框架的还不一样。

它的日志是这样的:

四月 11, 2019 1:52:46 下午 com.lk.TestLog main
信息: in
四月 11, 2019 1:52:46 下午 com.lk.TestLog main
严重: severe

可以看到其中的月份和 日志级别都使用了 汉字, 非常的明显的 特征。

2 使用slf4j 日志接口框架

前面都是直接使用的方式(除了logback), 如果我们统一到 slf4j 呢?这里说的是统一到 slf4j是指 代码中使用 slf4j的API。 首先我们要明白,slf4j 是什么? 它是一个统一的日志接口框架,这样说可能还是有些懵逼。 怎么说呢, 可以简单理解为它提供了几个关键的接口,但是没有实现他们。 所以说它只是一个 日志接口。通常我们需要 slf4j 结合其他日志框架来打印我们的日志。 老实说,这样做TM有些奇特, 这样做的主要目的是 方便的替换 我们日志框架的实现, 而不用修改代码—— 其实这样的需求也是非常少的。

不管怎么说,slf4j 已然成为了主流。

几乎所有的其他日志框架都可以作为 slf4j 日志接口的实现。怎么使用呢? 通常我们需要首先引入 slf4j-api-1.x.x.jar ( 目前来看, 主要有 1.5,1.6,1.7  三个实现,1.5 有些特别, 兼容性不是很好), 然后引入具体的实现的 jar。

有哪些实现?

2.1 slf4j-api 的NOP实现

NOP, 也就是No Operation,也就是不做任何操作。不需要引入任何其他jar,配置文件也是无。

2.2 slf4j-api 的logback实现

logback,前文已述

2.3 slf4j-api 的simple实现

simple,意味着简单。它非常简单的实现, 需要引入slf4j-simple-1.7.5.jar,

maven是这样的:

        <dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.12</version>
</dependency>

slf4j-simple 依赖了 slf4j-api 。slf4j-simple 通常只是把日志直接打印到 控制台。

配置是:simplelogger.properties 或者通过D系统参数。

2.4 slf4j-api 的JUL实现

其实就是把日志操作 转接给了 jdk , 也就是 jul , 需要 slf4j-jdk14-1.x.x.jar ,

maven是这样的:

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>1.7.25</version>
</dependency>

slf4j-jdk14 依赖了 slf4j-api 。

配置同直接使用JUL的场景

2.5 slf4j-api 的log4j 实现

具体日志工作交给了 log4j , 需要 slf4j-log4j12-1.x.x.jar (主要是 slf4j-log4j12-1.6.x.jar, slf4j-log4j12-1.7.x.jar), log4j-1.2.x.jar ,

maven是这样的:

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.6.1</version>
</dependency>

slf4j-log4j12 依赖了slf4j-api 和 log4j12 。

配置同直接使用log4j的场景

2.6 slf4j-api 的log4j2 实现

依赖 log4j2 , 需要 log4j-slf4j-impl-2.x.x.jar , log4j-api ,  log4j-core ,

maven是这样的:

<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.8.2</version>
</dependency> <dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.8.2</version>
</dependency>

log4j-slf4j-impl 依赖了slf4j-api 和 log4j-api,  但是并没有依赖   log4j-core , 因此, 我们需要手动引入 它 。

配置同直接使用log4j2的场景

2.7 slf4j-api 的JCL实现

依赖 commons-logging , 需要 slf4j-jcl ,

maven :用得少,略

配置:略

3 使用其他日志框架,然后桥接给 slf4j 日志接口框架

变态的还不仅仅是这个, 有时候,我们还需要 反向的 日志操作。比如 我们有代码已经直接使用的 log4j 的api, 但是想使用 slf4j 的实现,怎么办呢? 可以 先引入  log4j 的api的jar, 然后引入 slf4j-api , 然后引入 slf4j 的具体实现(如上), 比如 log4j  的接口,slf4j 做桥接, logback 的实现, 这个是完全可以的。 (这可以达到一种奇特的效果, 整个系统中, 虽然有各种第二方、三方框架, 他们直接使用各种各样的 日志api, 但是我都可以让他们 统一使用 logback的实现, 也就是 只使用一个 配置即可: logback.xml , 可以减少配置量)

但是, 我们肯定不会说, log4j  的接口,slf4j 做桥接, 然后又采用 log4j  作为slf4j  的实现。看起来行得通, 实际上会出现 循环引用的问题,

这种做法,其实有点绕,容易让人迷糊。也许是我见识短浅,我其实没有直接使用过这样做法。 不过, 现在中 确实也有这样的需求。

具体来说 也有很多种, log4j, log4j2, jcl,jul,等等。但是 我们似乎找不到 logback 桥接给 slf4j  的情况,我感觉是因为logback 没有直接使用的接口,它天生就是和slf4j 紧密联系的。

3.1 使用log4j的 api,然后桥接给slf4j

对于 log4j, 我们首先需要log4j的 api接口: log4j-over-slf4j-1.7.x.jar, 其他的就交给了 slf4j  ————  你可能有疑问, log4j 的接口在哪里呢? 没错,就是  log4j-over-slf4j-1.7.x.jar ,这里我们没有使用 log4j-1.x.x  jar,因为 log4j-1.x.x  jar 包含了 log4j 的实现,我们不需要它,我们需要排除它。 这种做法简直不可思议, 不过它就是这么发生了!

maven是这样的:

        <dependency>
<groupId>org.slf4j</groupId>
<version>1.7.21</version>
<artifactId>log4j-over-slf4j</artifactId>
</dependency>

log4j-over-slf4j 依赖了slf4j-api。

至于桥接到slf4j 之后的工作,比如配置文件啊,还需要的其他jar 啊,请参考前文。总之, 不要出现了 循环引用即可。

循环引用

另外,我注意到, 如果 log4j-over-slf4j  、  log4j-1.x.x  jar 如果同时存在于 maven 的pom ,或者classpath, 那么 到底是启用哪一个呢?  对应 maven, 答案是, 哪个先出现在pom中 就启用哪一个!

需要重要的是: log4j-over-slf4j.jar 不能和 slf4j-log4j12.jar 同时存在于 classpath 中, 否则

对于 slf4j-log4j12-1.7.25.jar :出现

Detected both log4j-over-slf4j.jar AND bound slf4j-log4j12.jar on the class path, preempting StackOverflowError.

slf4j-log4j12-1.7.25.jar  比较温和,有检测机制, slf4j-log4j12-1.7.25.jar 之前的 slf4j-log4j12-1.x.x jar 则是直接抛出 异常:

java.lang.StackOverflowError
at java.util.HashMap.hash(HashMap.java:338)
at java.util.HashMap.get(HashMap.java:556)
at org.slf4j.impl.Log4jLoggerFactory.getLogger(Log4jLoggerFactory.java:67)
at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:358)
at org.apache.log4j.Category.<init>(Category.java:57)
at org.apache.log4j.Logger.<init>(Logger.java:37)
at org.apache.log4j.Log4jLoggerFactory.getLogger(Log4jLoggerFactory.java:43)
at org.apache.log4j.LogManager.getLogger(LogManager.java:45)

除了log4j, 我想其他日志框架也是如此,不能出现over-slf4j 之后出现循环委托的情况。

3.2 使用log4j2的 api,然后桥接给slf4j

log4j 是 1.x 时代了, 如果是 log4j2 呢? 我们首先需要log4j2的 api接口:log4j-to-slf4j-2.x.x.jar,

maven是这样的:

<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-to-slf4j</artifactId>
<version>2.11.1</version>
</dependency>

log4j-to-slf4j-2.x.x.jar 依赖了slf4j-api 和log4j-api ( log4j-api 是log4j2 的api 接口 ), log4j-to-slf4j-2.x.x.jar 没有依赖 log4j-core。 那么 log4j2 是怎么反向桥接到 slf4j 上去的呢? 其实这个是 log4j-to-slf4j 的功劳,log4j-to-slf4j-2.x.x.jar 桥接了 log4j-api ,然后具体实现交给了 slf4j。

特别注意到:log4j-1.x 的桥接jar 名是这样的:  log4j-over-slf4j  而log4j-2.x 的桥接jar 名是这样的: log4j-to-slf4j,  名字非常相近。

至于桥接到slf4j 之后的工作,请参考前文。

3.3 使用JCL 的 api,然后桥接给slf4j

前面说过使用 SLF4J的API, 然后 使用JCL的实现, 反过来也是可以的。

jcl 并不是jul, 不是 java.util.logging 而是 org.apache.commons.logging , 也就是 commons-logging 的那一套。 为此, 我们需要一个 jcl-over-slf4j-1.x.x.jar

maven是这样的:

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.25</version>
</dependency>

至于桥接到slf4j 之后的工作,请参考前文。

3.4 使用JUL 的 api,然后桥接给slf4j

JUL 和 JCL 是仅仅一字之别,但是千万不能搞混啊!它是将 jdk logging API 打印的日志交给了 slf4j,我们 需要一个 jul-to-slf4j-1.7.x.jar

maven是这样的:

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-slf4j</artifactId>
<version>1.7.25</version>
</dependency>

代码中还需要做一些修改:

需要先执行下面的:

    static{
SLF4JBridgeHandler.install();
}

不过从打印的结果, 我看到 日志打印了两次, 似乎哪里出了问题,并没有达到想要的效果。

四月 11, 2019 6:40:12 下午 com.lk.TestLog main
信息: in
18:40:12,318 [com.lk.TestLog] - severe           --- log4j的日志
四月 11, 2019 6:40:12 下午 com.lk.TestLog main
严重: severe

需要注意的是 我们并没有 jul-over-slf4j 的jar, 这个可能是命名的历史原因, 暂时 jul-to-slf4j 可以理解为就是  jul-over-slf4j ...

至于桥接到slf4j 之后的工作,请参考前文。

4 使用A日志框架接口,然后桥接给 B日志框架实现

这样的需求也是有的,做法肯定也是有的,也肯定有人实现了的。比如,貌似 feign 也有一个实现,gossip 也有:

这些jar我没有用过,具体,就不多说了。

关于SLF4J 其实也可以写很多,还有JCL( 也就是commons-logging)具体的原理这里不多讲了,请参考其他的博客。总之,感觉这整个java 的日志系统 还是挺麻烦的,特别是其中的jar的命名,不太规律,容易混淆,不容易记住。搞来搞去, 容易把人搞晕呢,这个时候 就需要看其源码! 其实其中很多jar 的实现是很简单的,也就几个类,特别是那些桥接的jar, 一打开看一下就明白了,哦,说得也是呢!

 看我的文章后是不是有所收获呢?是不是应该点32个赞鼓励一下呢?

参考:

https://my.oschina.net/pingpangkuangmo/blog/410224
https://www.slf4j.org/legacy.html
https://blog.csdn.net/u011794238/article/details/50747953
https://blog.csdn.net/john1337/article/details/76152906
http://www.cnblogs.com/chen310/p/4216316.html
https://blog.csdn.net/u011794238/article/details/50783188
https://blog.csdn.net/u011794238/article/details/50771488

常见java日志系统的搭配详解:关于slf4j log4j log4j2 logback jul jcl commons-logging jdk-logging的更多相关文章

  1. 集中式日志系统 ELK 协议栈详解

    简介 在我们日常生活中,我们经常需要回顾以前发生的一些事情:或者,当出现了一些问题的时候,可以从某些地方去查找原因,寻找发生问题的痕迹.无可避免需要用到文字的.图像的等等不同形式的记录.用计算机的术语 ...

  2. 《手把手教你》系列基础篇(九十一)-java+ selenium自动化测试-框架设计基础-Logback实现日志输出-下篇(详解教程)

    1.简介 为了方便查看和归档:(1)不同包的日志可能要放到不同的文件中,如service层和dao层的日志:(2)不同日志级别:调试.信息.警告和错误等也要分文件输出.所以宏哥今天主要介绍和分享的是: ...

  3. JAVA中使用log4j及slf4j进行日志输出的方法详解

    JAVA中输出日志比较常用的是log4j,这里讲下log4j的配置和使用方法,以及slf4j的使用方法.  一.下载log4j的架包,并导入项目中,如下: 二.创建log4j.properties配置 ...

  4. java对象池commons-pool-1.6详解(一)

    自己的项目中用到了 对象池 commons-pool: package com.sankuai.qcs.regulation.protocol.client; import com.dianping. ...

  5. [日志log] 常用log日志记录方式对比和详解

    1.现在都有哪些记录日志的方法 A.java.util.logging.Logger - 使用详解 B.log4j - 使用详解 C.SLF4J(simple logging Facade for J ...

  6. Java基础-面向接口编程-JDBC详解

    Java基础-面向接口编程-JDBC详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.JDBC概念和数据库驱动程序 JDBC(Java Data Base Connectiv ...

  7. Java网络编程和NIO详解9:基于NIO的网络编程框架Netty

    Java网络编程和NIO详解9:基于NIO的网络编程框架Netty 转自https://sylvanassun.github.io/2017/11/30/2017-11-30-netty_introd ...

  8. Java日志系统及框架分析

    最近在考虑将容器(Tomcat)内的应用日志统一成slf4j + logback,主要目的有: 快速定位应用日志输出路径,方便日志的采集: 能动态调整日志的级别,方便线上问题定位: 方便在容器层面做扩 ...

  9. Java虚拟机之垃圾回收详解一

    Java虚拟机之垃圾回收详解一 Java技术和JVM(Java虚拟机) 一.Java技术概述: Java是一门编程语言,是一种计算平台,是SUN公司于1995年首次发布.它是Java程序的技术基础,这 ...

随机推荐

  1. Java中如何拆分字符串为字符数组

    题目:输入一串字符,由(){}[]组成,判断是否所有的括号都是闭括号,是的返回TRUE,不是返回FALSE. /*输入字符串,拆解为字符数组 * 用函数s.charAt(i)来完成 * * */imp ...

  2. 哈希表(Hash Map)

    今天第一次做Leetcode用到了散列表,之前学的数据结构的内容都忘了,正好趁热打铁补一补. 摘自其他博客的一个整合. 一.哈希表简介 数据结构的物理存储结构只有两种:顺序存储结构和链式存储结构(像栈 ...

  3. SP3871 GCDEX - GCD Extreme

    //author Eterna #define Hello the_cruel_world! #pragma GCC optimize(2) #include<iostream> #inc ...

  4. linux下用ctrl+r快速搜索history命令

    前提是,搜索已经使用的命令,否则是查不出来结果的. ctrl+r用途:反向搜索执行过的命令.(reverse-i-search) 1.任何目录下按住ctrl + r 2.输入历史命令中的字符串 ,比如 ...

  5. EXCEL 批量添加单元格名称

    在EXCEL单元格的值填入想为其设置的名称之后,调整下述代码的begin_line,begin_column,end_line,end_column的值,执行此宏,可用于批量为单元格设置名称. (注: ...

  6. python 集成cython 简单测试

      实际开发中我们可能需要集成c/c++ 编写的模块,我们可以通过cython 解决类似的问题 以下测试一个简单的c add 方法, 使用venv 同时构建为一个pip 包 环境准备 venv 初始化 ...

  7. 基于scrapy源码实现的自定义微型异步爬虫框架

    一.scrapy原理 Scrapy 使用了 Twisted异步网络库来处理网络通讯.整体架构大致如下 Scrapy主要包括了以下组件: 引擎(Scrapy)用来处理整个系统的数据流处理, 触发事务(框 ...

  8. Centos7 kernel 内核升级 GPU显卡驱动程序编译安装

    1.NVIDIA官网下载相关显卡驱动 #在服务器上查看网卡型号 lspci -mm | grep NVIDIA   #在NVIDIA官网下载相应型号驱动程序 https://www.geforce.c ...

  9. 移动端css水平垂直居中

    水平垂直居中 1.margin 负值调整偏移实现 兼容性: 当前流行的使用方法. <div class="box"> <div class="conte ...

  10. multipart/form-data和application/x-www-form-urlencoded区别

    FORM元素的enctype属性指定了表单数据向服务器提交时所采用的编码类型.例如: application/x-www-form-urlencoded: 窗体数据被编码为名称/值对.这是标准的编码格 ...