java日志门面之JCL和SLF4J

news2024/9/28 9:41:12

文章目录

  • 前言
  • 一、JCL
    • 1、JCL简介
    • 2、快速入门
    • 3、 JCL原理
  • 二、SLF4J
    • 1、SLF4J简介
    • 2、快速入门
      • 2.1、输出动态信息
      • 2.2、异常信息的处理
    • 3、绑定日志的实现
      • 3.1、slf4j实现slf4j-simple和logback
      • 3.2、slf4j绑定适配器实现log4j
      • 3.2、@Slf4j注解
    • 4、桥接旧的日志框架
      • 4.1、log4j日志重构为slf4j+logback的组合

前言

什么时日志门面?

  随着系统开发的进行,可能会更新不同的日志框架,造成当前系统中存在不同的日志依赖,让我们难以统一的管理和控制。借鉴JDBC的思想,为日志系统也提供一套门面,那么我们就可以面向这些接口规范来开发,避免了直接依赖具体的日志框架。这样我们的系统在日志中,就存在了日志的门面日志的实现

常见的日志框架及日志门面

  • 常见日志门面:JCL、slf4j
  • 常见日志实现:JUL、log4j、logback、log4j2
  • 框架诞生顺序:log4j --> JUL --> JCL --> slf4j --> logback --> log4j2

日志门面和日志实现的关系

在这里插入图片描述

一、JCL

1、JCL简介

  • 全称为Jakarta Commons Logging,是Apache提供的一个通用日志API
  • common-logging会通过动态查找的机制,在程序运行时自动找出log4j,或者jdk自带的jul
  • 使用它的好处就是,代码依赖是common-logging而非log4j的API, 避免了和具体的日志API直接耦合,在有必要时,可以更改日志实现的第三方库(不改变代码,只修改依赖
  • JCL有两个基本的抽象类:Log(日志记录器),LogFactory(日志工厂负责创建Log实例)

2、快速入门

jcl依赖

<dependency>
    <groupId>commons-logging</groupId>
    <artifactId>commons-logging</artifactId>
    <version>1.2</version>
</dependency>
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class JCLTest {
    @Test
    public void test01() {
        Log log = LogFactory.getLog(JCLTest.class);
        log.info("info");
    }
}

只导入commons-logging的输出结果

  • 此时没有任何第三方日志框架,我们使用的就是jdk自带的JUL

在这里插入图片描述

导入commons-logging并添加log4j依赖log4j.properties配置文件输出结果

<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>
#配置根节点logger
log4j.rootLogger=info,console

log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%-10p] [%t] [%d{yyyy-MM-dd HH:mm:ss:SSS}] [%l] %m%n

在这里插入图片描述

3、 JCL原理

  • 通过LogFactory动态加载Log实现类
  • Jdk14Logger就是jdk自带JUL日志框架(因为JUL从jdk1.4开始提供)
  • SimpleLog是日志门面JCL自带日志,功能简单一般不用

在这里插入图片描述

  • 日志门面创建公共接口org.apache.commons.logging.Log
  • 日志实现类Logger实现接口Log,这样对外暴露的Log不变,只需要动态加载不同的Logger
  • 如果只导入JCL门面,不导入其他日志实现,那么日志实现为jdk自带JUL
  • 如果导入JCL门面,并导入log4j依赖,那么日志实现为log4j,log4j优先级最高

在这里插入图片描述

二、SLF4J

1、SLF4J简介

  • 简单日志门面(Simple Logging Facade For Java) SLF4J主要是为了给Java日志访问提供一套标准、规范的API框架
  • 主要意义在于提供接口,具体的实现可以交由其他日志框架,例如log4j和logback等
  • slf4j自己也提供了功能较为简单的实现,但是一般很少用到
  • SLF4J最重要的两个功能就是对于日志框架的绑定以及日志框架的桥接

2、快速入门

slf4j依赖

<!--slf4j 核心依赖-->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.25</version>
</dependency>

<!--slf4j 自带的简单日志实现 -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-simple</artifactId>
    <version>1.7.25</version>
</dependency>
  • slf4j-api只提供api,具体日志实现由slf4j-simple提供
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class SLF4JTest {
    @Test
    public void test01(){
        Logger logger = LoggerFactory.getLogger(SLF4JTest.class);
        logger.trace("trace信息");
        logger.debug("debug信息");
        logger.info("info信息");
        logger.warn("warn信息");
        logger.error("error信息");
    }
}

输出结果:

在这里插入图片描述

2.1、输出动态信息

  • Logger方法:public void info(String format, Object... arguments);
