Java日志框架:slf4j作用及其实现原理

SLF4J使用教程以及常见问题解决(最新稳定版) 1.简介 官网:https://www.slf4j.org/manual.html (1)简单日记门面(simple logging Facade for Java)SLF4J是为各种loging APIs提供一个简单统一的接口。 (2)slf4j并不是一种具体的日志系统,而是一个用户日志系统的facade。 (3)在部署时,选择不同的日志系统包,即可自动转换到不同的日志系统上。 如:选择JDK自带的日志系统,则只需要将slf4j-api-XXX.jar和slf4j-jdkXXX.jar放置到classpa 阅读详情

转载,原文地址https://www.cnblogs.com/xrq730/p/8619156.html,代码演示部分有轻微改动

简单回顾门面模式

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

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

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

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

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

我们为什么要使用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有更深刻的认识。

  <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>
        <!-- slf4j日志框架 -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.25</version>
        </dependency>
        <!-- logback日志框架 -->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
        </dependency>
        <!-- slf4j-simple日志框架 -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>1.7.25</version>
        </dependency>
        <!-- log4j日志框架,需要额外的桥接包依赖 -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.14.1</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.25</version>
        </dependency>
    </dependencies>

写一段简单的Java代码:

import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


public class LogTest {

    @Test
    public void testSlf4j() {
        Logger logger = LoggerFactory.getLogger(Object.class);
        logger.error("123");
    }

}

接着我们修改一下pom文件,只引入slf4j日志框架,而不引入任何slf4j的实现类,运行Test方法,我们看一下控制台的输出为:

 <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>
        <!-- slf4j日志框架 -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.25</version>
        </dependency>
</dependencies>

看到没有任何日志的输出,这验证了我们的观点:slf4j不提供日志的具体实现,只有slf4j是无法打印日志的。

接着随意引入任何一个slf4j的日志实现框架,如log4j2(未打印信息,因为log4j2必须提供配置文件)

如slf4j-simple

如logback

看来我们只要引入了一个slf4j的具体实现类,即可使用该日志框架输出日志。

最后做一个测验,我们把所有日志打开,引入logback-classic、slf4j-simple、log4j,运行Test方法,控制台输出为:

和上面的差别是,可以输出日志,但是会输出一些告警日志,提示我们同时引入了多个slf4j的实现,然后选择其中的一个作为我们使用的日志系统。(图2是在修改pom,将logback框架的引入放在其他框架之前的运行结果,大致猜测,应该是选择引入顺序靠前的那个框架来作为日志输出者吧)

从例子我们可以得出一个重要的结论,即slf4j的作用:只要所有代码都使用门面对象slf4j,我们就不需要关心其具体实现,最终所有地方使用一种具体实现即可,更换、维护都非常方便。

slf4j实现原理

slf4j的用法就是常年不变的一句"Logger logger = LoggerFactory.getLogger(Object.class);"

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 " + LOGGER_NAME_MISMATCH_URL + " for an explanation");
        }
    }
    return logger;
}

从第2行开始跟代码,一直跟到LoggerFactory的bind()方法:

private final static void bind() {
    try {
        Set<URL> staticLoggerBinderPathSet = null;
        // skip check under android, see also
        // http://jira.qos.ch/browse/SLF4J-328
        if (!isAndroid()) {
            staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
            reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);
        }
        // the next line does the binding
        StaticLoggerBinder.getSingleton();
        INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
        reportActualBinding(staticLoggerBinderPathSet);
        fixSubstituteLoggers();
        replayEvents();
        // release all resources in SUBST_FACTORY
        SUBST_FACTORY.clear();
    } catch (NoClassDefFoundError ncde) {
        String msg = ncde.getMessage();
        if (messageContainsOrgSlf4jImplStaticLoggerBinder(msg)) {
            INITIALIZATION_STATE = NOP_FALLBACK_INITIALIZATION;
            Util.report("Failed to load class \"org.slf4j.impl.StaticLoggerBinder\".");
            Util.report("Defaulting to no-operation (NOP) logger implementation");
            Util.report("See " + NO_STATICLOGGERBINDER_URL + " for further details.");
        } else {
            failedBinding(ncde);
            throw ncde;
        }
    } catch (java.lang.NoSuchMethodError nsme) {
        String msg = nsme.getMessage();
        if (msg != null && msg.contains("org.slf4j.impl.StaticLoggerBinder.getSingleton()")) {
            INITIALIZATION_STATE = FAILED_INITIALIZATION;
            Util.report("slf4j-api 1.6.x (or later) is incompatible with this binding.");
            Util.report("Your binding is version 1.5.5 or earlier.");
            Util.report("Upgrade your binding to version 1.6.x.");
        }
        throw nsme;
    } catch (Exception e) {
        failedBinding(e);
        throw new IllegalStateException("Unexpected initialization failure", e);
    }
}

