commons-logging + log4j源码分析
分析之前先理清楚几个概念
Log4J = Log For Java
SLF4J = Simple Logging Facade for Java
看到Facade首先想到的就是设计模式中的门面(Facade)模式,实际上SLF4J 就是一个装"门面"的java日志框架,它只提供一层抽象且通用的日志API供调用方写日志使用,而真正实现写日志功能的则是Log4J、logback等框架和从jdk1.4之后开始提供的java.util.logging包,而具体要使用谁就要看SLF4J中设置的策略,整体来看的话也确实是使用了门面模式
关于这几个日志框架的诞生和关系推荐看下这篇博客:https://blog.csdn.net/qq_32625839/article/details/80893550
Apache的commons-logging和SLF4J 一样,也是一个抽象的日志框架,使用得更广泛,下面通过几段源码分析下其内部的门面模式是怎样实现的
一般写日志之前都要先用下面的方法获取到log对象
Log log = LogFactory.getLog(clz.getName()); 进入getLog方法
public static Log getLog(String name) throws LogConfigurationException {
return getFactory().getInstance(name);
}
看来是先获取到log工厂对象再获取到log对象,进入getFactory方法
//获取到上下文中的类加载器
ClassLoader contextClassLoader = getContextClassLoaderInternal();
//先尝试从缓存中获取LogFactory
LogFactory factory = getCachedFactory(contextClassLoader);
if (factory != null) {
return factory;
}
//如果存在commons-logging.properties配置文件且其中的use_tccl配置项为false,则使用thisClassLoader作为后续的类加载器,thisClassLoader就是当前类的加载器
Properties props = getConfigurationFile(contextClassLoader, FACTORY_PROPERTIES);
ClassLoader baseClassLoader = contextClassLoader;
if (props != null) {
String useTCCLStr = props.getProperty(TCCL_KEY);
if (useTCCLStr != null) {
if (Boolean.valueOf(useTCCLStr).booleanValue() == false) {
baseClassLoader = thisClassLoader;
}
}
}
//获取org.apache.commons.logging.LogFactory系统配置项,若存在则使用配置的类新建一个工厂对象
String factoryClass = getSystemProperty(FACTORY_PROPERTY, null);
if (factoryClass != null) {
factory = newFactory(factoryClass, baseClassLoader, contextClassLoader);
} //利用java中的SPI机制根据META-INF/services/org.apache.commons.logging.LogFactory中的配置类新建工厂对象
if (factory == null) {
try {
final InputStream is = getResourceAsStream(contextClassLoader, SERVICE_ID);
...
factory = newFactory(factoryClassName, baseClassLoader, contextClassLoader );
... //根据commons-logging.properties中的配置属性org.apache.commons.logging.LogFactory新建工厂对象
if (factory == null) {
String factoryClass = props.getProperty(FACTORY_PROPERTY);
factory = newFactory(factoryClass, baseClassLoader, contextClassLoader);
//如果以上都没成功创建工厂对象则直接使用默认的org.apache.commons.logging.impl.LogFactoryImpl类创建
factory = newFactory(FACTORY_DEFAULT, thisClassLoader, contextClassLoader); //最后放入缓存 cacheFactory(contextClassLoader, factory);
一般上述过程中属性配置都不加的话默认的logFactory实现类就是LogFactoryImpl,然后进入到它的getInstance方法看下
public Log getInstance(String name) throws LogConfigurationException {
Log instance = (Log) instances.get(name);
if (instance == null) {
instance = newInstance(name);
instances.put(name, instance);
}
return instance;
}
进入newInstance
protected Log newInstance(String name) throws LogConfigurationException {
Log instance;
try {
if (logConstructor == null) {
instance = discoverLogImplementation(name);
}
else {
Object params[] = { name };
instance = (Log) logConstructor.newInstance(params);
}
if (logMethod != null) {
Object params[] = { this };
logMethod.invoke(instance, params);
}
return instance;
进入discoverLogImplementation
private Log discoverLogImplementation(String logCategory)
throws LogConfigurationException {
initConfiguration();
Log result = null; //findUserSpecifiedLogClassName方法内部也是读取一些系统配置项,如果读取到了就根据配置的类名来创建日志对象
String specifiedLogClassName = findUserSpecifiedLogClassName();
if (specifiedLogClassName != null) {
result = createLogFromClass(specifiedLogClassName,logCategory,true);
if (result == null) {
StringBuffer messageBuffer = new StringBuffer("User-specified log class '");
messageBuffer.append(specifiedLogClassName);
messageBuffer.append("' cannot be found or is not useable.");
informUponSimilarName(messageBuffer, specifiedLogClassName, LOGGING_IMPL_LOG4J_LOGGER);
informUponSimilarName(messageBuffer, specifiedLogClassName, LOGGING_IMPL_JDK14_LOGGER);
informUponSimilarName(messageBuffer, specifiedLogClassName, LOGGING_IMPL_LUMBERJACK_LOGGER);
informUponSimilarName(messageBuffer, specifiedLogClassName, LOGGING_IMPL_SIMPLE_LOGGER);
throw new LogConfigurationException(messageBuffer.toString());
}
return result;
}
//如果上面没有创建成功则根据classesToDiscover数组的值作为类名依次创建日志对象,直到创建成功(不为空)就返回
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;
}
其中classesToDiscover数组的值是写死的
private static final String[] classesToDiscover = {
"org.apache.commons.logging.impl.Log4JLogger",
"org.apache.commons.logging.impl.Jdk14Logger",
"org.apache.commons.logging.impl.Jdk13LumberjackLogger",
"org.apache.commons.logging.impl.SimpleLog"
}
所以当我们的项目中引入的commons-logging和Log4j的jar包,其实不需要做任何配置,就会优先使用Log4JLogger做为实际的写日志实现类
整个过程中有两点需要注意的:
1.日志工厂对象和日志实现类对象都是先使用当前类或者thread context的classLoad将类加载进来再通过反射创建对象,这样的话如果有的插件使用自定义的classLoad加载,当插件内部打印日志时可能会出现无法创建日志对象或者使用了和预期不一致的日志对象
2.动态查找:对日志工厂实现类和日志实现类的动态查找基本上都是通过读取系统配置或者代码里写死的方式来查找,其实可以通过java SPI机制来实现
slf4J的实现方式与上述是完全不同的,它采用的是静态绑定,可以避免classLoad的问题,但在使用时要引入各种桥接包,如果引入了两个相反的桥接包就会导致循环依赖的StackOverflow
commons-logging + log4j源码分析的更多相关文章
- java 日志体系(四)log4j 源码分析
java 日志体系(四)log4j 源码分析 logback.log4j2.jul 都是在 log4j 的基础上扩展的,其实现的逻辑都差不多,下面以 log4j 为例剖析一下日志框架的基本组件. 一. ...
- Python 日志打印之logging.getLogger源码分析
日志打印之logging.getLogger源码分析 By:授客 QQ:1033553122 #实践环境 WIN 10 Python 3.6.5 #函数说明 logging.getLogger(nam ...
- Log4j源码分析
一.slf4j和log4j的关系: 也就是说slf4j仅仅是一个为Java程序提供日志输出的统一接口,并不是一个具体的日志实现方案,就比如JDBC一样,只是一种规则而已.必须搭配具体的log实现方案比 ...
- Log4j漏洞源码分析
Log4j漏洞源码分析 这几天Log4j的问题消息满天飞,今天我们就一起来看看从源码角度看看这个漏洞是如何产生的. 大家都知道这次问题主要是由于Log4j中提供的jndi的功能. 具体涉及到的入口类是 ...
- 精尽Spring Boot源码分析 - 日志系统
该系列文章是笔者在学习 Spring Boot 过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring Boot 源码分析 GitHub 地址 进行阅读 Sprin ...
- Solr4.8.0源码分析(4)之Eclipse Solr调试环境搭建
Solr4.8.0源码分析(4)之Eclipse Solr调试环境搭建 由于公司里的Solr调试都是用远程jpda进行的,但是家里只有一台电脑所以不能jpda进行调试,这是因为jpda的端口冲突.所以 ...
- Shiro源码分析
1.入口类:AbstractAuthenticator 用户输入的登录信息经过其authenticate方法: public final AuthenticationInfo authenticate ...
- solr源码分析之solrclound
一.简介 SolrCloud是Solr4.0版本以后基于Solr和Zookeeper的分布式搜索方案.SolrCloud是Solr的基于Zookeeper一种部署方式.Solr可以以多种方式部署,例如 ...
- SSO单点登录系列1:cas客户端源码分析cas-client-java-2.1.1.jar
落雨 cas 单点登录 希望能给以后来研究cas的兄弟留下一点思路,也算是研究了两天的成果,外国人的代码写的很晦涩,翻译下来也没有时间继续跟进,所以有错误的还请大家跟帖和我讨论,qq 39426378 ...
随机推荐
- Akka-CQRS(15)- Http标准安全解决方案:OAuth2+JWT
上期讨论过OAuth2, 是一种身份认证+资源授权使用模式.通过身份认证后发放授权凭证.用户凭授权凭证调用资源.这个凭证就是一种令牌,基本上是一段没什么意义的加密文,或者理解成密钥也可以.服务方通过这 ...
- Django项目的创建和管理
1.主题 这部分教程主要介绍如何通过Pycharm创建.管理.运行一个Django工程.对于Django模块的相关知识大家可以参考Python社区. 2.准备环境 Django版本为2.0或更高 Py ...
- SpringBoot整合Swagger和Actuator
前言 本篇文章主要介绍的是SpringBoot整合Swagger(API文档生成框架)和SpringBoot整合Actuator(项目监控)使用教程. SpringBoot整合Swagger 说明:如 ...
- 灵雀云CTO陈恺应邀出席国泰君安信息产业投资峰会,探讨全球科技产业新格局
2019年7月9-10日,国泰君安信息产业投资峰会在上海陆家嘴举办.作为国内容器PaaS领域的龙头公司,灵雀云受邀出席本次大会,在“数字化转型从云做起”的论坛中,CTO陈恺发表了<云原生助力企业 ...
- IIS配置应用时,请求Header或Cookie过长
可以在注册表中配置IIS MaxFieldLength MaxRequestBytes 注意配置后需重启http服务于iis服务 net stop http net start http iisres ...
- Socket编程(C语言实现):socket()函数英文翻译
最近开始研究使用Socket API来网络编程,想着把自己的感想.感悟写下来.我发现在编程之外还有不少概念性的东西要学习.我觉得应该有以下几点吧: 1.得了解下计算机网络的基本概念,如OSI的7层模型 ...
- 在rman恢复中incarnation的概念
摘要 本文主要介绍incarnation的由来,在rman恢复中的作用,以及相关rman恢复的注意事项. 概念说明 从10g开始,incarnation被引入,用于跨越resetlogs进行恢复,由此 ...
- poi解析excel文件报错
getFileMagic() only operates on streams which support mark(int) 使用 bis 解决 BufferedInputStream bis = ...
- GIL与异步回调
07.07自我总结 一.GIL 1.概念 在CPython中,这个全局解释器锁,也称为GIL,是一个互斥锁 2.带来的问题 首先必须明确执行一个py文件,分为三个步骤 从硬盘加载Python解释器到内 ...
- SQL Server 保存特殊字符时乱码
场景:协同完成的项目,数据库是同事创建,我们共同使用的表. 客户反应有一些字符查看的时候出现乱码.第一反应是否编码规则的问题.后来去数据库查发现数据库里就是乱码,百度了一下发现说特殊字符要保存在NVA ...