@Test
public void test02() {
    Logger logger = LoggerFactory.getLogger(SLF4JTest.class);
    String name = "zs";
    int age = 23;
    // logger.info("学生信息-姓名:"+name+";年龄:"+age); 字符串拼接,效率低
    // logger.info("学生信息-姓名:{},年龄:{}",new Object[]{name,age}); 老方式,代码冗余
    logger.info("学生信息-姓名:{},年龄:{}", name, age);// 新方式,简单
}

输出结果:

在这里插入图片描述

2.2、异常信息的处理

  • Logger方法:public void info(String msg, Throwable t);
@Test
public void test03() {
    Logger logger = LoggerFactory.getLogger(SLF4JTest.class);
    try {
        Class.forName("aaa");
    } catch (ClassNotFoundException e) {
        // 打印栈追踪信息
        // e.printStackTrace();
        logger.info("具体错误是:", e);
    }
}

输出结果:

在这里插入图片描述

3、绑定日志的实现

使用slf4j绑定日志的流程

  • 添加slf4j-api的依赖
  • 使用slf4j的API在项目中进行统一的日志记录
  • 绑定具体的日志实现框架
    • 绑定已经实现了slf4j的日志框架,直接添加对应依赖
    • 绑定没有实现slf4j的日志框架,先添加日志的适配器,再添加实现类的依赖
  • slf4j有且仅有一个日志实现框架的绑定(如果出现多个默认使用第一个依赖日志实现)

使用slf4j绑定日志的原理

  • public class StaticLoggerBinder implements LoggerFactoryBinder
  • 这个类负责静态org.slf4j.LoggerFactory类与相应的日志实现ILoggerFactory类绑定
  • 只要有这个类就可以实现slf4j门面+对应日志实现,包名都是org.slf4j.impl

3.1、slf4j实现slf4j-simple和logback

  slf4j-simple和logback都是slf4j门面出现后才有的日志实现,所以这两生来就有自己的StaticLoggerBinder类。

在这里插入图片描述

依赖

<!--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 自带的简单日志实现 -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-simple</artifactId>
    <version>1.7.25</version>
</dependency>

导包

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
  • 如果有多个日志实现的话,会出红色警告,默认使用先导入的实现
  • 无论使用slf4j自己的slf4j-simple还是logback,导包都不会变,都用的以上两个
@Test
public void test04(){
    Logger logger = LoggerFactory.getLogger(SLF4JTest.class);
    logger.trace("trace信息");
    logger.debug("debug信息");
    logger.info("info信息");
    logger.warn("warn信息");
    logger.error("error信息");
}

输出结果:

在这里插入图片描述

3.2、slf4j绑定适配器实现log4j

  • 由于log4j是在slf4j之前出品的日志框架实现,所以并没有遵循slf4j的API规范
  • 之前集成的logback,是slf4j之后出品的日志框架实现,就是按照slf4j的标准指定的API,所以我们导入依赖就能用
  • 如果想要使用slf4j门面,需要绑定一个适配器slf4j-log4j12

依赖

<!--slf4j 核心依赖-->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.25</version>
</dependency>

<!-- 
导入log4j适配器依赖
slf4j-log4j12依赖的slf4j-api,slf4j-api可以不用重复导入依赖
 -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.25</version>
</dependency>

导包

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Test
public void test05(){
    Logger logger = LoggerFactory.getLogger(SLF4JTest.class);
    logger.trace("trace信息");
    logger.debug("debug信息");
    logger.info("info信息");
    logger.warn("warn信息");
    logger.error("error信息");
}

log4j.properties

#指定日志的输出级别与输出端
log4j.rootLogger=info,console

# 配置appender输出方式:输出到控制台
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%-10p] [%t] [%d{yyyy-MM-dd HH:mm:ss:SSS}] [%l] %m%n

输出结果:

在这里插入图片描述

  与slf4j-simple和logback的实现原理其实一样,都是在相同包下org.slf4j.impl下创建StaticLoggerBinder类将slf4j门面与日志实现绑定起来。log4j需要适配器slf4j-log4j12,JUL需要适配器slf4j-jdk14

在这里插入图片描述

3.2、@Slf4j注解

  • 源代码
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class LogTest {
    @Test
    public void test() {
        log.info("hello world");
    }
}
  • 编译后class

在这里插入图片描述

4、桥接旧的日志框架

桥接解决的问题:当系统中存在之前的日志API,可以通过桥接转换到slf4j的实现

  • 先去除之前老的日志框架的依赖
  • 添加SLF4J提供的桥接组件
  • 为项目添加SLF4J的具体实现

4.1、log4j日志重构为slf4j+logback的组合

重构前

依赖

<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

log4j.properties

#配置根节点logger
log4j.rootLogger=trace,console

log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.conversionPattern=[%-10p]%r %c%t%d{yyyy-MM-dd HH:mm:ss:SSS} %m%n