这个地方第7行是一个关键,看一下代码:

static Set<URL> findPossibleStaticLoggerBinderPathSet() {
    // use Set instead of list in order to deal with bug #138
    // LinkedHashSet appropriate here because it preserves insertion order
    // during iteration
    Set<URL> staticLoggerBinderPathSet = new LinkedHashSet<URL>();
    try {
        ClassLoader loggerFactoryClassLoader = LoggerFactory.class.getClassLoader();
        Enumeration<URL> paths;
        if (loggerFactoryClassLoader == null) {
            paths = ClassLoader.getSystemResources(STATIC_LOGGER_BINDER_PATH);
        } else {
            paths = loggerFactoryClassLoader.getResources(STATIC_LOGGER_BINDER_PATH);
        }
        while (paths.hasMoreElements()) {
            URL path = paths.nextElement();
            staticLoggerBinderPathSet.add(path);
        }
    } catch (IOException ioe) {
        Util.report("Error getting resources from path", ioe);
    }
    return staticLoggerBinderPathSet;
}

这个地方重点其实就是第12行的代码,getLogger的时候会去classpath下找STATIC_LOGGER_BINDER_PATH,STATIC_LOGGER_BINDER_PATH值为"org/slf4j/impl/StaticLoggerBinder.class",即所有slf4j的实现,在提供的jar包路径下,一定是有"org/slf4j/impl/StaticLoggerBinder.class"存在的

我们不能避免在系统中同时引入多个slf4j的实现,所以接收的地方是一个Set。大家应该注意到,上部分在演示同时引入logback、slf4j-simple、log4j的时候会有警告,这就是因为有三个"org/slf4j/impl/StaticLoggerBinder.class"存在的原因,此时reportMultipleBindingAmbiguity方法控制台输出语句:

private static void reportMultipleBindingAmbiguity(Set<URL> binderPathSet) {
    if (isAmbiguousStaticLoggerBinderPathSet(binderPathSet)) {
        Util.report("Class path contains multiple SLF4J bindings.");
        for (URL path : binderPathSet) {
            Util.report("Found binding in [" + path + "]");
        }
        Util.report("See " + MULTIPLE_BINDINGS_URL + " for an explanation.");
    }
}

那网友朋友可能会问,同时存在三个"org/slf4j/impl/StaticLoggerBinder.class"怎么办?首先确定的是这不会导致启动报错,其次在这种情况下编译期间,编译器会选择其中一个StaticLoggerBinder.class进行绑定,这个地方sfl4j也在reportActualBinding方法中报告了绑定的是哪个日志框架:

 private static void reportActualBinding(Set<URL> binderPathSet) {
     // binderPathSet can be null under Android
     if (binderPathSet != null && isAmbiguousStaticLoggerBinderPathSet(binderPathSet)) {
         Util.report("Actual binding is of type [" + StaticLoggerBinder.getSingleton().getLoggerFactoryClassStr() + "]");
     }
 }

对照上面的截图,看最后一行,确实是"Actual binding is of type..."这句。

最后StaticLoggerBinder就比较简单了,不同的StaticLoggerBinder其getLoggerFactory实现不同,拿到ILoggerFactory之后调用一下getLogger即拿到了具体的Logger,可以使用Logger进行日志输出。

【一起学UniGUI】--UniGUI的部署选项(5) uniGUI支持Windows平台下所有主要部署选项。 1.Standalone Server (独立服务器) 最简单的部署方法是独立服务器。在这种模式下,应用程序服务器像桌面应用程序一样直接运行。它也是用于调试应用程序的模式。运行应用程序可执行文件后(它是一个EXE文件),它将最小化到托盘图标,并运行到用户手动终止为止。见下图: 一个独立的应用程序可以... 阅读详情

相关推荐

高通平台camera构架sensor驱动详解

优先阅读。

a5566255的专栏 3253

深入理解 SpringBoot 日志框架:从入门到高级应用——(一)日志框架原理

深入理解 SpringBoot 日志框架:从入门到高级应用——(一)日志框架原理

赵士杰——成功并非一蹴而就,而是在持之以恒的努力中逐渐实现。 1591

从零开始掌握Python机器学习:七步教程 基础篇

前言 「开始」往往是最难的,尤其是当选择太多的时候,一个人往往很难下定决定做出选择。本教程的目的是帮助几乎没有 Python 机器学习背景的新手成长为知识渊博的实践者,而且这个过程中仅需要使用免费的材料和资源即可。 第一步:基本 Python 技能 如果我们打算利用 Python 来执行机器学习,那么对 Python 有一些基本的了解就是至关重要的。 首先,你需要安装 Python。因为我们后...

hcgx2018的博客 1613

微服务学习系列15:日志框架slf4j使用和原理

slf4j主要是为了给Java日志访问提供一个标准、规范的API框架,其主要意义在于提供接口,具体的实现可以交由其他日志框架,例如log4j和logback等。对于一般的Java项目而言,日志框架会选择slf4j-api作为门面,配上具体的实现框架(log4j、logback等),中间使用桥接器完成桥接。例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。

yangyanping20108的博客 6855

频偏估计技术:从原理到5G应用的实战解析

本文深入解析了5G通信中的频偏问题及其核心技术。频偏如同调不准的旋钮,会导致信号失真与载波间干扰。文章详细拆解了整数倍与小数倍频偏估计两大核心方法,重点剖析了基于循环前缀和基于同步信号块的算法原理与优劣,并结合eMBB、mMTC、URLLC三大5G应用场景,提供了实战算法选择策略与参数调优避坑指南。

weixin_29218963的博客 264

WSL2下用QEMU模拟ARM开发板:从编译uboot到调试全流程(附常见错误解决)

本文详细介绍了在WSL2环境下使用QEMU模拟ARM开发板的完整流程,包括uboot编译、内核调试等关键步骤。通过QEMU模拟vexpress-a9开发板,开发者可以高效搭建嵌入式开发环境,解决常见错误并提升开发效率。

solidity8miner的博客 286

java日志框架详解-slf4j

java日志框架详解-slf4j

深圳市多克创新科技有限公司 1959

docker配置国内镜像源

【代码】docker配置国内镜像源。

愿听风成曲 9616

Java 日志框架:slf4j 作用及其实现原理

Java面试笔试面经、Java技术每天学习一点Java面试关注不迷路作者:五月的仓颉来源:https://www.cnblogs.com/xrq730slf4j是门面模式的典型应用,因此...

java面试笔试 587

手把手教你用STM32F103C8T6驱动HUB75 LED点阵屏(附74HC595级联代码)

本文详细介绍了如何使用STM32F103C8T6开发板驱动HUB75接口的LED点阵屏,包括硬件连接、74HC595级联代码、时序控制及灰度显示实现。通过实战案例展示动态文字显示和性能优化技巧,帮助开发者快速掌握LED点阵屏驱动技术。

weixin_42623010的博客 342

大厂校招:星宸科技嵌入式面试及参考答案(5万字长文)

