设为首页 收藏本站
查看: 759|回复: 0

[经验分享] Apache Commons-Logging 源码分析

[复制链接]

尚未签到

发表于 2017-1-1 07:36:39 | 显示全部楼层 |阅读模式
  Commons-Logging
  包结构:
DSC0000.png

org.apache.commons.logging.Log
 
一个接口,定义了五个输出级别(从低到高依次严重)
 
trace (the least serious)
debug
info
warn
error
fatal (the most serious)
 
PS:以下XXX代表某一个日志级别
 
每个级别都有一个isXXXEnabled 方法 可以判断是否启用了这个级别的输出
同时还有两个输出方法:
XXX(Object message)
XXX(Object message, Throwable t)
 
org.apache.commons.logging.LogSource
 
已经被弃用的类,提供了获得log实例对象的方法,LogFactory替代了这个类的作用。
 
不过从这个类中还是可以看到是如何获得log实现类的。
 
首先判断 org.apache.log4j.Logger 是否在classpath中,也就是说默认采用log4j作为实现。
 
其次判断
java.util.logging.Logger 与 org.apache.commons.logging.impl.Jdk14Logger是否存在,将使用jdk1.4提供的log作为底层实现。
 
否则将使用org.apache.commons.logging.impl.NoOpLog作为实现,将不提供log输出。
 
org.apache.commons.logging.LogFactory
 
抽象类,log工厂类,产生log的实例对象。


最常用的方法是getLog(Class
clazz)
 
可以看到方法内代码只有一行: return (getFactory().getInstance(clazz));
 
getInstance是个抽象方法,后面讲解。
 
这里解析下getFactory方法
 


  • 获得当前线程的classloader,命名为contextClassLoader
  • 根据contextClassLoader在缓存中获得logFactory,这里的缓存使用的是org.apache.commons.logging.impl.WeakHashTable,key是classloader,value是logFactory,如果在缓存中有logFactory则直接返回,没有进入下面的流程
  • 读取配置文件commons-logging.properties
  • 如果读到了配置文件,判断其中use_tccl属性,然后设定baseClassLoader是使用contextClassLoader还是使用加载本Class文件的那个classloader(名字为thisClassLoader)
  • 下面生成logFactory,这里会有使用四种方式,依次来尝试生成。
  • 将生成的logFactory缓存起来
  • 返回logFactory




第5条种四种方式是:





  • 通过系统属性中寻找org.apache.commons.logging.LogFactory的value值,根据值为类名生成logFactory
  • 通过资源META-INF/services/org.apache.commons.logging.LogFactory,获得的值为类名生成logFactor
  • 通过刚才读取的配置文件commons-logging.properties,从中获取以org.apache.commons.logging.LogFactory为key的value值,根据值为类名生成logFactory
  • 如果上面都不成功的话,会使用默认的实现类org.apache.commons.logging.impl.LogFactoryImpl来生成logFactory



 
下面来看下默认实现类有什么内容,其中的getInstance是如果运作的。
 
org.apache.commons.logging.impl.LogFactoryImpl
 
主要查看getInstance方法
 
源码如下:
 

        Log instance = (Log) instances.get(name);
if (instance == null) {
instance = newInstance(name);
instances.put(name, instance);
}
return (instance);
可以看到逻辑很简单,在一个缓存中查找是否有这个log,没有就生成一个,并放到缓存中,这里的name就是传入的className。
 
下面主要解析下newInstance方法:
 
首先会判断logConstructor是否为空,第一次必然为空,然后就调用discoverLogImplementation
 
在其方法中,会先查看是否有用户指定的实现类。如果没有则按照下面的顺序,在classpath中去寻找,找到就后进行实例化并返回该实例。
 
顺序如下:
org.apache.commons.logging.impl.Log4JLogger
org.apache.commons.logging.impl.Jdk14Logger

org.apache.commons.logging.impl.Jdk13LumberjackLogger

