文章目录

  • 前言
  • 一、认识JCL
    • 1.1、JCL概述
    • 1.2、第三方jar包
    • 1.3、认识Log接口与LogFactory抽象类
  • 二、JCL实际使用
    • 2.1、应用JUL(jdk自带日志)
    • 2.2、应用Log4j(第三方)
  • 三、源码分析
    • 3.1、Log接口实现类分析
    • 3.2、JCL原理分析(动态加载Log实现类)
  • 总结
  • 参考资料

前言

本篇博客主要介绍的是日志门面技术JCL,现在大多使用slf4j来作为日志门面了,不过还是需要学习了解一下。其他日志框架内容可见日志专栏。

所有博客文件目录索引(包含日志框架系列学习):博客目录索引(持续更新)


一、认识JCL

1.1、JCL概述

JCL(全称为Jakarta Commons Logging):是Apache提供的一个通用日志API,它为多个Java日志框架实现(如JULLog4j等)提供了一个统一的接口Log,其自身也提供一个日志实现SimpleLog(一般不使用它)。

  • 2014年的时候被Apache淘汰了(具有缺陷,当时只考虑了主流的应用框架,对于未来新出现框架不提供相应的实现类实现,除非自己修改源码进行完善),最新的版本为1.2(即最终版本,停止维护),后期又推出了更加优秀的日志门面框架。

JCL包含两个基本抽象类:Log类(通用接口)、LogFactory(负责创建Log实例)。

首先看下面这个图

这个图只是个抽象的举例,在JCL中提供了一个通用接口Log(包含多种日志级别方法),该Log接口有多个实现类其中的实现方法分别使用了不同日志框架的日志方法,这种方式解决了什么问题呢?

  • 能够让我们在系统进行升级时更好的切换使用日志框架,例如通过JCL日志门面我们默认使用的是JUL,若是想要替换使用Log4j,则只需要导入Log4jjar包并提供配置文件,不用修改方法即可使用。

1.2、第三方jar包

想要使用JCL,我们需要引入第三方jar包,下面给出pom.xml的坐标:

<dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.2</version>
</dependency>

1.3、认识Log接口与LogFactory抽象类

Log接口

  • 可以看到Log接口中都是对应的日志等级方法。
  • 右边则是Log接口的继承图,红框中的都是它的实现类,其中Jdk14Logger(其中Logger实例使用的JUL)、Log4JLogger(使用的是Log4j)

LogFactory抽象类

  • 我们需要关注其getLog()方法,获取Log实例是通过该工厂类来获取的。

二、JCL实际使用

2.1、应用JUL(jdk自带日志)

