假设之前项目中的日志框架统一采用SLF4J,具体实现框架为Logback,现在我们需要引入一个第三方依赖xxx.jar,而这个依赖底层使用的日志框架为Log4j,那么怎么让项目统一用Logback来记录日志呢?

桥接器

slf4j提供了一个叫桥接器的东西,可以将Log4j桥接到Slf4j,Slf4j再去使用Logback记录日志。

项目中的依赖如下:

<dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>1.7.5</version>
</dependency><dependency><groupId>org.slf4j</groupId><artifactId>log4j-over-slf4j</artifactId><version>1.7.5</version>
</dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.2.3</version>
</dependency>

这时我们再去调用Log4jTest,打印的是logback记录的日志。

package com.morris.spring.log;import org.apache.log4j.Logger;public class Log4jTest {public static void main(String[] args) {Logger logger = Logger.getLogger(Log4jTest.class);logger.info("log4j log");}
}

不相信可以删除项目中的log4j.properties,不会有警告。

死循环

项目中的依赖如下(idea中需要将log4j-over-slf4j放在slf4j-log4j12前面才能出现效果):

<dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>1.7.5</version>
</dependency><dependency><groupId>org.slf4j</groupId><artifactId>log4j-over-slf4j</artifactId><version>1.7.5</version>
</dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId><version>1.7.5</version>
</dependency>

这时我们再去调用Slf4jTest:

package com.morris.spring.log;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class Slf4jTest {public static void main(String[] args) {Logger logger = LoggerFactory.getLogger(Slf4jTest.class);logger.info("slf4j log");}
}

运行结果如下:

SLF4J: Detected both log4j-over-slf4j.jar AND slf4j-log4j12.jar on the class path, preempting StackOverflowError.
SLF4J: See also http://www.slf4j.org/codes.html#log4jDelegationLoop for more details.
Exception in thread "main" java.lang.ExceptionInInitializerErrorat org.apache.log4j.Logger.getLogger(Logger.java:40)at org.apache.log4j.Logger.getLogger(Logger.java:48)at com.morris.spring.log.Log4jTest.main(Log4jTest.java:9)
Caused by: java.lang.IllegalStateException: Detected both log4j-over-slf4j.jar AND slf4j-log4j12.jar on the class path, preempting StackOverflowError. See also http://www.slf4j.org/codes.html#log4jDelegationLoop for more details.at org.apache.log4j.Log4jLoggerFactory.<clinit>(Log4jLoggerFactory.java:51)... 3 more

系统会抛出一个StackOverflowError异常。

当同时引用了log4j-over-slf4j.jar和slf4j-log4j12.jar这两个依赖的时候,会出现这样的错误。究其本质的原因是: log4j将日志扔给slf4j --> slf4j又将日志扔回去给log4j,所以循环于此,所以会报错。

桥接器的工作原理

以log4j-over-slf4j.jar为例,如果使用了log4j-over-slf4j.jar,那么项目中就不在需要log4j.jar。

log4j对外提供了接口org.apache.log4j.Logger,那么log4j-over-slf4j也提供一个org.apache.log4j.Logger接口,全限定名保持一致,这样编译就不会报错了。

下面跟踪源码,探寻log4j-over-slf4j的org.apache.log4j.Logger接口底层是怎么桥接到slf4j。

从Log4jTest开始找:

Logger logger = Logger.getLogger(Log4jTest.class);
logger.info("log4j log"); // 进入info

org.apache.log4j.Category#info(java.lang.Object),注意这个info方法是父类Category的differentiatedLog,是父类org.apache.log4j.Category的。

