java-web环境集成各种主流日志框架(jcl,jul,slf4j,log4j,logback)总结

  最近一段时间,学习了java日志管理方面的内容,用过很久,但是没有系统的学习相关。 趁这个机会,将各种日志相关的内容,以及框架,和集成使用方面的内容拉通学习一下。 

  我将从以下的方面进行笔记的整理:

1.log4j1与log4j2在web集成方面的区别;

2.日志框架标准与实现的区别,及举例;面向框架的spring-jcl以及slf4j标准,在web方面的使用记录;

3.slf4j与其它各种日志框架的集成使用;

4.slf4j日志框架自动发现的实现原理,及其源码解读;

5.各种日志框架(不包括log4j2)自动加载配置文件的原理;

6.如何进行日志框架的无缝迁移;

log4j1与log4j2在web集成方面的区别

   我们应当知道,javaweb项目与一般的java项目是有区别的。 这些区别可以体现在: 启动的逻辑不同;环境上下文不同;用户交互方式不同等等。 因为web项目很多很多的框架可以使用,针对不同的目标类型,不同的逻辑构成。有些是不同的逻辑组件,有些是相同的业务逻辑组件不同的实现。  所以可复用组件,或者说标准尤为重要。   日志组件就是这样的一种存在。

  web环境搭建就没有什么必要介绍了。 以前记录过一篇:地址在这里

  看一下log4j1的使用

    依赖:

     使用:

   配置:

  这样一个log4j的日志就可以使用了。 不需要其它任何额外的配置。

在看一看log4j2的使用(基于web项目):

    依赖:

   使用:

   配置:

注册web上下文:

     这样,log4j2就可以在项目中使用了。

    实际上,这里的log4j2采用的是spring-jcl标准。  于是这就引出第二个问题了。

2.日志框架标准与实现的区别,及举例;面向框架的spring-jcl以及slf4j标准,在web方面的使用记录:

   先说第一个问题:标准与实现

      至此,我们实际上已经接触了很多对这样的逻辑关系的组件:

          比如:

              jdbc标准,由j2ee标准提出,对应的实现有:mysql的实现,sql-server的实现,oracle的实现。  其中,mysql的实现: mysql-connector.jar,由mysql官方提供。

              servlet标准,由j2ee标准提出,对应的实现有: tomcat服务器容器,jetty容器,websocket,websphere等。 其中典型的tomcat由apache基金会提供并开源。

              orm标准,由j2ee标准提出,对应的是是现有: mybatis,hibernate。 

              datasource标准,由jdk定义,对应的实现有: dbcp2实现,spring的相关实现,druid的实现。 其中druid由阿里巴巴提供。 

           类似于这样的标准与实现还有很多很多。。 

           要说它们的本质,那就是一堆接口,与一堆实现了这些接口的类组成。 当然在这个途中进行了大量的设计模式的使用,性能的考量,相应的扩展等。

           日志框架的标准与实现也是这样。

第二个问题:spring-jcl:

     spring-jcl的源码较少,因此我们不妨将它的源码类结构贴上:

    可见,它实际上整合的是apache的commons-loging。 只不过,进行了一些适配。  其中的关键是:LogFactory抽象类,以及LOG接口标准。 它的核心源码片段:

  

      

  以上给是该类初始化的静态块方法,它会根据上下文就进行适配,需注意它们实际上是有优先级关系的。   原理很简单,就是用类加载器去加载相应日志框架的spi,若找到,则判定为当前系统采用该日志框架。 否则依次进行迭代,直至采用jul。 因为它位于jdk中,无论如何都会有该日志框架的。 

   通过源码可以看到,它提供了三个大类型的日志框架实现,分别是: log4j2日志框架实现(因为该spi类只存在Log4j2中);slf4j日志框架标准;jul(java util logging)日志实现。  它们具有一定的优先级关系。 

    至于为什么是Log4j2,可以给出源码验证:

    ok,这是spring-jcl作为日志标准为基础的逻辑思路。   我上文使用的slf4j并没有说它是日志实现,而是标准。 为什么这么说呢? 这就是接下来的一个问题:

slf4j标准

  先看一看slf4j的项目结构:

 

    这也很好的印证了spring-jcl的动态发现,选择日志实现框架。 

     slf4j的标准是由这里的Logger接口提供的。 至于具体的实现,将在后面的问题中进行讨论。

最后,来看一看在web项目的中的使用:

    涉及到使用,那么必须的是标准的实现。  由于spring-jcl默认优先级的关系,log4j2我们必须先注释掉,然后才能使用slf4j。 但是我们知道,这是一个标准,如何使用呢?

    通过搜集相关的资料,我们了解到,slf4j对于主流的日志框架实现都提供了适配,或者基于该标准进行开发。

    如:slf4j-simple,它的依赖

 

     它是一个实现类,实现了slf4j的logger接口。

   slf4j-jdk4,它的依赖是

    他提供的是基于slf4j的logger接口,针对java.util,logging实现的适配类。 相应的实现由Jdk提供实现,无需依赖其它的第三方。