org.apache.commons.logging.impl.SimpleLog
 
生成log实例会调用方法:createLogFromClass
 
这里会先找到这个实现类的class文件
 
 

URL url;
String resourceName = logAdapterClassName.replace('.', '/') + ".class";
if (currentCL != null) {
url = currentCL.getResource(resourceName );
} else {
url = ClassLoader.getSystemResource(resourceName + ".class");
}

 
其实这里的1069行代码,ME觉得应该是个bug,明明resourceName已经加上了.class后缀,这里竟然要再加一遍,估计这个分支很少会跑进去,所以这个错误没有发现吧…
 
然后找个加载这个class文件,然后获取构造函数(带有一个String参数),接着实例化。
 
如果这里affectState(createLogFromClass最后一个参数)为true的话,会将刚才找到的构造函数,log类名等信息记录下来,方面后面使用。
 
最后返回log实例。
 
下面是关于log接口的几个实现类:
org.apache.commons.logging.impl.Log4JLogger
org.apache.commons.logging.impl.Jdk14Logger

org.apache.commons.logging.impl.Jdk13LumberjackLogger

org.apache.commons.logging.impl.SimpleLog
 
可以看到,前三个都是相当于适配器的功能,也就是说里面实际实现,是委托给其他log框架去完成的,例如log4j,jdk1.4自带的logging等等,最后一个才是该项目提供的log接口的一个实现类。
 
举个例子:
public void debug(Object message, Throwable
t) 这个方法
 
Log4JLogger中实现:
 


    public void debug(Object message, Throwable t) {
getLogger().log(FQCN, Priority.DEBUG, message, t );
}
 
SimpleLog中实现:
 

    public final void debug(Object message, Throwable t) {
if (isLevelEnabled(SimpleLog.LOG_LEVEL_DEBUG)) {
log(SimpleLog.LOG_LEVEL_DEBUG, message, t);
}
}
 
这里的log方法中,可以看到使用了java.io.StringWriter进行输出的。
 


 
总结下就是,commons-logging项目采用了一种动态搜索的机制,在实际运行时,动态的选择log 的实现方式。如果没有log4j,就使用jdk1.4的logging,如果再没有,就使用Lumberjack,它是为jdk1.2与1.3实现了Java日志 API,再没有,那就使用自身提供的一个log(SimpleLog)。
 
可以说commons-logging提供了一套简单的日志API给用户,但对于输出格式设定等问题,没有提供必要的API给用户,这点只能靠使用Log4j等更加强大的日志框架来完成,这也可能是为什么采用log4j作为最优先实现的原因吧。
 
项目中使用commons-logging的原因,可能是为了不与log4j死死的绑定在一起,使用commons-logging作为一个中间层,达到解耦的目的。
 
所以说commons-logging 的最大贡献就是为日志输出提供一个统一的接口,并且会在运行时,很方便的自动选择好日志实现系统。
 
 
顺便摘抄一段关于日志级别的信息:
 
确保日志信息在内容上和反应问题的严重程度上的恰当,是非常重要的。




  • fatal非常严重的错误,导致系统中止。期望这类信息能立即显示在状态控制台上。

  • error其它运行期错误或不是预期的条件。期望这类信息能立即显示在状态控制台上。

  •  warn使用了不赞成使用的API、非常拙劣使用API, '几乎就是'错误, 其它运行时不合需要和不合预期的状态但还没必要称为 "错误"。期望这类信息能立即显示在状态控制台上。

  • info运行时产生的有意义的事件。期望这类信息能立即显示在状态控制台上。

  • debug系统流程中的细节信息。期望这类信息仅被写入log文件中。

  • trace更加细节的信息。期望这类信息仅被写入log文件中。
  通常情况下,记录器的级别不应低于info。
  也就是说,通常情况下debug的信息不应被写入log文件中。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-322137-1-1.html 上篇帖子: apache顶级域名跳转 下篇帖子: apache级域名跳
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表