void differentiatedLog(Marker marker, String fqcn, int level, Object message, Throwable t) {String m = this.convertToString(message);if (this.locationAwareLogger != null) {this.locationAwareLogger.log(marker, fqcn, level, m, (Object[])null, t);} else {switch(level) {case 0:this.slf4jLogger.trace(marker, m);break;case 10:this.slf4jLogger.debug(marker, m);break;case 20:this.slf4jLogger.info(marker, m); // 发现实际调用了slf4jLogger.infobreak;case 30:this.slf4jLogger.warn(marker, m);break;case 40:this.slf4jLogger.error(marker, m);}}}

从上面的代码发现实际调用了slf4jLogger.info(),那么slf4jLogger是什么呢,查看他的类型得知org.slf4j.Logger,这样就完成了log4j桥接到slf4j。

SLF4J中的桥接器与源码剖析相关推荐

  1. jdk包含java语言核心的类_1.1 jvm核心类加载器--jdk源码剖析

    目录 前提: 运行环境 1. 类加载的过程 1.1 类加载器初始化的过程 1.2 类加载的过程 1.3 类的懒加载 2. jvm核心类加载器 3. 双亲委派机制 4. 自定义类加载器 5. tomca ...

  2. Django中的认证与权限 源码剖析

    rest_framework/request.py中部分认证和权限代码 def _authenticate(self):"""Attempt to authenticat ...

  3. python字符串代码对象_Python源码剖析 - Python中的字符串对象

    1. 前言 我们已经在 [Python中的整数对象] 章节中对定长对象进行了详细的讲解,接下来我们将介绍变长对象,而字符串类型,则是这类对象的典型代表. 这里必须先引入一个概念: Python 中的变 ...

  4. Swoft 源码剖析 - Swoft 中的注解机制

    作者:bromine 链接:https://www.jianshu.com/p/ef7... 來源:简书 著作权归作者所有,本文已获得作者授权转载,并对原文进行了重新的排版. Swoft Github ...

  5. STL源码剖析---空间配置器

    看过STL空间配置器的源码,总结一下:       1.STL空间配置器:主要分三个文件实现,stl_construct.h  这里定义了全局函数construct()和destroy(),负责对象的 ...

  6. STL源码剖析学习二:空间配置器(allocator)

    STL源码剖析学习二:空间配置器(allocator) 标准接口: vlaue_type pointer const_pointer reference const_reference size_ty ...

  7. STL源码剖析 空间配置器 查漏补缺

    ptrdiff_t含义 减去两个指针的结果的带符号整数类型 ptrdiff_t (Type support) - C 中文开发手册 - 开发者手册 - 云+社区 - 腾讯云 std::set_new_ ...

  8. STL源码剖析——空间配置器

    目录 构造和析构基本工具:construct() 和 destroy() 空间的配置与释放:std::alloc 二级空间配置器简述 空间配置函数allocate() 空间释放函数deallocate ...

  9. 字体查看器,源码奉上

    字体查看器 用于快速查看,预览,编辑,各种字体.基于PC中已经安装字体. 源码2KB,每个人都可以由编辑修改源码. 超级方便好用.按键键盘上下键,快速切换字体. 上图: 上源码 把源码复制保存到文本文 ...

最新文章

  1. python数据分析实训大纲,数据分析大赛考纲:(二)Python数据分析应会部分
  2. c语言代码,输入两个数,输出第一个数到第二个数之间所有的数
  3. 人工智能热潮下,我们该如何紧跟科技脚步呢?
  4. 通过js获取元素css3的transform rotate旋转角度方法
  5. 如何将SmartDraw中的图形导出LATEX可用的EPS格式?
  6. boost::geometry::comparable_distance用法的测试程序
  7. vi编辑器的学习使用(十三)
  8. 45万例患者基因检测显示:NGS很难检测出七分之一的致病变异
  9. hbase shell相关命令
  10. C/C++ 跨平台交叉编译、静态库/动态库编译、MinGW、Cygwin、CodeBlocks使用原理及链接参数选项...
  11. 网页中插入当前时间和实时天气
  12. 知网上下载硕博论文为PDF格式的方法
  13. 查看浏览器Browsers的内核版本, 可以用 navigator.userAgent
  14. 桌面小部件Wight父类AppWidgetProvider的三个方法
  15. php 支付宝实名认证
  16. 《Rework》语句摘录
  17. 移动端点击a标签后默认蓝色背景如何去除
  18. Python--fractions库【分数、有理数】
  19. C++ list initialization
  20. 免费的文件比较工具推荐一个

热门文章

  1. 微信公众号计算机编程,微信公众号群发文章怎么添加小程序?-电脑教程
  2. mysql的substr函数常用语法
  3. 阿凡达(Avatar)迄今为止最搞笑最靠谱的2个影评
  4. 2022年美赛C题-交易策略-完整解题论文和代码
  5. VR教育让学习变得简单而有趣,VR教育都有哪些优势?
  6. 【STM32】PWM 输出 (标准库)
  7. 优秀程序猿写技术文档的正确姿势
  8. 设置win服务器代理
  9. 网络舆情风险和危机监测解决方案
  10. 保研导师联系邮件模板