日志框架Slf4j作用及其实现原理

news2024/10/6 22:30:36

目录

    • 1 设计模式门面模式
    • 2 slf4j源码解析


1 设计模式门面模式

设计模式之门面模式与装饰器模式详解和应用:https://blog.csdn.net/ZGL_cyy/article/details/129073521

slf4j是门面模式的典型应用,因此在讲slf4j前,我们先简单回顾一下门面模式,

门面模式,其核心为外部与一个子系统的通信必须通过一个统一的外观对象进行,使得子系统更易于使用。

用一张图来表示门面模式的结构为:

img

门面模式的核心为Facade即门面对象,门面对象核心为几个点:

  • 知道所有子角色的功能和责任
  • 将客户端发来的请求委派到子系统中,没有实际业务逻辑
  • 不参与子系统内业务逻辑的实现

大致上来看,对门面模式的回顾到这里就可以了,开始接下来对SLF4J的学习。

2 slf4j源码解析

我们为什么要使用slf4j,举个例子:

我们自己的系统中使用了logback这个日志系统
我们的系统使用了A.jar,A.jar中使用的日志系统为log4j
我们的系统又使用了B.jar,B.jar中使用的日志系统为slf4j-simple

这样,我们的系统就不得不同时支持并维护logback、log4j、slf4j-simple三种日志框架,非常不便。

解决这个问题的方式就是引入一个适配层,由适配层决定使用哪一种日志系统,而调用端只需要做的事情就是打印日志而不需要关心如何打印日志,slf4j或者commons-logging就是这种适配层,slf4j是本文研究的对象。

从上面的描述,我们必须清楚地知道一点:slf4j只是一个日志标准,并不是日志系统的具体实现。理解这句话非常重要,slf4j只做两件事情:

  • 提供日志接口
  • 提供获取具体日志对象的方法

slf4j-simple、logback都是slf4j的具体实现,log4j并不直接实现slf4j,但是有专门的一层桥接slf4j-log4j12来实现slf4j。

为了更理解slf4j,我们先看例子,再读源码,相信读者朋友会对slf4j有更深刻的认识。

slf4j应用举例

上面讲了,我们先定义一个pom.xml,引入相关jar包:

<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的关系。

而我们的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);
    }

日志打印的具体实现便在

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虚拟机选择一个合适的实现类来执行日志打印。

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

其具体代码在上

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有多个实现的时候,如何保证其加载指定的实现呢?

实测:如果有多个日志实现的话,默认使用先导入的实现

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/978134.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

前端日期比较大小(超简单版,不需要转换时间戳)

思路&#xff08;把日期转换为Number类型进行比较&#xff09; 效果如图&#xff1a; 第一步&#xff1a;如果获取到的日期是 &#xff1a;"2023-08-03 00:00:00" 用 timesss.split( )[0] // .split( ) 中间有个空格哦 timesss是自己数据的变…

nlp系列(7)实体识别(Bert)pytorch

模型介绍 本项目是使用Bert模型来进行文本的实体识别。 Bert模型介绍可以查看这篇文章&#xff1a;nlp系列&#xff08;2&#xff09;文本分类&#xff08;Bert&#xff09;pytorch_bert文本分类_牧子川的博客-CSDN博客 模型结构 Bert模型的模型结构&#xff1a; 数据介绍 …

解决Android U无法通过adb安装应用(Caller has no access to session -1)的问题

在Android U&#xff08;14&#xff09;上&#xff0c;对通过adb安装应用做了限制。默认的情况下&#xff0c;当执行以下命令的时候 adb install XXX.apk会提示执行异常 Performing Streamed Install adb: failed to install XXX.apk: Exception occurred while executing in…

java面试之ThreadLocal问题

