官网
https://commons.apache.org/proper/commons-logging/
1. 什么是JCL?
Apache Commons Logging(简称 JCL)是一个轻量级的日志接口库,提供了日志记录的抽象层。它答应开辟职员编写独立于具体日志实现的代码,而具体的日志实现(如Log4j、SLF4J或java.util.logging)可以在运行时设置。这种设计简化了日志库的集成和切换。
2. JCL的主要特点
- 灵活性:通过抽象层,可以自由选择日志实现。
- 自动发现机制:运行时动态发现类路径中的可用日志实现。
- 兼容性:支持主流日志框架,如Log4j、SLF4J和java.util.logging。
- 简朴易用:只需依赖commons-logging.jar,无需复杂设置。
3. JCL的核心组件
- Log 接口
提供通用的日志记录方法(如debug、info、warn、error、fatal)。
- Log log = LogFactory.getLog(YourClass.class);
- log.info("信息日志");
- log.error("错误日志");
复制代码 - LogFactory 类
用于创建Log接口的实例。LogFactory实现了日志系统的自动发现和绑定。
4. JCL的实现机制
JCL利用自动发现机制选择合适的日志实现:
- 首先查抄类路径中是否存在Log4j,假如存在则利用Log4j (高版本的JCL已经移除了对log4j的支持)
- 假如找不到Log4j,JCL会查抄java.util.logging并利用它。
- 假如前两个都不可用,则利用内置的SimpleLog作为默认实现。
可以通过设置文件commons-logging.properties显式指定日志实现。例如:
- org.apache.commons.logging.Log=org.apache.commons.logging.impl.Log4JLogger
复制代码 5. SimpleLog 简介
JCL内置的SimpleLog是一个轻量级实现,实用于没有复杂日志需求的小型项目。
它通过系统属性进行设置,例如:
- org.apache.commons.logging.simplelog.defaultlog:设置默认日志级别。
- org.apache.commons.logging.simplelog.showdatetime:是否表现日期时间。
6. Code
POM
- <dependency>
- <groupId>commons-logging</groupId>
- <artifactId>commons-logging</artifactId>
- <version>1.3.2</version>
- </dependency>
复制代码 Example 1 : 默认日志实现 (JCL 1.3.2版本)
- package com.artisan.jcl;
- import org.apache.commons.logging.Log;
- import org.apache.commons.logging.LogFactory;
- public class JavaCommonsLoggingTest {
- private static final Log log = LogFactory.getLog(JavaCommonsLoggingTest.class);
- public static void main(String[] args) {
- log.info("这是信息日志");
- log.warn("这是警告日志");
- log.error("这是错误日志");
- }
- }
复制代码
Example 2 : JCL (1.2版本) + Log4J 【安全风险高,请勿利用】
log4j1从2005年11月更新到2012年3月, 最新的依赖(May 26, 2012)
- <dependency>
- <groupId>log4j</groupId>
- <artifactId>log4j</artifactId>
- <version>1.2.17</version>
- </dependency>
复制代码 2015年8月5日,项目管理委员会公布Log4j 1.x已End Of Life 。发起用户利用Log4j 1升级到Apache Log4j 2
为了演示这种组合,我们将JCL降级到1.2版本
pom
- <!-- Jakarta Commons Logging -->
- <dependency>
- <groupId>commons-logging</groupId>
- <artifactId>commons-logging</artifactId>
- <version>1.2</version>
- </dependency>
- <!-- Log4j 核心依赖 -->
- <dependency>
- <groupId>log4j</groupId>
- <artifactId>log4j</artifactId>
- <version>1.2.17</version>
- </dependency>
复制代码 log4j.properties
- # 设置根日志记录器的日志级别为 DEBUG,并将其输出到控制台和文件
- log4j.rootLogger=DEBUG, console, file
- # 配置控制台输出
- log4j.appender.console=org.apache.log4j.ConsoleAppender
- log4j.appender.console.layout=org.apache.log4j.PatternLayout
- log4j.appender.console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
- # 可选:配置日志文件滚动
- log4j.appender.file=org.apache.log4j.RollingFileAppender
- log4j.appender.file.File=application.log
- log4j.appender.file.MaxFileSize=10MB
- log4j.appender.file.MaxBackupIndex=10
- log4j.appender.file.layout=org.apache.log4j.PatternLayout
- log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
- # 可选:配置特定包的日志级别
- log4j.logger.com.artisan=DEBUG
复制代码 Code
- package com.artisan.jcl;
- import org.apache.commons.logging.Log;
- import org.apache.commons.logging.LogFactory;
- public class JavaCommonsLoggingTest {
- private static final Log logger = LogFactory.getLog(JavaCommonsLoggingTest.class);
- public static void main(String[] args) {
- logger.trace("This is a trace message");
- logger.debug("This is a debug message");
- logger.info("This is an info message");
- logger.warn("This is a warning message");
- logger.error("This is an error message");
- logger.fatal("This is a fatal message");
- }
- }
复制代码 输出
7. 利用场景与长处
- 实用于须要在多个日志框架之间切换的场景。
- 提供了对遗留系统的支持,使其可以或许与现代日志框架协同工作。
- 适合中央件开辟,避免直接绑定特定的日志框架。
8. 常见问题
- 为什么不直接利用SLF4J?
JCL比SLF4J更早出现,仍被很多遗留系统利用。假如是全新项目,发起考虑SLF4J,它解决了JCL的一些范围性。
- 性能是否有消耗?
JCL的动态绑定机制在启动时大概略有开销,但运行时性能与直接利用日志实现靠近。
9. 动态绑定机制源码分析
让我们以LogFactory.getLog(JavaCommonsLoggingTest.class) 为切入口 ,
- /**
- * 获取指定类的日志记录器实例
- *
- * @param clazz 要获取日志记录器的类
- * @return 指定类的日志记录器实例
- * @throws LogConfigurationException 如果日志配置存在错误,则抛出此异常
- */
- public static Log getLog(final Class<?> clazz) throws LogConfigurationException {
- // 调用日志工厂的实例方法获取日志记录器
- return getFactory().getInstance(clazz);
- }
复制代码 getFactory
重点看下: getFactory()
主要功能是根据一系列优先级规则查找并返回一个 LogFactory 实例。
- 获取类加载器:首先获取当火线程的上下文类加载器。
- 查抄缓存:假如该类加载器已经有一个对应的 LogFactory 实例,则直接返回该实例。
- 加载设置文件:实验从 commons-logging.properties 文件中读取设置信息。
- 确定是否利用TCCL:根据设置文件中的 use_tccl 属性决定是否利用线程上下文类加载器。
- 查找实现类:
- 首先实验通过系统属性 org.apache.commons.logging.LogFactory 查找。
- 假如未找到,实验利用 JDK 1.3 的服务发现机制。
- 假如仍未找到,实验从设置文件中查找。
- 最后,实验利用默认的实现类 org.apache.commons.logging.impl.LogFactoryImpl。
- 创建并缓存实例:创建 LogFactory 实例并将其缓存。
假设: 没有org.apache.commons.logging.LogFactory 这个系统设置项,classpath下没有包罗META-INF/services/org.apache.commons.logging.LogFactory 这个文件的Jar包、没有commons-logging.properties 文件,只有commons-logging这个jar
LogFactoryImpl
我们来看下 LogFactoryImpl ,
- /** Log4JLogger class name */
- private static final String LOGGING_IMPL_LOG4J_LOGGER = "org.apache.commons.logging.impl.Log4JLogger";
- /** Jdk14Logger class name */
- private static final String LOGGING_IMPL_JDK14_LOGGER = "org.apache.commons.logging.impl.Jdk14Logger";
- /** Jdk13LumberjackLogger class name */
- private static final String LOGGING_IMPL_LUMBERJACK_LOGGER =
- "org.apache.commons.logging.impl.Jdk13LumberjackLogger";
- /** SimpleLog class name */
- private static final String LOGGING_IMPL_SIMPLE_LOGGER = "org.apache.commons.logging.impl.SimpleLog";
- private static final String[] classesToDiscover = {
- LOGGING_IMPL_JDK14_LOGGER,
- LOGGING_IMPL_SIMPLE_LOGGER
- };
复制代码 可知: 默认实现为
- LOGGING_IMPL_JDK14_LOGGER org.apache.commons.logging.impl.Jdk14Logger
复制代码 log4j 不再是默认实现
commons-logging的动态绑定机制实现如上,但是这种机制的问题在哪儿呢,由于它利用了ClassLoader探求和载入底层的日志库, 导致了象OSGI如许的框架无法正常工作,由于OSGI的差别的插件利用本身的ClassLoader。 OSGI的这种机制保证了插件相互独立,然而却使Apache Common-Logging无法工作 。 以是就有了Slf4j这种静态绑定的方案。
10. 总结
JCL为日志记录提供了一种同一的接口,虽然不如SLF4J现代化,但在历史遗留系统中仍有广泛的利用代价。假如须要简化日志实现的切换,JCL是一个可靠的选择。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |