作为Java开发,平时虽然日志使用的比较多,但是其深入的原理压根就没想过要去研究,有一种日用而不知的感觉。

扪心自问,这么简单的问题,确实不清楚。

之后,便是知耻而后勇,便有了对平常使用的日志的仔细研究。

下面,笔者以我们通常用的最多的spring-boot-starter-web说起。

相信大家对下面的这个依赖很熟悉:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency>

这个是我们目前搭建springboot项目最常见的依赖引入。

在idea中,我们通过ctrl+鼠标左键,一步步点击进去,可以看到:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId><version>2.5.0</version><scope>compile</scope>
</dependency>

继续:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-logging</artifactId><version>2.5.0</version><scope>compile</scope>
</dependency>

最终我们看到了在项目中默认使用的log框架:

<dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.2.3</version><scope>compile</scope>
</dependency>
...
<dependency><groupId>org.slf4j</groupId><artifactId>jul-to-slf4j</artifactId><version>1.7.30</version><scope>compile</scope>
</dependency>

笔者研究的便是slf4j及其实现logback的关系。

在进入正题之前,我们先回顾我们常见的一个设计模式:外观模式。

其定义为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。详见:JAVA设计模式之门面模式(外观模式) | 菜鸟教程

而我们的slf4j便是相当于一个Facade层,所用的日志打印都是通过slf4j来转发,但是具体的功能实现是由logback来实现,当然也可以由别的依赖来实现,比如slf4j-simple。

首先我们看springboot框架默认实现:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class TestLogger {private static final Logger log = LoggerFactory.getLogger(TestLogger.class);public static void main(String[] args) {log.info("test---------->>>>>>>>>>>><<<<<<<");}
}

归根结底,所有的用法都只是片面,我们要理解原理,还是要从源码入手。进入getLogger方法:

public static Logger getLogger(Class<?> clazz) {Logger logger = getLogger(clazz.getName());if (DETECT_LOGGER_NAME_MISMATCH) {Class<?> autoComputedCallingClass = Util.getCallingClass();if (autoComputedCallingClass != null && nonMatchingClasses(clazz, autoComputedCallingClass)) {Util.report(String.format("Detected logger name mismatch. Given name: \"%s\"; computed name: \"%s\".", logger.getName(), autoComputedCallingClass.getName()));Util.report("See http://www.slf4j.org/codes.html#loggerNameMismatch for an explanation");}}return logger;}

重点关注

getLogger(clazz.getName())

继续点击:

public static Logger getLogger(String name) {ILoggerFactory iLoggerFactory = getILoggerFactory();return iLoggerFactory.getLogger(name);}

日志打印的具体实现便在

getILoggerFactory()
public static ILoggerFactory getILoggerFactory() {if (INITIALIZATION_STATE == 0) {Class var0 = LoggerFactory.class;synchronized(LoggerFactory.class) {if (INITIALIZATION_STATE == 0) {INITIALIZATION_STATE = 1;performInitialization();}}}switch(INITIALIZATION_STATE) {case 1:return SUBST_FACTORY;case 2:throw new IllegalStateException("org.slf4j.LoggerFactory in failed state. Original exception was thrown EARLIER. See also http://www.slf4j.org/codes.html#unsuccessfulInit");case 3:return StaticLoggerBinder.getSingleton().getLoggerFactory();case 4:return NOP_FALLBACK_FACTORY;default:throw new IllegalStateException("Unreachable code");}}

日志打印的重点便在于返回

StaticLoggerBinder.getSingleton().getLoggerFactory()

这个对象来实现具体的日志打印工作,那StaticLoggerBinder这个类又是从哪里来,是要干什么的呢?

我们通过实际代码执行可以知道,StaticLoggerBinder便是logback这个jar包提供对slf4j日志接口LoggerFactoryBinder的具体实现,也就是说实际的日志打印slf4j不能执行,只能通过接口的实现类StaticLoggerBinder来进行执行。

这样的好处便在于slf4j相当于只是提供一个接口或者说标准,但是具体的执行可以由其实现类来执行,这样只要是实现了slf4j标准接口的任意日志框架便都可以来执行日志打印。

通过这种方式slf4j可以同时支持多种日志框架,且无需任何配置,只需要引入特定的jar包让其拥有指定全类名的StaticLoggerBinder类即可。

那么这样也会导致另一个问题,如果系统引入了多个同时实现slf4j接口的类,那么系统怎么办,是否会报错?