导包

import org.apache.log4j.Logger;
@Test
public void test07(){
    Logger logger = Logger.getLogger(Log4jTest.class);
    logger.info("info信息");
}
  • 查看Logger类源码,属于log4j包下的类

在这里插入图片描述

重构后

  • 依赖:删除log4j依赖,添加slf4j提供的桥接组件和logback依赖
<!-- log4j相关的桥接器 -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>log4j-over-slf4j</artifactId>
    <version>1.7.25</version>
</dependency>
<!-- logback依赖 -->
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.3</version>
</dependency>
  • 导包:依然不变
import org.apache.log4j.Logger;
@Test
public void test08(){
    Logger logger = Logger.getLogger(Log4jTest.class);
    logger.info("info信息");
}
  • 查看Logger类源码,属于log4j-over-slf4j包下的类

在这里插入图片描述

  如此操作,看上去依赖还是org.apache.log4j.Logger,实际已不再是log4j包下的类,日志实现也变成了slf4j门面可以随意搭配的方式了。

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

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

相关文章

什么是原生IP?

代理IP的各个类型称呼有很多&#xff0c;且它们在网络使用和隐私保护方面扮演着不同的角色。今天将探讨什么是原生IP以及原生IP和住宅IP之间的区别&#xff0c;帮助大家更好地理解这两者的概念和实际应用&#xff0c;并选择适合自己的IP类型。 一、什么是原生IP&#xff1f; 原…

流程、程序和政策之间的差异

流程、程序和政策是公司遵循的指导方针&#xff0c;以确保公司以有效和安全的方式运营。 每个企业都需要它们&#xff0c;但有时可能会让人搞不清一个从哪里开始&#xff0c;另一个从哪里结束。 企业经常混淆它们的用法&#xff0c;或者在真正含义上指错一个。 从高层次来看…

“数字武当”项目荣获2024年“数据要素×”大赛湖北分赛文化旅游赛道一等奖

9月26日&#xff0c;由国家数据局、湖北省人民政府指导的首届湖北省数据要素创新大会暨2024年“数据要素”大赛湖北分赛颁奖仪式在湖北武汉举行。由大势智慧联合武当山文化旅游发展集团有限公司参报的武当山“数字武当”项目&#xff0c;荣获文化旅游赛道一等奖。 据悉&#x…

《Linux从小白到高手》理论篇(三):vi/vim编辑器和Linux文件处理“三剑客”(sed/grep/awk)

List item 本篇介绍vi/vim编辑器和Linux文件处理“三剑客”&#xff08;sed/grep/awk&#xff09;&#xff0c;这5个工具命令可能是Linux最最常用的&#xff0c;而且功能超级强大。 vi/vim vi和vim的基本介绍 所有的 Linux 系统都会内建 vi 文本编辑器。Vim 具有程序编辑的…

【frp】frp重启、frp启动、frp后台启动、frps dashboard等等

我写的关于frp配置的文章&#xff1a;frp配置 服务端frps 1. 创建服务文件 sudo nano /etc/systemd/system/frps.service2. 添加服务配置 在打开的文件中添加以下内容&#xff1a; [Unit] DescriptionFRPS Server Afternetwork.target[Service] Typesimple ExecStart/root…

LeetCode[简单] 136. 只出现一次的数字

给你一个 非空 整数数组 nums &#xff0c;除了某个元素只出现一次以外&#xff0c;其余每个元素均出现两次。找出那个只出现了一次的元素。 你必须设计并实现线性时间复杂度的算法来解决此问题&#xff0c;且该算法只使用常量额外空间。 思路 当 A 与 B 不同时&#xff0c;按…

代理模式简介:静态代理VS与动态代理

代理模式&#xff1a;静态代理VS动态代理 1、定义2、分类2.1 静态代理2.2 动态代理 3、使用场景4、总结 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 1、定义 代理模式是一种设计模式&#xff0c;通过代理对象控制对目标对象的访问。简而…

Stable Diffusion 使用详解(13)--- 3D纹理增强

目录 背景 Normal Map 描述 原理 使用心得 例子 描述 原图 参数设置 底模 ​编辑 正负相关性提示词 其他参数 controlnet 效果 还能做点啥 调整 效果 背景 实际上&#xff0c;在stable diffusion 中&#xff0c;你获取发现很多controlnet 其实功能有点类似&…

【优化】Vite手动分包

前言 每次打包完成后&#xff0c;默认情况下会将第三方库和自己的代码统统打包到一个JS文件中 打包后的 JS 对应一个指纹&#xff0c;当修改了自己的业务代码并重新打包后&#xff0c;还会将第三方库重新打包&#xff0c;继而生成一个新指纹&#xff0c;浏览器发现指纹变了&am…