什么是ThreadLocal,它的基本用法是什么 简单来说就是能在多线程中保持变量独立的线程对象 不用Threadlocal多线程访问同一个变量会出现的问题 package com.pxx;/*** Created by Administrator on 2023/9/3.*/ public class Demo1 {private String v1;public String getV1() …

群晖 DS918通过CISCO SG250 LACP 链路聚合效果不佳的问题解决

问题表现 使用的是CISCO交换机打开LACP 链路聚合&#xff0c;且DS918上完成接口聚合并配置为平衡TCP模式后。通过IPREF测速整体网络性能仅能达到300Mbps左右。 问题解决 检查CISCO交换机LAG配置中&#xff0c;针对DS918的接口组是否正确配置了流量配置。请按照如下图所示&#…

易云维®医院后勤管理系统软件利用物联网智能网关帮助实现医院设备实现智能化、信息化管理

近年来&#xff0c;我国医院逐渐意识到医院设备信息化管理的重要性&#xff0c;逐步建立医院后勤管理系统软件&#xff0c;以提高信息化管理水平。该系统是利用数据库技术&#xff0c;为医院的中央空调、洁净空调、电梯、锅炉、医疗设备等建立电子档案&#xff0c;把设备监控、…

Python中的PYTHONPATH

迷途小书童 读完需要 4分钟 速读仅需 2 分钟 大家好&#xff0c;我是迷途小书童&#xff01; 今天来聊聊 PYTHONPATH。 PYTHONPATH 是一个环境变量&#xff0c;它是一个列表&#xff0c;列表的元素是目录&#xff0c;也就是一些文件夹的路径&#xff0c;它告诉 Python 解释器去…

《自然的艺术形态》

艺术是科学的最高形式。《自然的艺术形态》是恩斯特海克尔在19世纪博物学和生物学的最高峰对自然界所作出的最美阐释。透过自然科学巨匠的慧眼&#xff0c;人类能多一个视角&#xff0c;认识栩栩如生的自然万物&#xff0c;其奇美&#xff0c;其壮观&#xff0c;若非建立在自然…

vs+opencv+QT调试程序

2021-09-28vsopencvQT简单的图像处理工程_opencv 用qt还是vs_二两山栀子的博客-CSDN博客 【vsopencvQt搭建简单的图像处理界面】https://www.bilibili.com/video/BV16T411j7XQ?vd_source0aeb782d0b9c2e6b0e0cdea3e2121eba 调试过程一直出现这种问题&#xff0c;后来改DEBUG为…

HDLBits 练习 Always if2 并给出逻辑简化过程

题目 Always if2 在前面的练习中我们使用了简单的逻辑门与一些逻辑门的组合。这些电路都可以作为组合电路的例子。 组合意味着这个电路的输出只是输入的函数&#xff08;数学意义上的&#xff09;。数学上的函数就意味着当你给定一个输入的时候 对应的只会有一个输出。因此有一…

ChatPaper临时升级教程

ChatPaper临时升级教程 文章目录 ChatPaper临时升级教程必要的声明&#xff1a;升级教程&#xff1a; 必要的声明&#xff1a; 最近只能手动发卡了&#xff0c;所以单独写一个手动升级的教程。 先声明一下付费的内容&#xff1a; 500K大概是30篇左右的总结&#xff1b; 200k大…

计算机网络的故事——HTTP首部

HTTP首部 在HTTP协议通信交互中使用的首部字段。不限于RFC2616中定义的47种首部字段&#xff0c;还有Cookie、setCookie和Content-Disposition等 HTTP 首部字段将定义成缓存代理和非缓存代理的行为&#xff0c;分成 2 种类型。端到端首部和逐跳首部

单向链表(c/c++)

链表是一种常见的数据结构&#xff0c;其中运用到了结构体指针&#xff0c;链表可以实现动态存储分配&#xff0c;换而言之&#xff0c;链表是一个功能强大的数组&#xff0c;可以在某个节点定义多种数据类型&#xff0c;可以实现任意的添加&#xff0c;删除&#xff0c;插入节…

通过nginx将https协议反向代理到http协议请求上

通过nginx将https协议反向代理到http协议请求上 1、问题背景2、介绍nginx的反向代理功能及配置https协议3、具体实现3.1 后端服务支持方式3.2 nginx重定向方式 3.3、nginx的反向代理方式4、关于nginx常用模块和指令 1、问题背景 目前一个系统仅支持https协议访问&#xff0c;因…

anaconda navigator打不开,一直在loading画面

anaconda navigator打不开&#xff0c;一直在loading画面。百度解决方法&#xff0c;用网上的方法在命令窗口里运行conda update anaconda结果一直显示 solving environment卡在那里。又尝试用管理员身份运行还是不行&#xff0c;打开后出现There in aninstance of Anaconda Na…

在MySQL中查看数据库和表的数据大小

在MySQL中查看数据库和表的数据大小 在管理和维护MySQL数据库时&#xff0c;了解数据库和表的数据大小是非常重要的。这可以帮助您监控数据库的增长、优化性能以及规划存储需求。本博客将介绍如何使用SQL查询来查看MySQL数据库和表的数据大小。 查看MySQL数据库的总数据大小 …

linux并发服务器 —— IO多路复用(八)

半关闭、端口复用 半关闭只能实现数据单方向的传输&#xff1b;当TCP 接中A向 B 发送 FIN 请求关闭&#xff0c;另一端 B 回应ACK 之后 (A 端进入 FIN_WAIT_2 状态)&#xff0c;并没有立即发送 FIN 给 A&#xff0c;A 方处于半连接状态 (半开关)&#xff0c;此时 A 可以接收 B…

vscode使用delve调试golang程序

环境配置 delve仓库&#xff0c;含有教程&#xff1a;https://github.com/go-delve/delve golang的debugging教程&#xff1a;https://github.com/golang/vscode-go/wiki/debugging > go version go version go1.20 windows/amd64> go install github.com/go-delve/de…

使用 Nacos 在 Spring Boot 项目中实现服务注册与配置管理

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

转录因子分析预测 cistrome db

Toolkit for CistromeDBhttp://dbtoolkit.cistrome.org/基因表达调控系列问题汇总(持续更新) - 知乎 (zhihu.com)