slf4j jcl jul log4j1 log4j2 logback各组件系统日志切换
作者:乒乓狂魔 发布时间:2023-08-08 13:00:41
系列文章已完成,目录如下:
jdk-logging log4j logback日志系统实现机制原理介绍
commons-logging与jdk-logging、log4j1、log4j2、logback的集成原理
slf4j与jul、log4j1、log4j2、logback的集成原理
各种jar包总结
log4j1
:
log4j:log4j1的全部内容
log4j2
:
log4j-api:log4j2定义的API
log4j-core:log4j2上述API的实现
logback
:
logback-core:logback的核心包
logback-classic:logback实现了slf4j的API
commons-logging
:
commons-logging:commons-logging的原生全部内容
log4j-jcl:commons-logging到log4j2的桥梁
jcl-over-slf4j:commons-logging到slf4j的桥梁
slf4j转向某个实际的日志框架:
场景介绍:如 使用slf4j的API进行编程,底层想使用log4j1来进行实际的日志输出,这就是slf4j-log4j12干的事。
slf4j-jdk14:slf4j到jdk-logging的桥梁
slf4j-log4j12:slf4j到log4j1的桥梁
log4j-slf4j-impl:slf4j到log4j2的桥梁
logback-classic:slf4j到logback的桥梁
slf4j-jcl:slf4j到commons-logging的桥梁
某个实际的日志框架转向slf4j:
场景介绍:如 使用log4j1的API进行编程,但是想最终通过logback来进行输出,所以就需要先将log4j1的日志输出转交给slf4j来输出,slf4j再交给logback来输出。将log4j1的输出转给slf4j,这就是log4j-over-slf4j做的事
这一部分主要用来进行实际的日志框架之间的切换(下文会详细讲解)
jul-to-slf4j:jdk-logging到slf4j的桥梁
log4j-over-slf4j:log4j1到slf4j的桥梁
jcl-over-slf4j:commons-logging到slf4j的桥梁
集成总结
commons-logging与其他日志框架集成
1 commons-logging与jdk-logging集成:
需要的jar包:
commons-logging
2 commons-logging与log4j1集成:
需要的jar包:
commons-logging
log4j
3 commons-logging与log4j2集成:
需要的jar包:
commons-logging
log4j-api
log4j-core
log4j-jcl(集成包)
4 commons-logging与logback集成:
需要的jar包:
logback-core
logback-classic
slf4j-api、jcl-over-slf4j(2个集成包,可以不再需要commons-logging)
5 commons-logging与slf4j集成:
需要的jar包:
jcl-over-slf4j(集成包,不再需要commons-logging)
slf4j-api
slf4j与其他日志框架集成
slf4j与jdk-logging集成:
需要的jar包:
slf4j-api
slf4j-jdk14(集成包)
slf4j与log4j1集成:
需要的jar包:
slf4j-api
log4j
slf4j-log4j12(集成包)
slf4j与log4j2集成:
需要的jar包:
slf4j-api
log4j-api
log4j-core
log4j-slf4j-impl(集成包)
slf4j与logback集成:
需要的jar包:
slf4j-api
logback-core
logback-classic(集成包)
slf4j与commons-logging集成:
需要的jar包:
slf4j-api
commons-logging
slf4j-jcl(集成包)
日志系统之间的切换
log4j无缝切换到logback
1 案例
我们已经在代码中使用了log4j1的API来进行日志的输出,现在想不更改已有代码的前提下,使之通过logback来进行实际的日志输出。
已使用的jar包:
log4j
使用案例:
private static final Logger logger=Logger.getLogger(Log4jTest.class);
public static void main(String[] args){
if(logger.isInfoEnabled()){
logger.info("log4j info message");
}
}
上述的Logger是log4j1自己的org.apache.log4j.Logger,在上述代码中,我们在使用log4j1的API进行编程
现在如何能让上述的日志输出通过logback来进行输出呢?
只需要更换一下jar包就可以:
第一步:去掉log4j jar包
第二步:加入以下jar包
log4j-over-slf4j(实现log4j1切换到slf4j)
slf4j-api
logback-core
logback-classic
第三步:在类路径下加入logback的配置文件
原理是什么呢?
2 切换原理
看下log4j-over-slf4j就一目了然了:
我们可以看到,这里面其实是简化更改版的log4j。去掉log4j1的原生jar包,换成该简化更改版的jar包(可以实现无缝迁移)。
但是简化更改版中的Logger和原生版中的实现就不同了,简化版中的Logger实现如下(继承了Category):
public class Category {
private String name;
protected org.slf4j.Logger slf4jLogger;
private org.slf4j.spi.LocationAwareLogger locationAwareLogger;
Category(String name) {
this.name = name;
slf4jLogger = LoggerFactory.getLogger(name);
if (slf4jLogger instanceof LocationAwareLogger) {
locationAwareLogger = (LocationAwareLogger) slf4jLogger;
}
}
}
从上面可以看到简化版中的Logger内部是使用slf4j的API来生成的,所以我们使用的简化版的Logger会委托给slf4j来进行输出,由于当前类路径下有logback-classic,所以slf4j会选择logback进行输出。从而实现了log4j到logback的日志切换。
下面的内容就只讲解日志系统到slf4j的切换,不再讲解slf4j选择何种日志来输出
jdk-logging无缝切换到logback
1 案例
private static final Logger logger=Logger.getLogger(JulSlf4jLog4jTest.class.getName());
public static void main(String[] args){
logger.log(Level.INFO,"jul info a msg");
logger.log(Level.WARNING,"jul waring a msg");
}
可以看到上述是使用jdk-logging自带的API来进行编程的,现在我们想这些日志交给logback来输出
解决办法如下:
第一步:加入以下jar包:
jul-to-slf4j (实现jdk-logging切换到slf4j)
slf4j-api
logback-core
logback-classic
第二步:在类路径下加入logback的配置文件
第三步:在代码中加入如下代码:
static{
SLF4JBridgeHandler.install();
}
切换原理
先来看下jul-to-slf4j jar包中的内容:
我们看到只有一个类:SLF4JBridgeHandler
它继承了jdk-logging中定义的java.util.logging.Handler,Handler是jdk-logging处理日志过程中的一个处理器(具体我也没仔细研究过),在使用之前,必须要提前注册这个处理器,即上述的SLF4JBridgeHandler.install()操作,install后我们就可以通过这个handler实现日志的切换工作,如下:
protected Logger getSLF4JLogger(LogRecord record) {
String name = record.getLoggerName();
if (name == null) {
name = UNKNOWN_LOGGER_NAME;
}
return LoggerFactory.getLogger(name);
}
在处理日志的过程中,使用了slf4j的原生方式LoggerFactory来获取一个slf4j定义的Logger来进行日志的输出
而slf4j则又会选择logback来进行实际的日志输出
commons-logging切换到logback
使用案例
使用的jar包
commons-logging
案例如下:
private static Log logger=LogFactory.getLog(JulJclTest.class);
public static void main(String[] args){
if(logger.isTraceEnabled()){
logger.trace("commons-logging-jcl trace message");
}
}
可以看到我们使用commons-logging的API来进行日志的编程操作,现在想切换成logback来进行日志的输出(这其实就是commons-logging与logback的集成)
解决办法如下:
第一步:去掉commons-logging jar包(其实去不去都无所谓)
第二步:加入以下jar包:
jcl-over-slf4j(实现commons-logging切换到slf4j)
slf4j-api
logback-core
logback-classic
第三步:在类路径下加入logback的配置文件
切换原理
这个原理之前都已经说过了,可以看下commons-logging与logback的集成
就是commons-logging通过jcl-over-slf4j 来选择slf4j作为底层的日志输出对象,而slf4j又选择logback来作为底层的日志输出对象。
常用的日志场景切换解释
上面把日志的切换原理说清楚了,下面就针对具体的例子来进行应用
先来看下slf4j官方的一张图:
下面分别详细说明这三个案例
1 左上图
现状:
目前的应用程序中已经使用了如下混杂方式的API来进行日志的编程:
现在想统一将日志的输出交给logback
commons-logging
log4j1
jdk-logging
解决办法:
加入以下jar包:
第一步:将上述日志系统全部无缝先切换到slf4j
去掉commons-logging(其实去不去都可以),使用jcl-over-slf4j将commons-logging的底层日志输出切换到slf4j
去掉log4j1(必须去掉),使用log4j-over-slf4j,将log4j1的日志输出切换到slf4j
使用jul-to-slf4j,将jul的日志输出切换到slf4j
第二步:使slf4j选择logback来作为底层日志输出
slf4j-api
logback-core
logback-classic
下面的2张图和上面就很类似
2 右上图
现状:
目前的应用程序中已经使用了如下混杂方式的API来进行日志的编程:
现在想统一将日志的输出交给log4j1
commons-logging
jdk-logging
解决办法:
加入以下jar包:
第一步:将上述日志系统全部无缝先切换到slf4j
去掉commons-logging(其实去不去都可以),使用jcl-over-slf4j将commons-logging的底层日志输出切换到slf4j
使用jul-to-slf4j,将jul的日志输出切换到slf4j
第二步:使slf4j选择log4j1来作为底层日志输出
加入以下jar包:
slf4j-api
log4j
slf4j-log4j12(集成包)
3 左下图
现状:
目前的应用程序中已经使用了如下混杂方式的API来进行日志的编程:
现在想统一将日志的输出交给jdk-logging
commons-logging
log4j
解决办法:
第一步:将上述日志系统全部无缝先切换到slf4j
去掉commons-logging(其实去不去都可以),使用jcl-over-slf4j将commons-logging的底层日志输出切换到slf4j
去掉log4j1(必须去掉),使用log4j-over-slf4j,将log4j1的日志输出切换到slf4j
第二步:使slf4j选择jdk-logging来作为底层日志输出
加入以下jar包:
slf4j-api
slf4j-jdk14(集成包)
冲突说明
仍然是这里的内容slf4j官网的冲突说明
其实明白上面介绍的各jar包的作用,就很容易理解
jcl-over-slf4j 与 slf4j-jcl 冲突
jcl-over-slf4j: commons-logging切换到slf4j
slf4j-jcl : slf4j切换到commons-logging
如果这两者共存的话,必然造成相互委托,造成内存溢出
log4j-over-slf4j 与 slf4j-log4j12 冲突
log4j-over-slf4j : log4j1切换到slf4j
slf4j-log4j12 : slf4j切换到log4j1
如果这两者共存的话,必然造成相互委托,造成内存溢出。但是log4j-over-slf4内部做了一个判断,可以防止造成内存溢出:
即判断slf4j-log4j12 jar包中的org.slf4j.impl.Log4jLoggerFactory是否存在,如果存在则表示冲突了,抛出异常提示用户要去掉对应的jar包,代码如下,在slf4j-log4j12 jar包的org.apache.log4j.Log4jLoggerFactory中:
jul-to-slf4j 与 slf4j-jdk14 冲突
jul-to-slf4j : jdk-logging切换到slf4j
slf4j-jdk14 : slf4j切换到jdk-logging
如果这两者共存的话,必然造成相互委托,造成内存溢出
结束语
至此,这个日志系列就算终于完成了。它注重于日志系统之间的交互与集成,所以想深入研究单个日志系统的架构的话,就需要各位自行去深入研究了!
来源:https://my.oschina.net/pingpangkuangmo/blog/410224
猜你喜欢
- 一、概念 1. 为了能让程序操作数据库,对数据库中的表进行操作,每一种数据库都会提供一套连接和操作该数据库的驱动,而且每种数据库
- SpringBoot集成Mybatis时mybatis.mapper-locations和@MapperScan的作用1、mybatis.m
- 1. 前言现在很多应用都有小悬浮窗的功能,比如看直播的时候,通过Home键返回桌面,直播的小窗口仍可以在屏幕上显示。下面将介绍下悬浮窗的的一
- Android Studio 打包 jar 及 aar 包创建工程 New -> Module -> Library在gradl
- 在Android中,线程内部或者线程之间进行信息交互时经常会使用消息,这些基础的东西如果我们熟悉其内部的原理,将会使我们容易、更好地架构系统
- 网络中数据传输经常是xml或者json,现在做的一个项目之前调其他系统接口都是返回的xml格式,刚刚遇到一个返回json格式数据的接口,通过
- 实践过程效果代码public partial class Form1 : Form{ public Form1()
- 本文实例为大家分享了Java执行SQL脚本文件到数据库的具体方式,供大家参考,具体内容如下方式一:直接读取SQL脚本文件的内容,然后传递到S
- 本文实例为大家分享了android TextView跑马灯效果的具体代码,供大家参考,具体内容如下一、要点设置四个属性android:sin
- 一、介绍在实际的软件项目开发过程中,我可以很负责任的跟大家说,如果你真的实际写代码的时间超过5年,你对增删改查这类简单的功能需求开发,可以说
- 多选和单选的不同之处单选的时候,选中一个就可以直接把结果返回,因此本身底部弹窗无需状态管理。但到多选的时候,需要知道当前选中的选项,有选项被
- 本文实例讲述了Android编程之消息机制。分享给大家供大家参考,具体如下:一、角色描述1.Looper: 一个线程可以产生一个Looper
- 1、右值1.1 简介首先区分一下左右值:左值是指存储在内存中、有明确存储地址(可取地址)的数据;右值是指可以提供数据值的数据(不可取地址)如
- Java HashSetHashSet 基于 HashMap 来实现的,是一个不允许有重复元素的集合。HashSet 允许有 null 值。
- 错误处理到目前为止,我们都没怎么介绍onComplete()和onError()函数。这两个函数用来通知订阅者,被观察的对象将停止发送数据以
- Android 消息机制1.概述Android应用启动时,会默认有一个主线程(UI线程),在这个线程中会关联一个消息队列(MessageQu
- 前言今天的文章从下面这张图片开始,这张图片Java开发们应该很熟悉了我们都知道无锁状态是对象头是有位置存储hashcode的,而变为偏向锁状
- 1、取得控制台应用程序的根目录方法方法1、Environment.CurrentDirectory 取得或设置当前工作目录的完整限定路径方法
- 右击有main方法的类===> Run as===> Run Configurations ===>双击java
- 问题:在用Java程序进行读写含中文的txt文件时,经常会出现读出或写入的内容会出现乱码。原因其实很简单,就是系统的编码和程序的编码采用了不