SLF4J Spring Boot日志框架

JAVA日志框架

JAVA有好多优秀的日志框架,比如log4j、log4j2、logback、JUL(java.util.logging)、JCL(JAVA Common Logging)等等,logback是后起之秀,是Spring Boot默认日志框架。

今天文章的目标不是研究JAVA的这些日志框架,而是在应用中处于他们前面的日志门面SLF4J,以及初步了解一下Spring Boot的默认日志框架是在什么地方配置的、怎么替换Spring Boot默认的日志框架。

SLF4J

SFL4J,全名Simple Logging Facade for Java,意思是简单JAVA日志门面,是Facade设计模式的一个典型实现。

SFL4J本身并没有日志的任何实现,它只是一个日志门面,目的是为了让应用层能够简单的、方便的使用以上提到的各种不同的日志框架,在代码层不做任何改动的情况下,在最终部署的时候决定具体使用哪一个日志框架。冲这一点,SLF4J就非常NB,能允许程序员在编码的时候不考虑具体使用哪一个日志框架、部署的时候不需要修改一行代码、随意选择日志框架。而且,虽然不是很有必要,但是只要你高兴,应用运行的过程中你都可以随时切换日志框架,比如你刚开始选择log4j2,但是后来觉得不爽想要换成logback,只要你在开发的时候使用了SLF4J,你就可以任性地随时切换。

使用SLF4J

严格来说,SLF4J只需要一个包:slf4j-api-xxx.jar(xxx是版本号) 就可以使用,POM文件中引入:

    <dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId></dependency>

之后:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class HelloWorld {public static void main(String[] args) {Logger logger = LoggerFactory.getLogger(HelloWorld.class);logger.info("Hello World");}
}

应用classpath下有任何前述日志框架存在的情况下,以上代码中的日志打印语句就会根据日志框架的配置输出到控制台或者日志文件中了。

SLF4J API及jar包框架

上述案例可以发现,原则上来说,项目Pom文件引入slf4j-api之后,SLF4J就可以正常工作了,但是SLF4J可以正常工作,并不代表整个日志系统可以正常工作,因为SLF4J只是日志门面,需要绑定后端的日志框架之后,日志系统才能正常工作。

我们先看一下SLF4J官网对于SLF4J API的框架图:
在这里插入图片描述
可以看出,如果我们采用SLF4J本家的日志框架作为我们应用的日志框架的话,比如用logback的话,我们只需要slf4j-api.jar + logback相关的jar包就可以了,或者我们用slf4j自带的simple日志框架的话,也只需要slf4j-api.jar + slf4j-simple.jar。

但是如果用其他的三方日志框架,比如log4j、或者JUL的话,由于三方框架比如log4j早在SLF4J出现之前就已经占据JAVA日志的半壁江山了,所以早期版本的日志框架不可能适应后面才出现的SLF4J,所以只能SLF4J自己想办法来适应。

SLF4J给出的解决方案就是针对各日志框架的API:


slf4j-log4j12-2.0.10.jar: 为绑定log4j version 1.2,这是一个很古老的版本了,早已宣布寿终正寝了,但是考虑到有些老系统可能还在使用log4j 1.x,所以提供了这个jar包以便兼容。其实从SLF4J 1.7.35之后,slf4j-log4j模块的调用就已经在编译器直接导航到slf4j-reload4j的调用了,所以这个模块对于新版本的SLF4J来说几乎没用了。


slf4j-reload4j-2.0.10.jar:后期版本的SLF4J绑定log4j日志框架,同时需要reload4j.jar(log4j 1.2x之后的替代品)。


slf4j-jdk14-2.0.10.jar: SLF4J绑定JUL(java14之后的内置日志框架)日志框架,同时需要JDK14。


slf4j-nop-2.0.10.jar: 应用不绑定任何日志框架的情况下的SLF4J的默认NOP实现,啥也没干,只是在应用运行进行日志框架绑定的时候不报错。


slf4j-simple-2.0.10.jar: SLF4J自己实现的一个简单的、轻量级日志框架,应该没人用。


slf4j-jcl-2.0.10.jar: 绑定Apache Commons Logging。


logback-classic-xxx.jar: logback日志框架,同时需要logback-core-xxx.jar,可直接支持SLF4J。


将以上jar包放入或移除当前应用(或者通过pom文件)后,就可以轻而易举的实现日志框架的更换,不需要你动一行代码,这就是SLF4J的魔力。

不绑定

如果不引入任何日志框架、代码中又使用了SLF4J的话,会是什么情况?

