java日志门面之JCL和SLF4J
文章目录
- 前言
- 一、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
- 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 {@Testpublic 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,consolelog4j.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 {@Testpublic 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

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,consolelog4j.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门面可以随意搭配的方式了。
相关文章:
java日志门面之JCL和SLF4J
文章目录 前言一、JCL1、JCL简介2、快速入门3、 JCL原理 二、SLF4J1、SLF4J简介2、快速入门2.1、输出动态信息2.2、异常信息的处理 3、绑定日志的实现3.1、slf4j实现slf4j-simple和logback3.2、slf4j绑定适配器实现log4j 4、桥接旧的日志框架4.1、log4j日志重构为slf4jlogback的…...
Oracle DB运维常用的视图及数据字典
List item 本文介绍一些Oracle DB日常运维最常用到(使用频率很高)的视图及数据字典 用户有关的常用视图: 1、 查看当前用户的缺省表空间* SQL>select username,default_tablespace from user_users; 2、 查看当前用户的角色 SQL>sele…...
vue.config.js devServer中changeOrigin的作用
问题 vue开发时,为了解决前端跨域问题,通常在vue.config.js配置 devServer proxy devServer: {proxy:{/api: {target: http://b.com,changeOrigin: false},}, }官方文档http-proxy options对changeOrigin的解释 option.changeOrigin: true/false, Defa…...
基于Ubuntu 20.04 LTS上部署MicroK8s(最小生产的 Kubernetes)
目录 文章目录 目录简介Kubernetes简介MicroK8s简介Ubuntu系统MicroK8s的优势安装环境基本要求执行安装命令加入群组(使用非 root 用户访问)开启 dashboard 仪表盘查看服务名称查看仪表盘开放的端口打开浏览器检查状态打开你想要的服务(使用附加组件)开始使用 microk8s访问 Kub…...
Spring:项目中的统一异常处理和自定义异常
介绍异常的处理方式。在项目中,都会进行自定义异常,并且都是需要配合统一结果返回进行使用。 1.背景引入 (1)背景介绍 为什么要处理异常?如果不处理项目中的异常信息,前端访问我们后端就是显示访问失败的…...
有点快要跟不上时代的感觉
团队的群里面有一个同事突然问了下,下面的这个 JavaScript 如何进行优化 var startIndex (start undefined || start null) ? null : start[0].Value;看上面的代码就是典型的判断和返回的问题。 如果是要调试的话也不是做不出来,但可能要花点时间&a…...
【pytorch】pytorch入门4:神经网络的卷积层
文章目录 前言一、定义概念 缩写二、性质三、代码总结参考文献 前言 使用 B站小土堆课程的笔记 一、定义概念 缩写 卷积层是神经网络中用于突出特征来进行分类任务的层。 二、性质 卷积核例子:vgg16 model 三、代码 添加库 python代码块import os import …...
【机器学习】探索LSTM:深度学习领域的强大时间序列处理能力
目录 🍔 LSTM介绍 🍔 LSTM的内部结构图 2.1 LSTM结构分析 2.2 Bi-LSTM介绍 2.3 使用Pytorch构建LSTM模型 2.4 LSTM优缺点 🍔 小结 学习目标 🍀 了解LSTM内部结构及计算公式. 🍀 掌握Pytorch中LSTM工具的使用. &…...
QT学习笔记之文件操作
你千万不要跟任何人谈起任何事。你只要一谈起,就会想念起每一个人来。 在ui界面添加一个LineEdit(lEt)、QPushButton(btn)、QWidget widget.cpp #include "widget.h" #include "ui_widget.h" #include <QFile> #include <QFileDialo…...
Mybatis XML配置文件操作数据库
Mybaits在操作数据库时,可以有两种方式;第一种是使用注解的方式操作,另一种是使用XML配置文件的方式:一般而言,若没有特别的要求,则编写一些简单的SQL语句,可以直接使用注解的方式;编…...
Ansible-template模块动态生成特定文件
文章目录 一、Jinja2介绍什么是主要特性安装基本用法进阶特性总结 Jinja2与Ansible关系1. 模板引擎2. Ansible 的依赖3. 变量和模板4. 动态生成配置5. 社区和生态系统总结 二、Ansible如何使用Jinja2使用template模块Jinja2文件中使用判断和循环Jinja2文件中使用判断语法 Jinja…...
【Hadoop】【vim编辑器】【~/.bashrc 文件】如何编辑
1. 进入 vim 编辑器 在终端中输入以下命令: vim ~/.bashrc 2. 进入插入模式 打开文件后,你将处于普通模式。在普通模式下,你不能直接编辑文本。 要进入插入模式,请按下 i 键。这时,你应该会看到屏幕底部出现 -- 插…...
vs code自动报错
让vs code自动报错, 点击插件 → 搜索error lens → 点击install, 下载完后,编写的代码有问题就会自动报错了。 5、修改默认缩进字符 点击设置(settings) → 点击常用设置 → 修改字符缩进。...
详细分析Nginx中的proxy_pass 末尾斜杠
目录 前言1. 基本知识2. Demo 前言 对于Nginx的讲解,更多推荐阅读: Nginx配置静态网页访问(图文界面)Nginx将https重定向为http进行访问的配置(附Demo)Nginx从入门到精通(全)详细分…...
数据结构:双指针—移动0(OJ283)
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。 请注意 ,必须在不复制数组的情况下原地对数组进行操作。 示例 1: 输入: nums [0,1,0,3,12] 输出: [1,3,12,0,0]示例 2: 输入: nums [0] 输出: […...
LeetCode - 850 矩形面积 II
题目来源 850. 矩形面积 II - 力扣(LeetCode) 题目描述 给你一个轴对齐的二维数组 rectangles 。 对于 rectangle[i] [x1, y1, x2, y2],其中(x1,y1)是矩形 i 左下角的坐标, (xi1, yi1) 是该…...
Jenkins Pipeline 中通过勾选参数来控制是否构建 Docker 镜像
1.定义参数: 使用 booleanParam 定义一个布尔参数,示例如下 booleanParam(name: BUILD_DOCKER, description: 是否构建Docker镜像, defaultValue: false)2.使用参数: 在 stage 中,根据参数的值决定构建方式: stage(编…...
C++入门基础知识86(实例)——实例11【计算自然数之和】
成长路上不孤单😊😊😊😊😊😊 【14后😊///C爱好者😊///持续分享所学😊///如有需要欢迎收藏转发///😊】 今日分享关于计算自然数之和相关内容! 关…...
ChatGPT与R语言融合技术在生态环境数据统计分析、绘图、模型中的实践与进阶应用
自2022年GPT(Generative Pre-trained Transformer)大语言模型的发布以来,它以其卓越的自然语言处理能力和广泛的应用潜力,在学术界和工业界掀起了一场革命。在短短一年多的时间里,GPT已经在多个领域展现出其独特的价值…...
OpenAi以及Dify结合生成Ai模型
文章目录 1、Dify介绍2、使用 Dify3、部署Docker1.系统要求2.系统虚拟化3.下载docker 4、安装WSL1.检查是否已经安装 五、访问系统六、添加模型 1、Dify介绍 Dify官方地址。 Dify 是一个开源的 LLM 应用开发平台。其直观的界面结合了 AI 工作流、RAG 管道、Agent、模型管理、…...
eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)
说明: 想象一下,你正在用eNSP搭建一个虚拟的网络世界,里面有虚拟的路由器、交换机、电脑(PC)等等。这些设备都在你的电脑里面“运行”,它们之间可以互相通信,就像一个封闭的小王国。 但是&#…...
设计模式和设计原则回顾
设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...
stm32G473的flash模式是单bank还是双bank?
今天突然有人stm32G473的flash模式是单bank还是双bank?由于时间太久,我真忘记了。搜搜发现,还真有人和我一样。见下面的链接:https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...
大话软工笔记—需求分析概述
需求分析,就是要对需求调研收集到的资料信息逐个地进行拆分、研究,从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要,后续设计的依据主要来自于需求分析的成果,包括: 项目的目的…...
安宝特方案丨XRSOP人员作业标准化管理平台:AR智慧点检验收套件
在选煤厂、化工厂、钢铁厂等过程生产型企业,其生产设备的运行效率和非计划停机对工业制造效益有较大影响。 随着企业自动化和智能化建设的推进,需提前预防假检、错检、漏检,推动智慧生产运维系统数据的流动和现场赋能应用。同时,…...
抖音增长新引擎:品融电商,一站式全案代运营领跑者
抖音增长新引擎:品融电商,一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中,品牌如何破浪前行?自建团队成本高、效果难控;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...
测试markdown--肇兴
day1: 1、去程:7:04 --11:32高铁 高铁右转上售票大厅2楼,穿过候车厅下一楼,上大巴车 ¥10/人 **2、到达:**12点多到达寨子,买门票,美团/抖音:¥78人 3、中饭&a…...
HBuilderX安装(uni-app和小程序开发)
下载HBuilderX 访问官方网站:https://www.dcloud.io/hbuilderx.html 根据您的操作系统选择合适版本: Windows版(推荐下载标准版) Windows系统安装步骤 运行安装程序: 双击下载的.exe安装文件 如果出现安全提示&…...
ardupilot 开发环境eclipse 中import 缺少C++
目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...
微信小程序云开发平台MySQL的连接方式
注:微信小程序云开发平台指的是腾讯云开发 先给结论:微信小程序云开发平台的MySQL,无法通过获取数据库连接信息的方式进行连接,连接只能通过云开发的SDK连接,具体要参考官方文档: 为什么? 因为…...