slf4j-jcl,它的依赖

   它提供的是基于slf4j的logger接口的,针对commons-logging实现的适配类,它需要依赖jcl的实现:

slf4j-log4j12,它的依赖

  它提供的是基于slf4j的logger接口的,针对log4j实现的适配类,它需要依赖log4j的实现,这里使用的是log4j1:

  logbak,它的依赖

  它提供的是基于slf4j的Logger接口的实现类。

以上所说的适配类,以及实现类可以通过查看继承关系得到验证

   注意,这里有一个有趣的现象,spring-jcl 作为日志框架为其它日志实现提供支持,包括对slf4j提供支持。  spring-jcl是基于commons-logging实现的;  而作为slf4j日志标准来说,它同时也提供了对jcl的集成支持。  这就感觉有那么一丝丝怪异,形成了一个类似环形的结构。。   不过,这只是感觉上来说,spring-jcl中的那个日志工厂与commons-logging的日志工厂应该并不相同,只不过他们提供了相同的包结构。如果是这样的话, 在一定程度上可能会存在潜在的bug。 具体怎样等以后有兴趣了在看看看吧。

   如何使用

      针对这些slf4j标准的实现,它的原理与spring-jcl优点相似,就是在运行时动态的寻找实现。 因此,这里举一个例子,其它的类推即可,至于适配的那种,则需要根据本身框架实现来进行配置使用即可。 比如,就拿Simple来说:

   第一步,当然是引入相关依赖;

   第二步,建立配置文件:

      如:

   第三步,在相应的位置使用日志框架,它会自动扫描,动态寻找依赖,如spring-jcl:

   运行,即可。  其它的日志使用类似!!   包括logback。    这样的话,简单的问题背后难免引起我们的思考。  因此这就是接下来思考的问题了。

slf4j日志框架自动发现的实现原理,及其源码解读:

   这个问题实际上可以与spring-jcl类比着看。  先回顾一下spring-jcl是如何实现自动选择实现的:  它实际上是通过类加载器去加载特定的类,如果加载成功,则认为使用该框架作为实现,否则就一路迭代,选择其它的实现。  直到最终会确定一个一个实现类。   这个思路有点类似于责任链传递模式。

    那么slf4j是如何实现的呢? 

    我主要从两个方面入手分析了这个问题: 第一,如何确定有哪些实现模块在当前项目中(如何发现);  第二,如何加载实现项目的启动类(如何实现)

    第一个问题:  我是通过查看资料,与查看源代码进行分析的,过程如下:

  这是位于slf4j的日志工厂的其中一个静态方法,通过源码逻辑,可以分析出,它实际上是完成了获得实现模块的功能。 这个操作通过类加载器实现:

  

  该类加载器是继承于URLClassLoader。  它的名称为: 平行类加载器。    当然前面有一些关于类加载机制的双亲委派机制。  这是jvm部分的基础知识,也是java的核心知识。 很早以前看过了。(大概昨年春节的时候看过源码,也从此开启奋斗之旅。)。 需要注意,在web容器中的类加载器机制中,并没有继续采用双亲委派机制了。  通过运行时调试,我们可以分析出,获得资源的这个实际实现是由tomcat容器的一个类加载器实现的:

  

(为了实验的目的,我特意放了两个依赖进来。)

  由于在idea中我并没有采用内置容器,所以具体的实现细节源码没有查看了。   至此,第一个问题就解决了。 

第二个问题

     记得我们之前看过基于Slf4j的实现类,其中除了一些适配类,实现类外,有一个类没有去描述过它,因为它既不是抽象类,也不是其他类的适配器类。 并且这个类位于slf4j-api的项目中。  

     看一看它的源码片段:

  在slf4j的抽象工厂中有如下的一些关于它的操作:

   

 

 

它的核心方法代码片段

自此,第二个问题也解决的差不多了。   它的实现方法是: 抽象+ 代理

  各种日志框架(不包括log4j2)自动加载配置文件的原理:

     这个问题,各个日志框架实现类都有各自的实现。  仍然是采用类比的思想,挑一个简单的来看看,复杂的就不看了。   要说简单,那自然就是slf4j-simple了。 

    它的核心业务类: SimpleLogger。 

    具有如下的一些代码片段:

   其它自动加载配置文件的原理估计也差不多。  就不看了。 

那么就剩最后一个问题了:

如何进行日志框架的无缝迁移:

     实际上,这个问题在上面的分析过程中已经解决了。  只要是基于标准实现的,要替换实现,我们要做的仅仅是替换相应的依赖即可,并且这个过程用户不需要其它额外的配置(当然,对于不同实现之间的必要的适配还是需要做的)。   关于日志使用的部分,就告一段落。

猜你喜欢

转载自blog.csdn.net/qq_36285943/article/details/84321918