如果版本是SLF4J 1.6.0 之前的版本,没有加载任何日志框架的情况下,SLF4J在绑定日志框架的时候会抛出异常:NoClassDefFoundError 。因为绑定日志框架的时候找不到org.slf4j.impl.StaticLoggerBinder类。

之后的、SLF4J2.0之前的版本,控制台会打印:

SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See https://www.slf4j.org/codes.html#StaticLoggerBinder for further details.

SLF4J2.0以上,控制台会打印:

SLF4J: No SLF4J providers were found.
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See https://www.slf4j.org/codes.html#noProviders for further details.

SLF4J 1.6.0之后,如果你的项目并没有加载日志框架,SLF4J提供了一个日志哑实现,叫no-operation (NOP) logger implementation,这个NOP其实就是个假把式,所有的日志打印操作都不会得到任何输出,NOP啥都不干。

桥接API

如果你的已有应用没有使用SLF4J、而是使用JCL或log4j等JAVA日志框架,SLF4J非常贴心的为你提供了两种方案、让你能够以最小代价将as-is系统的日志框架切换到SLF4J上来:

  1. Bridging legacy APIs:桥接API
  2. slf4j-migrator:代码迁移工具

桥接API图:
在这里插入图片描述
通过桥接API,允许你将既有系统的基于log4j、JCL、JUL等日志框架的系统迁移到SLF4J上来。原理是:jcl-over-slf4j.jar替换原有的commons-logging.jar,应用中对原来的JCL日志框架的调用接口都被替换成了jcl-over-slf4j.jar中的桥接接口,jcl-over-slf4j.jar桥接接口会将日志接口API重新定向到SLF4J中、从而纳入到SLF4J体系中来。

slf4j-migrator代码迁移工具是直接对你的代码动手术的,支持JCL、log4j、JUL的迁移:
在这里插入图片描述

Spring Boot默认日志框架

Spring Boot的默认日志框架是在spring-boot-starter中指定的,spring-boot-starter中包含了spring-boot-starter-logging:

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

而spring-boot-starter-logging中引入了logback:

<dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.4.11</version><scope>compile</scope></dependency>

所以,Spring Boot的默认日志框架是logback。

如果想要替换默认的日志框架,比如换成log4j2,首先需要在pom文件中加入依赖:

        <dependency><groupId>org.slf4j</groupId><artifactId>slf4j-reload4j</artifactId><version>2.0.10</version></dependency>

之后直接启动应用,发现:
在这里插入图片描述
这个信息是在应用系统的早期就打印出来了,说明日志框架的加载在Spring Boot启动过程中是比较早的。

然后,logback也可以正常工作了(代码中加入log可以检查一下底层日志框架到底是啥):

    public HelloWorldController(){log.info("---1=2---");log.info(log.getClass().getName());}

在这里插入图片描述
但是我们想要替换logback为log4j2的目标却没有实现!

原因在上面第一张图里已经说了,SLF4J在classpath下发现了两个provider(我们用的是SLF4J2.x版本,通过provider绑定底层日志框架)。

发现两个provider的话,SLF4J会依赖JVM随机绑定一个,我们测试的这个案例是绑定了logback。

怎么能让它绑定log4j呢?

其实通过SLF4J的源码发现了一种方法,就是指定环境变量:
在这里插入图片描述

System property for explicitly setting the provider class. If set and the provider could be instantiated, then the service loading mechanism will be bypassed.
Since:2.0.9

如果指定了这个环境变量,SLF4J的加载机制就会被跳过而直接加载指定的provider,比如:
在这里插入图片描述
从代码看这个设置也是应该能生效的:

