本章源代码

https://github.com/gitaxin/JavaLog/blob/master/JCL/src/test/java/cn/giteasy/jcl/test/JCLTest.java

JCL简介

全称为Jakarta Commons Logging,是Apache提供的一个通用日志API。

用户可以自由选择第三方的日志组件作为具体实现,像 log4j,或者 jdk 自带的 jul,common-logging 会通过动态查找的机制,在程序运行时自动找出真正使用的日志库。

当然,common-logging 内部有一个Simple logger 的简单实现,但是功能很弱。所以使用common-logging,通常都是配合着log4j以及其他日志框架来使用。

使用它的好处就是,代码依赖是 common-logging而非log4j的API, 避免了和具体的日志API直接耦合,在有必要时,可以更改日志实现的第三方库。

JCL 有两个基本的抽象类:
Log:日志记录器。
LogFactory:日志工厂(负责创建 Log 实例)

image.png

案例

引入依赖

  1. <dependency>
  2. <groupId>junit</groupId>
  3. <artifactId>junit</artifactId>
  4. <version>4.13.2</version>
  5. <scope>test</scope>
  6. </dependency>
  7. <dependency>
  8. <groupId>commons-logging</groupId>
  9. <artifactId>commons-logging</artifactId>
  10. <version>1.2</version>
  11. </dependency>

入门案例

在没有导入第三方的日志框架情况下,例如log4j,会使用JUL日志框架做日志的记录操作

JCL使用的原则:
如果有log4j,优先使用log4j
如果项目中没有任何第三方日志框架,使用的就是JUL

  1. @Test
  2. public void testJCL(){
  3. Log log = LogFactory.getLog(JCLTest.class);
  4. log.fatal("========= FATAL信息 test jcl =========");
  5. log.error("========= ERROR信息 test jcl =========");
  6. log.warn("========= WARN信息 test jcl =========");
  7. log.info("========= INFO信息 test jcl =========");
  8. log.debug("========= DEBUG信息 test jcl =========");
  9. log.trace("========= TRACE信息 test jcl =========");
  10. /**
  11. * 输出结果:(JUL日志格式默认风格)
  12. * 一月 22, 2022 3:35:26 下午 cn.giteasy.jcl.test.JCLTest testJCL
  13. * 严重: ========= FATAL信息 test jcl =========
  14. * 一月 22, 2022 3:35:26 下午 cn.giteasy.jcl.test.JCLTest testJCL
  15. * 严重: ========= ERROR信息 test jcl =========
  16. * 一月 22, 2022 3:35:26 下午 cn.giteasy.jcl.test.JCLTest testJCL
  17. * 警告: ========= WARN信息 test jcl =========
  18. * 一月 22, 2022 3:35:26 下午 cn.giteasy.jcl.test.JCLTest testJCL
  19. * 信息: ========= INFO信息 test jcl =========
  20. */
  21. }

JCL引入log4j依赖

添加依赖

  1. <dependency>
  2. <groupId>log4j</groupId>
  3. <artifactId>log4j</artifactId>
  4. <version>1.2.17</version>
  5. </dependency>

添加配置文件log4j.properties

  1. log4j.rootLogger = trace,consoleAppender,fileAppender
  2. #*********************************************************************************************
  3. #配置appender输出方式为控制台输出
  4. log4j.appender.consoleAppender = org.apache.log4j.ConsoleAppender
  5. log4j.appender.consoleAppender.layout = org.apache.log4j.PatternLayout
  6. log4j.appender.consoleAppender.layout.conversionPattern = %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5p %l %m%n
  7. #*********************************************************************************************
  8. #配置appender输出到文件中
  9. log4j.appender.fileAppender = org.apache.log4j.FileAppender
  10. log4j.appender.fileAppender.layout = org.apache.log4j.PatternLayout
  11. log4j.appender.fileAppender.layout.conversionPattern = %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5p %l %m%n
  12. log4j.appender.fileAppender.file = D:\\log4j_test.log
  13. log4j.appender.fileAppender.encoding = UTF-8

