log4j,log4j2,slf4j,common-logging,jdk-logging集成以及切换

时间:2022-12-17 09:15:42

1.各种jar包解释

  • log4j:log4j1的全部内容

  • log4j2:

    • log4j-api :log4j2 定义下的api
    • log4j-core:log4j2 对定义的实现
  • logback:
    • logback-core:logback核心包
    • logback-classic:logback实现了slf4j的api
  • commons-logging:
    • commons-logging:原生的全部内容
    • log4j-jcl:commons-logging到log4j2的桥梁
    • jcl-over-log4j: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的桥梁

备注:链接:官方各个jar包说明http://logging.apache.org/log4j/2.x/runtime-dependencies.html
jcl全称:Jakarta Commons Logging
jul全称:Java Logging Framework

2.slf4j与其他日志框架的集成

  • slf4j与jdk-logging集成:
    • slf4j-api, slf4j-jdk14(集成包)
  • slf4j与log4j1集成:
    • slf4j-api,log4j, slf4j-jdk14(集成包)
  • slf4j与log4j2集成:
    • slf4j-api, log4j-api, log4j-core, log4j-slf4j-impl(集成包)
  • slf4j与logback集成:
    • slf4j-api,logback-core,logback-classic(集成包)
  • slf4j与commons-logging集成:
    • slf4j-api,commons-logging,slf4j-jcl(集成包)

3.日志系统之间的切换

log4j1切换到logback

示例:
import org.apache.log4j.Logger;
...
private static final Logger logger=Logger.getLogger(JulSlf4jLog4jTest.class.getName());

操作步骤:
- 去掉log4j包
- 添加log4j-over-slf4j(桥梁),slf4j-api,logback-core,logback-classic
- 添加logback的配置文件

实现原理:log4j-over-slf4j内部是简化版的log4j(定义与log4j中基本一致,可实现无缝迁移),真正实现的时候使用slf4j-api里面的来具体实现,slf4j会自动适配真正使用哪种来实现,举一反三,slf4j向哪种日志输出可以*搭配

jdk-logging到logback

示例:
import java.util.logging.Logger;
...
private static final Logger logger=Logger.getLogger(JulSlf4jLog4jTest.class.getName());

操作步骤:
- 添加jar包:jul-to-slf4j (实现jdk-logging切换到slf4j),slf4j-api,logback-core,logback-classic
- 添加logback配置文件
- 在代码中添加如下代码
static {
SLF4JBridgeHandler.install();
}

实现原理:在jul-to-slf4j里面只有1个类:SLF4JBridgeHandler,这个类集成了jdk-logging里面的java.util.logging.Handler,这个是jdk-logging处理日志中的一个处理器,在使用之前,必须提前注册这个处理器,具体没细研究过,初始化后这个类后slf4j的原生LoggerFactory会获取一个定义的logger来进行日志的输出

commons-logging切换到logback

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.jar(无所谓)
- 添加:jcl-over-slf4j(实现commons-logging切换到slf4j),slf4j-api,logback-core,logback-classic
- 添加logback配置文件

实现原理:commons-logging通过jcl-over-slf4j 来选择slf4j作为底层的日志输出对象,而slf4j又选择logback来作为底层的日志输出对象

混合日志切换1

项目中目前使用commons-logging,log4j1,jdk-logging
现在想统一将日志的输出交给logback
操作步骤:
- 将上述日志输出全部切换到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
- 添加logback配置文件

混合日志切换2

项目中目前使用commons-logging,log4j进行编程现在统一切换到jdk-logging
操作步骤:
- 切换到slf4j来输出
- - 去掉commons-logging(是否去掉无影响),使用jcl-over-slf4j将commons-logging底层实现切换到slf4j
- - 去掉log4j1(必须去),使用log4j-over-slf4j,将log4j1的实现切换到slf4j
- 选择真正的实现
- - 添加slf4j-api,slf4j-jdk14(集成包)

4.冲突说明

参考slf4j官网的冲突说明http://www.slf4j.org/legacy.html

jcl-over-log4j与slf4j-jcl冲突

  • jcl-over-log4j: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内部做了一个判断,可以防止造成内存溢出

jul-to-slf4j 与 slf4j-jdk14 冲突

  • jul-to-slf4j : jdk-logging切换到slf4j
  • slf4j-jdk14 : slf4j切换到jdk-logging
    冲突同样是内存溢出

总结区别比较

内部实现原理这里不再细说,可参考文章:http://blog.csdn.net/a283398689/article/details/52776145

commons-logging和slf4j

  • 都是日志接口,但common-logging通过动态查找的机制,在程序运行时自动找出真正使用的日志库。由于它使用了ClassLoader寻找和载入底层的日志库, 导致了象OSGI这样的框架无法正常工作,因为OSGI的不同的插件使用自己的ClassLoader。 OSGI的这种机制保证了插件互相独立,然而却使Apache Common-Logging无法工作。
  • slf4j在编译时静态绑定真正的Log库,因此可以再OSGI中使用。另外,SLF4J 支持参数化的log字符串,避免了之前为了减少字符串拼接的性能损耗而不得不写的if(logger.isDebugEnable()),现在你可以直接写:logger.debug(“current user is: {}”, user)。拼装消息被推迟到了它能够确定是不是要显示这条消息的时候,但是获取参数的代价并没有幸免。

性能比较

log4j2 >= slf4j >log4j1,具体性能如何,我没自己测试,有兴趣可以试试

实际操作

  • 新写的接口尽可能用slf4j的api编写,从上面可以看到slf4j基本接了所有的实现,以后切换也比较方便
  • 项目开发过程中,发现log4j1的依赖比较多,这个基本上去除不干净,稍微不注意就使用成了log4j1的api,导致日志打印不出来,这里再次区分
    • log4j1:import org.apache.log4j.Logger;
    • log4j2:import org.apache.logging.log4j.Logger;