从4道入门题目里面看Java和C的差别

目录 1.如何在IDEA进行循环输入 1.1题目概述 1.2循环输入 1.3println和print 2.如何调用数学函数 3.如何生成随机数字 4.字符串比较的方法 4.1错误案例分析 4.3正确比较方法 1.如何在IDEA进行循环输入 1.1题目概述 这个也是牛客上面的一个题目引发的思考&#xff1a;…

MySQl查询分析工具 EXPLAIN ANALYZE

文章目录 EXPLAIN ANALYZE是什么Iterator 输出内容解读EXPLAIN ANALYZE和EXPLAIN FORMATTREE的区别单个 Iterator 内容解读 案例分析案例1 文件排序案例2 简单的JOIN查询 参考资料&#xff1a;https://hackmysql.com/book-2/ EXPLAIN ANALYZE是什么 EXPLAIN ANALYZE是MySQL8.…

Linux进程的学习(持续更新)

冯诺依曼体系结构 概念&#xff1a; 冯・诺依曼体系结构是一种计算机体系结构&#xff0c;由美籍匈牙利科学家约翰・冯・诺依曼提出。它奠定了现代计算机的基本结构。 计算机分为以上五大部件组成&#xff1a; 输入设备&#xff1a;键盘&#xff0c;鼠标&#xff0c;网卡&…

【前端框架对比和选择】React 与 Vue 框架设计思路对比

框架总览 前端框架繁多&#xff0c;在学习的时候也会陷入困惑&#xff0c;我们应该抓住最主流的内容 Vue/React&#xff0c;深入底层&#xff0c;尝试揣摩框架作者的设计思路&#xff0c;开阔前端培训自己的视野&#xff0c;大家也不要把自己限制在框架之中&#xff0c;认为工…

Python | Leetcode Python题解之第442题数组中重复的数据

题目&#xff1a; 题解&#xff1a; class Solution:def findDuplicates(self, nums: List[int]) -> List[int]:ans []for x in nums:x abs(x)if nums[x - 1] > 0:nums[x - 1] -nums[x - 1]else:ans.append(x)return ans

气膜健身馆:提升运动体验与健康的理想选择—轻空间

近年来&#xff0c;气膜健身馆作为一种新兴的运动场所&#xff0c;正逐渐受到越来越多健身爱好者的青睐。这种独特的建筑形式不仅提供了良好的运动环境&#xff0c;更在健康和运动表现上展现出诸多优势。 优越的空气质量 气膜结构的核心技术通过内外气压差形成稳定的气膜&#…

Mysql调优之索引优化(四)

一、mysql索引结构B树原理 B树开始就是n树&#xff0c;不是二叉树 B树的非叶子结点存储了数据&#xff0c;导致层级会很深&#xff0c;每一层又有数据又有索引。 B树只有叶子结点存储数据&#xff0c;其余都是存储索引&#xff0c;增加了每层存取索引的数量&#xff08;3层结构…

Linux开源网络:高性能数据平面

数据平面的性能在很大程度上取决于网络 I/O 的性能&#xff0c;而网络数据包从网卡到用户空间的应用程序需要经历多个阶段&#xff0c;本文从数据平面基础到NFV&#xff0c;NFC基础设施再到OVS-DPDK VPP进行概论上的描述。 部分内容来源于《Linux开源网络全栈详解&#xff1a;从…

助力商用车远程检测维修,贝锐向日葵携手上海星融打造标杆案例

商用车是一个成熟且复杂的领域&#xff0c;伴随着数字化与信息化的不断发展&#xff0c;商用车领域的上下游厂商也正在积极的拥抱数字化&#xff0c;应用信息化工具所带来的“红利”&#xff0c;来提供更高质量的产品与服务&#xff0c;其中比较典型的代表&#xff0c;就是在商…

②EtherCAT转Modbus485RTU网关多路同步高速采集无需编程串口服务器

EtherCAT转Modbus485RTU网关多路同步高速采集无需编程串口服务器https://item.taobao.com/item.htm?ftt&id798036415719 EtherCAT 串口网关 EtherCAT 转 RS485 &#xff08;接上一章&#xff09; 自由协议通信步骤 &#xff08;以MS-A2-1041为例&#xff09; 接收与…

OSM转出shp的数据为啥有那么多空属性

当打开我们提供的OSM转出SHP格式的电力或其它类型数据可能会看到很多空属性&#xff0c;这个原因其实如果是写程序的人会很容易理解&#xff0c;但一般人可能理解起来比较困难。我试着解释一下&#xff0c;能理解就理解。不能理解您就记住这是格式转换产生的冗余数据就行&#…