测试类

  1. @Test
  2. public void testUCL_Log4j(){
  3. Log log = LogFactory.getLog(JCLTest.class);
  4. log.fatal("========= FATAL信息 test jcl =========");
  5. log.error("========= ERROR信息 test jcl =========");
  6. log.warn("========= WARN信息 test jcl =========");
  7. log.info("========= INFO信息 test jcl =========");
  8. log.debug("========= DEBUG信息 test jcl =========");
  9. log.trace("========= TRACE信息 test jcl =========");
  10. /**
  11. * 输出信息:
  12. *
  13. * log4j:WARN No appenders could be found for logger (cn.giteasy.jcl.test.JCLTest).
  14. * log4j:WARN Please initialize the log4j system properly.
  15. * log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
  16. *
  17. *
  18. * 我们观察控制台打印日志,可以发现已经使用了log4j作为日志输出,
  19. * 警告信息为没有发现Appender,是因为没有进行log4j的相关配置,需要log4j.properties配置文件进行配置,
  20. *
  21. * 进行配置后输出信息:
  22. * 2022-01-22 15:42:16.053 [main] FATAL cn.giteasy.jcl.test.JCLTest.testUCL_Log4j(JCLTest.java:54) ========= FATAL信息 test jcl =========
  23. * 2022-01-22 15:42:16.056 [main] ERROR cn.giteasy.jcl.test.JCLTest.testUCL_Log4j(JCLTest.java:55) ========= ERROR信息 test jcl =========
  24. * 2022-01-22 15:42:16.056 [main] WARN cn.giteasy.jcl.test.JCLTest.testUCL_Log4j(JCLTest.java:56) ========= WARN信息 test jcl =========
  25. * 2022-01-22 15:42:16.057 [main] INFO cn.giteasy.jcl.test.JCLTest.testUCL_Log4j(JCLTest.java:57) ========= INFO信息 test jcl =========
  26. * 2022-01-22 15:42:16.057 [main] DEBUG cn.giteasy.jcl.test.JCLTest.testUCL_Log4j(JCLTest.java:58) ========= DEBUG信息 test jcl =========
  27. * 2022-01-22 15:42:16.057 [main] TRACE cn.giteasy.jcl.test.JCLTest.testUCL_Log4j(JCLTest.java:59) ========= TRACE信息 test jcl =========
  28. *
  29. *
  30. *
  31. *
  32. */
  33. }

总结:

虽然日志框架进行了改变,但是代码完全没有改变。
日志门面的好处:
门面技术是面向接口的开发,不再依赖具体的实现类,减少代码的耦合性。
可以根据实际需求,灵活的切换日志框架。
统一的API,方便开发者学习和使用,统一的配置管理便于项目日志的维护

源码分析

Log接口的4个实现类

  • JDk13
  • JDK14 正常java.util.logging
  • Log4j 我们集成的log4j
  • Simple JCL自带实现类

image.png

(1)查看Jdk14Logger证明里面使用的是JUL日志框架

image.png
(2)查看Log4JLogger证明里面使用的是Log4j日志框架
image.png
(3)观察LogFactory,查看如何加载的Logger对象
image.pngimage.png
这是一个抽象类,无法实例化,需要观察其实现类LogFactoryImpl
(4)观察LogFactoryImpl,真正加载日志实现使用的就是这个实现类LogFactoryImpl
(5)进入getLog方法
进入getInstance方法
找到instance = this.newInstance(name);//继续进入
image.png
找到instance = this.discovertogImplementation(name);// 表示发现一个日志的实现
image.png

image.png
遍历我们拥有的日志实现框架,遍历的是一个数组,这个数组是按照 log4j、 jdk14、jdk13、SimpleLogger的顺序依次遍历
表示的是,第一个要遍历的就是log4j,如果有log4j则执行该日志框架,如果没有,则遍历出来第二个,使用jdk14的JUL日志框架,以此类推.

  1. result = this createLogFromclass(classesToDiscover[i], logCategory, true);
  1. <br /> 表示帮我们创建Logger对象在这个方法中,我们看到了
  1. c = class.forName(logAdapterclassName, true, currentCL);


是取得该类型的反射类型对象,使用反射的形式帮我们创建logger对象

  1. constructor = c.getconstructor(this.logConstructorsignature);