首先引入jar包,接着通过使用LogFactory获取到Log实例,调用日志方法即可:

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;public class LogTest {public static void main(String[] args) {//获取Log实例Log log = LogFactory.getLog(LogTest.class);log.info("info");}
}

  • 通过输出的内容我们可以判定是JUL默认输出的格式内容。

说明:在不导入其他指定日志框架时(如Log4j),通过工厂类获取到的Log实例其实就是Jdk14Logger实例(方法实际上调用的就是JUL的日志方法),我们之后看源码即可知晓。


2.2、应用Log4j(第三方)

想要从JUL切换到Log4j,我们只需要简单一步,导入Log4jjar包即可,引入坐标:

<dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.12</version>
</dependency>

添加配置文件log4j.properties,若是maven项目放置到resource目录下:

# rootLogger日志等级为trace,输出到屏幕上
log4j.rootLogger = trace,console# console
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern= [%-5p]%r %l %d{yyyy-MM-dd HH:mm:ss:SSS} %m%n

测试程序

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;public class LogTest {public static void main(String[] args) {//获取Log实例Log log = LogFactory.getLog(LogTest.class);log.info("info");}
}

可以看到使用相同的方法,随着我们的jar包引入切换使用为Log4j的日志方法,是不是很妙。之后我们看其中源码即可知道究竟是如何实现了的。


三、源码分析

3.1、Log接口实现类分析

我们知道Log接口有多个实现类,其实现类的方法即调用的是各个日志框架的方法,我们深入源码查看一下JUL、Log4j的日志方法是如何被应用到JCL的实现类中的:

Jdk14Logger实现类

我们注重看其中的构造器以及部分日志等级方法:

//注意这里引入的类是JDK自带的日志JUL的Logger类
import java.util.logging.Level;
import java.util.logging.Logger;public class Jdk14Logger implements Log, Serializable {protected static final Level dummyLevel = Level.FINE;protected transient Logger logger = null;//默认为nullpublic Jdk14Logger(String name) {this.name = name;//调用了自身的getLogger()方法logger = getLogger();//见1}//1、获取Logger实例方法public Logger getLogger() {if (logger == null) {//重要:这里调用的是java.util.logging.Logger(即JUL)的获取实例方法logger = Logger.getLogger(name);}return logger;}//info日志等级调用public void info(Object message) {//实际上就是调用的JUL的INFO日志等级的Log方法log(Level.INFO, String.valueOf(message), null);}//fatal:我们之前在Log4j中看到是最高等级,这里也用来表示最高的意思public void fatal(Object message, Throwable exception) {//实际上就是调用JUL的SERVER日志等级(最高)的Log方法log(Level.SEVERE, String.valueOf(message), exception);}
}

看到这里我们其实心里就清楚了,原来就是外面套了个方法呀,通过继承Log接口来让Log接口统一管理各个日志框架,从而达到切换日志框架不改变代码的操作。那对于JCLLog4j的方法我们也差不多知晓了是如何实现了的。

Log4JLogger实现类

  • 老样子都实现了Log接口的日志等级方法

直接看其中构造器以及相关方法还有静态代码块:

//注意这里使用的是Log4j的日志
import org.apache.log4j.Logger;
import org.apache.log4j.Priority;
import org.apache.log4j.Level;public class Log4JLogger implements Log, Serializable {//默认为nullprivate transient volatile Logger logger = null;private static final String FQCN = Log4JLogger.class.getName();//类被加载时进行初始化static {if (!Priority.class.isAssignableFrom(Level.class)) {throw new InstantiationError("Log4J 1.2 not available");}Priority _traceLevel;try {} catch(Exception ex) {_traceLevel = Level.DEBUG;}traceLevel = _traceLevel;}//无参构造器public Log4JLogger() {name = null;}//有参构造器public Log4JLogger(String name) {this.name = name;//通过调用自身类的getLogger()获取this.logger = getLogger();//见1}//1、通过org.apache.log4j.Logger的getLogger()来获取实例public Logger getLogger() {Logger result = logger;if (result == null) {synchronized(this) {result = logger;if (result == null) {logger = result = Logger.getLogger(name);}}}return result;}//fatal等级方法就是调用的Log4j的FATAL等级,是一致的public void fatal(Object message) {getLogger().log(FQCN, Level.FATAL, message, null);}
}
  • 我们可以看到当获取该实现类的实例对象时,做了获取Log4jLogger实例操作,其中的日志等级方法中实际就是调用的Log4j的日志方法。

注意:看其中的静态方法就有一个Level.DEBUG;获取操作,我们可以试想一下若是没有引入Log4j的jar包,那么该操作也不会实现即可报错,也是一种证实开发者没有导入Log4j的操作(这里只是我的设想)。


3.2、JCL原理分析(动态加载Log实现类)

分析下面的第7行程序段:

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;public class LogTest {public static void main(String[] args) {//获取Log实例Log log = LogFactory.getLog(LogTest.class);log.info("info");}
}

源码分析

public abstract class LogFactory {//1、实际调用了其实现类的方法public static Log getLog(Class clazz) throws LogConfigurationException {//获取LogFactoryImplreturn getFactory().getInstance(clazz);//见2}
}public class LogFactoryImpl extends LogFactory {protected Constructor logConstructor = null;//该数组中包含了对应的全限定类名private static final String[] classesToDiscover = {LOGGING_IMPL_LOG4J_LOGGER,//"org.apache.commons.logging.impl.Log4JLogger""org.apache.commons.logging.impl.Jdk14Logger","org.apache.commons.logging.impl.Jdk13LumberjackLogger","org.apache.commons.logging.impl.SimpleLog"};//2、获取Log实例public Log getInstance(Class clazz) throws LogConfigurationException {//这里传入类的名称return getInstance(clazz.getName());//见3}//3、其中获取到Log实例public Log getInstance(String name) throws LogConfigurationException {Log instance = (Log) instances.get(name);if (instance == null) {//通过类名获取到Log实例instance = newInstance(name);//见4instances.put(name, instance);}return instance;}//4、获取新的实例protected Log newInstance(String name) throws LogConfigurationException {Log instance;try {//默认为nullif (logConstructor == null) {//重要:查找Log实现类,及获取Log的单个实现类instance = discoverLogImplementation(name);//见5}else {Object params[] = { name };instance = (Log) logConstructor.newInstance(params);}if (logMethod != null) {Object params[] = { this };logMethod.invoke(instance, params);}//返回获取到的Log实例即可return instance;} catch (LogConfigurationException lce) {....}//5、该方法通过数组的顺序来进行实现类实例private Log discoverLogImplementation(String logCategory)throws LogConfigurationException {...//无关紧要的先省略if (isDiagnosticsEnabled()) {logDiagnostic("No user-specified Log implementation; performing discovery" +" using the standard supported logging implementations...");}//核心部分:从数组中进行遍历,可见classesToDiscover数组内容(上面),顺序依次为Log4JLogger、Jdk14Logger...//其中的result == null则是判断是否获取到了实例,若获取到退出for循环for(int i=0; i<classesToDiscover.length && result == null; ++i) {result = createLogFromClass(classesToDiscover[i], logCategory, true);}if (result == null) {throw new LogConfigurationException("No suitable Log implementation");}//返回指定实例return result;}
  • 第38行:调用方法获取到Log的实现类,确定使用的日志框架。
  • 第73行:核心代码,根据多种框架实现类的权限定类名去查找获取Log实例的。

说明:我们在调用LogFactory.getLog()方法时获取了JCLLog接口的实现类实例,在实现类中的构造器里获取到了真正对应的日志框架Logger实例。

这样的话我们之前的第二部分就解释的通为什么默认获取到的是JUL,在添加了jar包之后则为Log4j了!!!


总结

1、JCL日志门面中Log是通用接口,LogFactory.getLog()用于获取对应Log的实现类。

2、Log接口(包含了主要的日志等级方法)的实现类主要的是两个,Jdk14Logger(即JUL,不导任何包默认)和Log4JLogger(即Log4j,导入Log4j则切换)。

3、对于获取Log实例是在LogFactory.getLog()中执行获取的,执行顺序见如下核心代码

private static final String[] classesToDiscover = {LOGGING_IMPL_LOG4J_LOGGER,//"org.apache.commons.logging.impl.Log4JLogger""org.apache.commons.logging.impl.Jdk14Logger","org.apache.commons.logging.impl.Jdk13LumberjackLogger","org.apache.commons.logging.impl.SimpleLog"
};//核心部分:从数组中进行遍历,可见classesToDiscover数组内容(上面),顺序依次为Log4JLogger、Jdk14Logger...
//其中的result == null则是判断是否获取到了实例,若获取到退出for循环
for(int i=0; i<classesToDiscover.length && result == null; ++i) {result = createLogFromClass(classesToDiscover[i], logCategory, true);
}

4、在获取到指定Log实例时,即使用构造器会进行各个实现类中的Logger实例的赋值获取(通过对应的日志框架),其中的日志等级方法调用的是对应框架的日志等级方法。


参考资料

[1] 视频:2020年Java进阶教程,全面学习多种java日志框架


我是长路,感谢你的耐心阅读,如有问题请指出,我会听取建议并进行修正。
欢迎关注我的公众号:长路Java,其中会包含软件安装等其他一些资料,包含一些视频教程以及学习路径分享。
编程学习qq群:891507813 我们可以一起探讨学习
注明:转载可,需要附带上文章链接

03、JCL(日志门面)相关推荐

  1. Java日志框架 -- JCL日志门面(JCL概念介绍、JCL示例)

    1. JCL 全称为Jakarta Commons Logging,是Apache提供的一个通用日志API.是日志门面的一种实现方式,另外一种日志门面的实现方式是Slf4j. 它是为 "所有 ...

  2. 日志门面技术(3):JCL(Jakarta Commons Logging)

    目录 背景 ▎ JCL的诞生 JCL 是什么? ▎快速入门案例 JCL原理 ✈ 源码断点查看执行流程 JCL日志门面总结 ☁ 每日一题:为什么要学习日志门面JCL? 日志框架出现的历史顺序:Log4j ...

  3. 日志框架(3) : 日志门面、JCL介绍、SLF4J介绍

    文章目录 日志门面 日志门面概述 门面模式(外观模式) 日志门面 常见的日志框架及日志门面 JCL简介 SLF4J SLF4J简介 SLF4J桥接技术 SLF4J特点 SLF4J集成日志实现(jul, ...

  4. Java日志框架 -- SLF4J日志门面(入门案例、SLF4J优点、SLF4J日志绑定、SL4J桥接旧的日志框架)

    1. SLF4J日志门面 JCL日志门面逐渐被淘汰了,因为他无法动态的扩展具体的日志实现框架. 简单日志门面(Simple Logging Facade For Java) SLF4J主要是为了给Ja ...

  5. Java日志门面- JCL和 常用日志门面SLFJ详解

    使用日志门面的原因 目前经常用的日志框架技术有:JUL.Log4j.log4j2.logback用来记录日志信息 ,之前我们讲过,我们学习不同的日志框架.他们的API是不同的,这样难以进行有效的记忆, ...

  6. Java日志门面技术 JCL

    文章目录 背景 JCL的诞生 JCL是什么? 快速入门 JCL原理 设计缺陷 背景 在前面博文中,我们分别讲述了常用的2个日志框架:JUL(Java Util Logging).Log4J.那么如何选 ...

  7. java常用日志框架日志门面及实现 SLF4J 、Jboss-logging 、JCL、Log4j、Logback、Log4j2、JUL,springboot集成 log4j、log4j2

    java常用日志框架日志门面SLF4J .Jboss-logging .JCL.Log4j及实现 Logback.Log4j2.JUL,springboot集成 log4j.log4j2 .logba ...

  8. 学习Java日志框架之——搞懂日志门面(JCL+SLF4J)

    文章目录 系列文章目录 一.什么是日志门面 1.门面模式(外观模式) 2.日志门面 二.了解JCL 1.JCL组件结构 2.JCL案例 (1)JCL默认实现 (2)导入log4j测试原有程序 三.SL ...

  9. 常用日志门面和日志实现

    一.什么是日志门面和日志实现 日志是什么?日志:说明系统实时运行状态的信息. 比如:System.out.println()语句就是一种最低级的日志. 什么是日志门面和日志实现? 日志门面:是日志实现 ...

最新文章

  1. 微软BI 之SSIS 系列 - Lookup 组件的使用与它的几种缓存模式 - Full Cache, Partial Cache, NO Cache...
  2. 简易在线实验室管理系统
  3. CentOS系统启动流程
  4. 算法:删除数组中的重复项
  5. 吴恩达深度学习课程deeplearning.ai课程作业:Class 1 Week 4 assignment4_2
  6. 通俗易懂:贪心算法(一):分配问题 (力扣455分发饼干 和135分发糖果)
  7. VTK:Points之PowercrustExtractSurface
  8. 过滤XML数据中的非主流特殊字符
  9. Exchange与ADFS单点登录 PART 3:部署和配置WAP
  10. asp命令执行语句】_2分钟教你使用ASP.NET CORE创建并发布网页应用
  11. [HAOI2008]糖果传递 结论题
  12. 苹果笔记本电脑亮度无法调节_苹果更新笔记本加量还降价,教育优惠全面开启!...
  13. Bailian2795 金银岛【背包+贪心】
  14. 机器学习之邹博笔记1
  15. ipxspx协议linux,三大协议 TCPIP NETBIOS IPX (转)
  16. 2017实习生在线编程——奇虎360(分金子)
  17. python 100以内的奇数和_python入门:输出1-100之内的所有奇数和偶数
  18. Adobe Flash Player 是什么
  19. 系统性能优化策略 -- 持续优化更新
  20. bitmap.compress(图片压缩的两种方式)(1,质量压缩;2,采样率压缩)

热门文章

  1. 环链电动葫芦出现下滑怎么办
  2. 对象的发布与逸出简单理解
  3. CC11XX系列的介绍和区别
  4. 面试官:Spring中用了哪些设计模式?
  5. android用支付宝打开指定链接,手机浏览器唤起支付宝并打开指定页面
  6. 场强测试软件,无线场强测试系统的软件结构设计与实现
  7. 汽车人生系列之(一): 人生如车
  8. Microsoft Visual Studio Installer Project下载太慢
  9. 达梦环境配置ODBC驱动
  10. 算法——股票买卖问题