static List<SLF4JServiceProvider> findServiceProviders() {List<SLF4JServiceProvider> providerList = new ArrayList<>();// retain behaviour similar to that of 1.7 series and earlier. More specifically, use the class loader that// loaded the present class to search for servicesfinal ClassLoader classLoaderOfLoggerFactory = LoggerFactory.class.getClassLoader();SLF4JServiceProvider explicitProvider = loadExplicitlySpecified(classLoaderOfLoggerFactory);if(explicitProvider != null) {providerList.add(explicitProvider);return providerList;}

loadExplicitlySpecified方法:

static SLF4JServiceProvider loadExplicitlySpecified(ClassLoader classLoader) {String explicitlySpecified = System.getProperty(PROVIDER_PROPERTY_KEY);if (null == explicitlySpecified || explicitlySpecified.isEmpty()) {return null;}try {String message = String.format("Attempting to load provider \"%s\" specified via \"%s\" system property", explicitlySpecified, PROVIDER_PROPERTY_KEY);Util.report(message);Class<?> clazz = classLoader.loadClass(explicitlySpecified);Constructor<?> constructor = clazz.getConstructor();Object provider = constructor.newInstance();return (SLF4JServiceProvider) provider;} catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {String message = String.format("Failed to instantiate the specified SLF4JServiceProvider (%s)", explicitlySpecified);Util.report(message, e);return null;} catch (ClassCastException e) {String message = String.format("Specified SLF4JServiceProvider (%s) does not implement SLF4JServiceProvider interface", explicitlySpecified);Util.report(message, e);return null;}}

如果指定了这个系统参数的话,就直接通过classloader实例化这个provider…但是确实没有测试成功,暂时没找到原因。

我们还有另外一个启用log4j2、停用logback的方案,pom文件排除logback:

        <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><exclusions><exclusion><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId></exclusion></exclusions></dependency>

之后刷新pom,发现logback的引用已经消失了。
刷新前:
在这里插入图片描述
刷新后:
在这里插入图片描述

启动应用:
在这里插入图片描述
就会发现Spring Boot项目使用log4j就没有用logback那么舒服了,log4j是需要配置的。随便放一个log4j.properties配置文件就可以正常工作了。

@Slf4j注解

@Slf4j是lombok的一个注解,所以你就能知道他其实没啥,语法糖而已,帮着你在当前类生成一个:

private static final org.slf4j.Logger log =org.slf4j.LoggerFactory.getLogger(Youclass.class);

Ohhh…OK

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

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

相关文章

oracle19c容器数据库rman备份特性-----性能优化(三)

目录 冗余备份片 1.备份的时候指定 2.rman配置中设定 归档备份&#xff08;将备份集保留&#xff09; 二级备份&#xff08;将备份文件保留&#xff09; 1.备份闪回恢复区的恢复文件 2.备份所有恢复文件 recovery catalog database 1.創建recovery catalog 2.创建VPC…

zabbix-proxy代理安装及其他监控方式

zabbix-proxy代理安装及其他监控方式 安装zabbix-proxyserver端配置zabbix-proxy配置被监控的agent安装中问题解决监控网络设备JMX和IPMI监控方式 zabbix-proxy的安装&#xff0c;至少需要准备三台机器&#xff0c;一台安装服务端&#xff0c;一台安装agent端&#xff0c;这里就…

《网络是怎样连接的》2.3节图表(自用)

图4.1&#xff1a;TCP拆分数据与ACK号 图4.2&#xff1a;连接阶段与通信阶段ACK号与序号的交互过程 首先&#xff0c;客户端在连接时需要计算出与从客户端到服务器方向通信相关的序号初始值&#xff0c;并将这个值发送给服务器&#xff08;①&#xff09;。 接下来&#xff0c…

基于SpringBoot的企业OA管理系统

文章目录 项目介绍主要功能截图:部分代码展示设计总结项目获取方式🍅 作者主页:超级无敌暴龙战士塔塔开 🍅 简介:Java领域优质创作者🏆、 简历模板、学习资料、面试题库【关注我,都给你】 🍅文末获取源码联系🍅 项目介绍 基于SpringBoot的企业OA管理系统,java项…

【C++PCL】点云处理LCCP分割

作者:迅卓科技 简介:本人从事过多项点云项目,并且负责的项目均已得到好评! 公众号:迅卓科技,一个可以让您可以学习点云的好地方 本专栏特色:根据经验和大家分享每个参数的调试规范,解决大家因为参数的问题而产生的苦恼。 目录 1.原理介绍 2.代码效果 3.源码展示

HTTP协议九种请求方法的异同

HTTP&#xff08;Hypertext Transfer Protocol&#xff09;定义了多种请求方法&#xff08;也称为HTTP方法或动作&#xff09;&#xff0c;用于指定客户端对服务器执行的操作。以下是HTTP协议中常见的九种请求方法&#xff1a; 1. **GET&#xff1a;** - 用于请求指定的资源…

在k8s集群中部署多nginx-ingress

关于ingress的介绍&#xff0c;前面已经详细讲过了&#xff0c;参考ingress-nginx详解和部署方案。本案例ingress的部署使用deploymentLB的方式。 参考链接&#xff1a; 多个ingress部署 文章目录 1. 下载ingress的文件2. 文件资源分析3. 部署ingress3.1 部署第一套ingress3.1…

C# 反射的乌云,MethodInfo的Json序列化参数入参问题

文章目录 前言直接运行MethodInfo运行结果 Json解决ParamterInfo实例化运行结果无法实例化问题部分参数的问题 Json反序列化 经过长达一天的研究&#xff0c;我终于完全的解决的了实战思路方法测试用例运行测试运行结果 代码总结总结 前言 我上篇文章已经基本解决了反射的基本…

众和策略:沪指跌0.91%险守2900点,半导体、金融等板块走低

8日早盘&#xff0c;两市股指低开低走&#xff0c;沪指一度失守2900点&#xff0c;深成指、创业板指跌约1%&#xff0c;科创50指数创前史新低。 到午间收盘&#xff0c;沪指跌0.91%报2902.4点&#xff0c;深成指跌1.17%&#xff0c;创业板指跌0.99%&#xff0c;科创50指数跌超…

python每日学12:面向对象的编程

背景&#xff1a;最近有些时间&#xff0c;打算把python每日学的计划继续执行起来。由于不知道从何入手&#xff0c;就暂定从学习《python学习手册》开始&#xff0c;这本书也是python领域的一本权威著作&#xff0c;各方面都写的非常详细&#xff0c;读起来很有帮助&#xff0…

pythonPandas四:数据操作与转换

当涉及到数据操作和转换时&#xff0c;Pandas提供了许多有用的功能。以下是一些示例说明&#xff1a; 1.数据选择和操作&#xff1a; import pandas as pd# 创建一个示例DataFrame data {Name: [Alice, Bob, Charlie],Age: [25, 30, 35],City: [New York, London, Paris]} d…

HTTP 请求参数之三种格式

Query String Parameters 、Form Data 、 Request Payload 三种格式的区别。主要是因为 Content-Type&#xff08;用于表明发送数据流的类型&#xff09; 与请求方式 method 不同&#xff0c;导致传递的数据格式不同。 1. Query String Parameters 格式&#xff1a; ?keyvalu…

07GoF之三种工厂模式

GoF&#xff08;Gang of Four&#xff09;:四人组,《Design Patterns: Elements of Reusable Object-Oriented Software》&#xff08;《设计模式》)的作者,设计了23种设计模式.但时代发展,还有其它的设计模式&#xff0c;比如&#xff1a;JavaEE的设计模式&#xff08;DAO模式…

VSCode安装GitHub Copilot插件方法

VSCode安装GitHub Copilot插件的步骤及注意事项如下&#xff1a; 安装步骤&#xff1a; 确保系统要求&#xff1a; 确保你正在使用的Visual Studio Code版本是最新的&#xff0c;且支持GitHub Copilot。同时&#xff0c;Copilot需要你的操作系统是Windows、macOS或Linux&#x…

如何通过PreMaint状态监测发现设备故障:以振动监测为例

在现代工业环境中&#xff0c;设备的健康状况对于维持生产效率至关重要。计划外停机可能导致巨大的成本损失&#xff0c;因此采用先进的监测技术成为预防性维护的核心策略之一。其中&#xff0c;振动监测作为一种早期故障检测手段&#xff0c;通过PreMaint状态监测系统的引入&a…

【前端面试题】每日一个前端面试专题

一、typeof 与 instanceof 区别?如何instanceof的原理 问题回答思路 typeof和instanceof 都可以检测数据类型&#xff0c;typeof检测如果是基本数据类型返回对应的结果。如果检测是对象的话&#xff0c;默认返回一个object&#xff0c;所以无法区分那种对象。 typeof 数据ins…

组件封装原则

在Vue中封装组件时&#xff0c;遵循低耦合、高内聚、可扩展性和可维护性的原则非常重要。以下是一些实现这些原则的关键点&#xff0c;并结合Vue的实践方式给出案例说明&#xff1a; 1. 单一职责原则&#xff1a; 每个组件专注于一个特定的功能或UI部分&#xff0c;例如&#…

Java动态代理机制,创建代理对象的方法(InvocationHandler,Proxy)

目录 1.什么是动态代理2.如何为Java对象创建一个代理对象 1.什么是动态代理 特点&#xff1a;无侵入式的给代码增加额外的功能。 在Java中&#xff0c;动态代理是一种机制&#xff0c;允许在运行时创建一个代理对象来代替原始对象&#xff0c;并可以在代理对象的方法执行前后追…

Hive操作命令上手手册

内容来自于《大数据Hive离线计算开发实战》 Hive原理 Hive是一个基于Hadoop的数据仓库和分析系统&#xff0c;用于管理和查询大型数据集。以下是Hive的原理&#xff1a; 数据仓库&#xff1a;Hive将结构化的数据文件映射成一张表&#xff0c;并提供类SQL查询功能。用户可以使…

第十五章 ECMAScript6新增的常用语法

文章目录 一、声明关键字二、箭头函数三、解构赋值四、展开运算符五、对字符的补充六、Symbol七、对象的简写语法八、Set和Map九、for-of 一、声明关键字 ES6新增的声明关键字&#xff1a; let&#xff0c;const&#xff1a;声明变量class&#xff1a;声明类import&#xff0c…