当一个新的硬件设备被插入系统时,内核会发送一个设备事件通知 udev,udev 会根据设备的属性和规则,为该设备创建一个合适的设备文件。通过去除不必要的驱动程序、禁用不需要的功能、选择合适的内核配置选项、优化内核编译选项和定制内核模块等方法,可以减小内核的大小和复杂性,提高系统的性能和稳定性。上半部通常是在中断关闭的情况下执行的,以确保中断处理的原子性。处理碎片化内存是内存管理中的一个重要问题,可以采用内存压缩、内存池管理、伙伴系统、首次适应算法和最佳适应算法等方法来减少内存碎片的产生,提高内存的利用率。

大模型大数据攻城狮的专栏 2929

【Log日志】Java日志框架:slf4j作用及其实现原理

简单回顾门面模式 slf4j是门面模式的典型应用,因此在讲slf4j前,我们先简单回顾一下门面模式, 门面模式,其核心为外部与一个子系统的通信必须通过一个统一的外观对象进行,使得子系统更易于使用。用一张图来表示门面模式的结构为: 门面模式的核心为Facade即门面对象,门面对象核心为几个点: 知道所有子角色的功能和责任 将客户端发来的请求委派到子系统中,没有实际业务逻辑 不参与子系...

anndy_的专栏 577

YOLO + SubspaceAD:一张良品图,检出所有未知缺陷

当YOLO遇上子空间建模,工业质检正迎来从“见多识广”到“举一反三”的质变时刻。

①答疑群聊服务;②YOLO大模型知识问答系统;③计算机视觉论文生成智能体; 500

java slf4j 例子_浅谈Java日志框架slf4j作用及其实现原理

SLF4J是一个日志框架抽象层,底下绑定具体的日志框架,比如说Log4J,Logback,Java Logging API等。SLF4J也有自身的默认实现,但是我们还是主要以日志框架抽象层的身份使用SLF4J。要使用SLF4J,得包含对"org.slf4j:slf4j-api"的依赖。简单回顾门面模式slf4j是门面模式的典型应用,因此在讲slf4j前,我们先简单回顾一下门面模式,门面模式,其核心...

weixin_35241413的博客 270

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

html打印日志_Java日志框架:slf4j作用及其实现原理

简单回顾门面模式slf4j是门面模式的典型应用,因此在讲slf4j前,我们先简单回顾一下门面模式,门面模式,其核心为外部与一个子系统的通信必须通过一个统一的外观对象进行,使得子系统更易于使用。用一张图来表示门面模式的结构为:门面模式的核心为Facade即门面对象,门面对象核心为几个点:知道所有子角色的功能和责任将客户端发来的请求委派到子系统中,没有实际业务逻辑不参与子系统内业务逻辑的实现...

weixin_39784263的博客 251

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

java 日志_Java基础:Java日志框架:slf4j作用及其实现原理

简单回顾门面模式slf4j是门面模式的典型应用,因此在讲slf4j前,我们先简单回顾一下门面模式,门面模式,其核心为外部与一个子系统的通信必须通过一个统一的外观对象进行,使得子系统更易于使用。用一张图来表示门面模式的结构为:门面模式的核心为Facade即门面对象,门面对象核心为几个点:知道所有子角色的功能和责任将客户端发来的请求委派到子系统中,没有实际业务逻辑不参与子系统内业务逻辑的实现大致上来看...

weixin_39743414的博客 186

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

java sl4j 日志_Java日志框架:slf4j作用及其实现原理

简单回顾门面模式slf4j是门面模式的典型应用,因此在讲slf4j前,我们先简单回顾一下门面模式,门面模式,其核心为外部与一个子系统的通信必须通过一个统一的外观对象进行,使得子系统更易于使用。用一张图来表示门面模式的结构为:门面模式的核心为Facade即门面对象,门面对象核心为几个点:知道所有子角色的功能和责任将客户端发来的请求委派到子系统中,没有实际业务逻辑不参与子系统内业务逻辑的实现大致上来看...

weixin_39630909的博客 144
上一篇: spring 问题收集
下一篇: log4j2配置解析
MrMoving
博客等级 码龄10年 54粉丝 145原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值