这种特殊情况,slf4j也有做处理,其处理方式便是通过打印所有的引入实现类,然后由JVM虚拟机选择一个合适的实现类来执行日志打印。

这样既不会影响系统日志执行,也能使程序员通过日志,清楚的看出系统中存在哪些日志的实现类。

其具体代码在上午

getILoggerFactory()方法中的performInitialization()执行:
private static final void performInitialization() {bind();if (INITIALIZATION_STATE == 3) {versionSanityCheck();}}

在bind执行:

private static final void bind() {try {String msg;try {Set<URL> staticLoggerBinderPathSet = null;if (!isAndroid()) {staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);}StaticLoggerBinder.getSingleton();INITIALIZATION_STATE = 3;reportActualBinding(staticLoggerBinderPathSet);} catch (NoClassDefFoundError var7) {
......

重点便在于

findPossibleStaticLoggerBinderPathSet();//找到潜在的slf4j实现类
reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);//打印所有的实现类全类名
reportActualBinding(staticLoggerBinderPathSet);//打印实际的实现类全类名

详细方法如下:

static Set<URL> findPossibleStaticLoggerBinderPathSet() {LinkedHashSet staticLoggerBinderPathSet = new LinkedHashSet();try {ClassLoader loggerFactoryClassLoader = LoggerFactory.class.getClassLoader();Enumeration paths;if (loggerFactoryClassLoader == null) {paths = ClassLoader.getSystemResources(STATIC_LOGGER_BINDER_PATH);} else {paths = loggerFactoryClassLoader.getResources(STATIC_LOGGER_BINDER_PATH);}while(paths.hasMoreElements()) {URL path = (URL)paths.nextElement();staticLoggerBinderPathSet.add(path);}} catch (IOException var4) {Util.report("Error getting resources from path", var4);}return staticLoggerBinderPathSet;}

可以看到,其是通过

STATIC_LOGGER_BINDER_PATH

即,

private static String STATIC_LOGGER_BINDER_PATH = "org/slf4j/impl/StaticLoggerBinder.class

也就是指定的全限定类名来进行加载的。

这样就涉及到一个问题, 不同的jar包依赖是可以创建同样的全限定类名的,这样会导致

paths = loggerFactoryClassLoader.getResources(STATIC_LOGGER_BINDER_PATH);

出现多个具有同样全限定类名的类被重复找到。

那么在slf4j有多个实现的时候,如何保证其加载指定的实现呢?

以实现框架slf4j-sample和logback为例,在工程的pom文件中:

sif4j-sample在前,默认引入logback在后,其运行结果如下:

可以看到 sif4j-sample和logback有着一个完全相同的全限定类名:StaticLoggerBinder,那么系统到底选用哪一个呢?通过日志打印我们可以看到系统选择的是sif4j-sample,这是由于在pom文件中,其引入在logback之前,JVM虚拟机加载class文件时,便会选择优先引入的StaticLoggerBinder类。

反之,如果默认引入在前,则会使用默认logback。

如果只需要引入一个日志实现,也可以显示的注释或者排除掉另外一个。

参考:Java日志框架:slf4j作用及其实现原理 - 五月的仓颉 - 博客园

Java日志框架:slf4j作用及其实现原理相关推荐

  1. Java日志框架Slf4j+Log4j入门

    一.日志系统介绍 slf4j,即简单日志门面(Simple Logging Facade for Java),不是具体的日志解决方案,它只服务于各种各样的日志系统.简答的讲就是slf4j是一系列的日志 ...

  2. [java进阶]3.slf4j作用及其实现原理

    参考博客:https://www.cnblogs.com/xrq730/p/8619156.html 1. 简单回顾门面模式 slf4j是门面模式的典型应用,因此在讲slf4j前,我们先简单回顾一下门 ...

  3. Java日志框架SLF4J和log4j以及logback的联系和区别

    1.SLF4J(Simple logging Facade for Java) 意思为简单日志门面,它是把不同的日志系统的实现进行了具体的抽象化,只提供了统一的日志使用接口,使用时只需要按照其提供的接 ...

  4. Java日志框架 -- SLF4J日志门面(入门案例、SLF4J优点、SLF4J日志绑定、SL4J桥接旧的日志框架)

    1. SLF4J日志门面 JCL日志门面逐渐被淘汰了,因为他无法动态的扩展具体的日志实现框架. 简单日志门面(Simple Logging Facade For Java) SLF4J主要是为了给Ja ...

  5. 一篇文章带你搞定 Java 日志框架 slf4j

    文章目录 一.门面模式 二.为什么要使用 slf4j ? 三.如何使用 一.门面模式 slf4j是门面模式的典型应用,因此在讲slf4j前,先简单学习下门面模式, 门面模式,其核心为外部与一个子系统的 ...

  6. Java日志框架-SLF4J入门 [ LogBack 样例实现 ]

    概述 slf4j只是一个日志标准,并不是日志系统的具体实现. 我们编程的时候只需要操作slf4j,具体底层实现不关注,只需要配置即可. slf4j只做两件事情: 提供日志接口 提供获取具体日志对象的方 ...

  7. Java日志框架-SLF4J入门

    1.新建一个maven项目,引入下列依赖: <dependency><groupId>org.slf4j</groupId><artifactId>sl ...

  8. java日志框架JUL、JCL、Slf4j、Log4j、Log4j2、Logback 一网打尽

    为什么程序需要记录日志 我们不可能实时的24小时对系统进行人工监控,那么如果程序出现异常错误时要如何排查呢?并且系统在运行时做了哪些事情我们又从何得知呢?这个时候日志这个概念就出现了,日志的出现对系统 ...

  9. java sl4j 日志_Java日志框架Slf4j+Log4j入门

    一.日志系统介绍 slf4j,即简单日志门面(Simple Logging Facade for Java),不是具体的日志解决方案,它只服务于各种各样的日志系统.简答的讲就是slf4j是一系列的日志 ...

  10. 学习Java日志框架之——搞懂日志门面(JCL+SLF4J)

    文章目录 系列文章目录 一.什么是日志门面 1.门面模式(外观模式) 2.日志门面 二.了解JCL 1.JCL组件结构 2.JCL案例 (1)JCL默认实现 (2)导入log4j测试原有程序 三.SL ...

最新文章

  1. isContinuous 反色处理
  2. 用户界面线程AfxBeginThread的使用
  3. swiper 定义放多少张图片,小程序swiper轮播图,自定义样式,两种方法:原生方法和bindchange方法;将点点改为数字(当前第几张 /总共几张);点击点点跳转当前图片...
  4. DzzOffice增加应用对扩展名文件的支持设置,将会在Beta中提供。
  5. 【嵌入式】C语言高级编程-变参函数(08)
  6. ASP.NET开发资源
  7. 【李宏毅2020 ML/DL】P16 PyTorch Tutorial | 最后提及了 apex.amp
  8. vue三种常用获取input值方法
  9. SpringMVC @ControllerAdvice 注解的官方解释
  10. Thinkphp结合phpqrcode生成二维码海报代码
  11. OD使用教程23 - 调试篇23
  12. sql prompt linux,sqlplus中灵活使用sqlprompt提示符
  13. vue启动项目报错 `webpack-dev-server --inline --progress --config build/webpack.dev.conf
  14. Unity3D利用代码生成脚本模板
  15. Linux学习整理-终端快捷键(常用)
  16. 虚拟机安装win10教程(详细版)
  17. 联想计算机怎么开启网络共享,联想笔记本开启Wi-Fi共享图文教程
  18. 单位根检验urdf_怎样分析单位根检验结果
  19. php 带参页面跳转页面,跳转页面带参数_如何带参数跳转php界面
  20. 从零开始学_JavaScript_系列(五)——dojo(基础,动画移动,重力模拟,动画合并,添加标签)...

热门文章

  1. 办公人员必须会的15种求和技巧
  2. html游戏加载不出图片吗,uc浏览器加载不出图片怎么办?uc浏览器加载不出图片的解决方法...
  3. 论文阅读笔记:Deep Conversational Recommender System: A New Frontier for Goal-Oriented Dialogue Systems
  4. 腾讯云游戏数据库 TcaplusDB 的一些常见问题的解答
  5. 基于知识图谱和图卷积神经网络的应用——学习笔记
  6. 【每日一练】64—CSS实现彩虹文字的动画效果
  7. Koltin简明学习,also,takeIf,takeUnless
  8. take apart /ke back等动词词组
  9. 深圳一普通中学老师工资单曝光,秒杀程序员
  10. 解决 Error starting userland proxy: listen tcp 0.0.0.0:6379: bind: address already in use