JAVA 日志库3
Commons Logging的实现可以分成以下几个步骤:
1. LogFactory类初始化
a. 缓存加载LogFactory的ClassLoader(thisClassLoader字段),出于性能考虑。因为getClassLoader()方法可能会使用AccessController(虽然目前并没有使用),因而缓存起来以提升性能。
b. 初始化诊断流。读取系统属性org.apache.commons.logging.diagnostics.dest,若该属性的值为STDOUT、STDERR、文件名。则初始化诊断流字段(diagnosticStream),并初始化诊断消息的前缀(diagnosticPrefix),其格式为:”[LogFactory from <ClassLoaderName@HashCode>] “, 该前缀用于处理在同一个应用程序中可能会有多个ClassLoader加载LogFactory实例的问题。
c. 如果配置了诊断流,则打印当前环境信息:java.ext.dir、java.class.path、ClassLoader以及ClassLoader层级关系信息。
d. 初始化factories实例(Hashtable),用于缓存LogFactory(context-classloader –-> LogFactory instance)。如果系统属性org.apache.commons.logging.LogFactory.HashtableImpl存在,则使用该属性定义的Class作为factories Hashtable的实现类,否则,使用Common Logging实现的WeakHashtable。若初始化没有成功,则使用Hashtable类本身。使用WeakHashtable是为了处理在webapp中,当webapp被卸载是引起的内存泄露问题,即当webapp被卸载时,其ClassLoader的引用还存在,该ClassLoader不会被回收而引起内存泄露。因而当不支持WeakHashtable时,需要卸载webapp时,调用LogFactory.relase()方法。
e. 最后,如果需要打印诊断信息,则打印“BOOTSTRAP COMPLETED”信息
2. 查找LogFactory类实现,并实例化。
当调用LogFactory.getLog()方法时,它首先会创建LogFactory实例(getFactory()),然后创建相应的Log实例。getFactory()方法不支持线程同步,因而多个线程可能会创建多个相同的LogFactory实例,由于创建多个LogFactory实例对系统并没有影响,因而可以不用实现同步机制。
a. 获取context-classloader实例。
b. 从factories Hashtable(缓存)中获取LogFactory实例。
c. 读取commons-logging.properties配置文件(如果存在的话,如果存在多个,则可以定义priority属性值,取所有commons-logging.properties文件中priority数值最大的文件),如果设置use_tccl属性为false,则在类的加载过程中使用初始化cache的thisClassLoader字段,而不用context ClassLoader。
d. 查找系统属性中是否存在org.apache.commons.logging.LogFactory值,若有,则使用该值作为LogFactory的实现类,并实例化该LogFactory实例。
e. 使用service provider方法查找LogFactory的实现类,并实例化。对应Service ID是:META-INF/services/org.apache.commons.logging.LogFactory
f. 查找commons-logging.properties文件中是否定义了LogFactory的实现类:org.apache.commons.logging.LogFactory,是则用该类实例化一个出LogFactory
g. 否则,使用默认的LogFactory实现:LogFactoryImpl类。
h. 缓存新创建的LogFactory实例,并将commons-logging.properties配置文件中所有的键值对加到LogFactory的属性集合中。
3. 通过LogFactory实例查找Log实例(LogFactoryImpl实现)
使用LogFactory实例调用getInstance()方法取得Log实例。
a. 如果缓存(instances字段,Hashtable)存在,则使用缓存中的值。
b. 查找用户自定义的Log实例,即从先从commons-logging.properties配置文件中配置的org.apache.commons.logging.Log(org.apache.commons.logging.log,旧版本)类,若不存在,查找系统属性中配置的org.apache.commons.logging.Log(org.apache.commons.logging.log,旧版本)类。如果找到,实例化Log实例
c. 遍历classesToDiscover数组,尝试创建该数组中定义的Log实例,并缓存Log类的Constructor实例,在下次创建Log实例是就不需要重新计算。在创建Log实例时,如果use_tccl属性设为false,则使用当前ClassLoader(加载当前LogFactory类的ClassLoader),否则尽量使用Context ClassLoader,一般来说Context ClassLoader和当前ClassLoader相同或者是当前ClassLoader的下层ClassLoader,然而在很多自定义ClassLoader系统中并没有设置正确的Context ClassLoader导致当前ClassLoader成了Context ClassLoader的下层,LogFactoryImpl默认处理这种情况,即使用当前ClassLoader。用户可以通过设置org.apache.commons.logging.Log.allowFlawedContext配置作为这个特性的开关。
d. 如果Log类定义setLogFactory()方法,则调用该方法,将当前LogFactory实例传入。
e. 将新创建的Log实例存入缓存中。
4. 调用Log实例中相应的方法
在使用Commons Logging时,经常在服务器部署中会遇到ClassLoader的问题,这也是经常被很多人所诟病的地方,特别是在和Log4J一起使用的时候。常见的如,由于Common Logging使用非常广泛,因而很多Web容器(WebSphere)在内也会使用它作为日志处理系统而将其jar包引入到容器本身中,此时LogFactory是使用Web容器本身的ClassLoader装载的,即使Log4J中使用了ContextClassLoader来查找配置文件,此时的Thread依然在容器中,因而它使用的ClassLoader还是容器本身的ClassLoader实例,此时需要把Log4J的配置文件放到共享目录下,该配置文件才能被正常识别(以我的理解,容器在启动的时候,它根本无法获得Web应用程序中的jar包,所以也需要将Log4J的jar包放到共享目录中才可以,不过我木有用过WebSphere,也没法测试,所以只能猜测~)。
if (logger.isDebugEnabled()) {
logger.info("Loading XML bean definitions from " + encodedResource.getResource());
}
存在isDebugEnabled()的判断逻辑是为了在避免多余的字符串拼接,即如果不存在isDebugEnabled()判断,即使当前日志级别为ERROR时,在遇到logger.info()调用时,它还会先拼接日志消息的字符串,然后进入该方法内,才发现这个日志语句不用打印。而这种多余的拼接不仅浪费了多余的CPU操作,而且会增加GC的负担。SLF4J则提供以下的方式来解决这个问题:
logger.info("Loading XML bean definitions from {}", encodedResource.getResource());
SLF4J的设计如下:
由于采用了静态绑定的方式,而不是像Commons Logging中的动态绑定,SLF4J中LoggerFactory的实现要比Commons Logging中LogFactory的实现要简单的多。即LoggerFactory调用getILoggerFactory()方法,该方法会初始化LoggerFactory,即通过在bind()方法中加载classpath中的StaticLoggerBinder类,并根据加载结果设置当前LoggerFactory的初始化状态,从而在getILoggerFactory()方法中通过当前LoggerFactory的状态判断返回的ILoggerFactory实例。简单的示意图如下:
JAVA 日志库3的更多相关文章
- JAVA日志库2
一.常用日志Jar关系 2015第30周四Java日志组件 接口:将所有日志实现适配到了一起,用统一的接口调用. 实现:目前主流的日志实现 旧日志到slf4j的适配器:如果使用了slf4j,但是只想用 ...
- Java 日志框架概述(slf4j / log4j / JUL / Common-logging(JCL) / logback)
一.简介 JAVA日志在初期可能官方并没有提供很好且实用的规范,导致各公司或OSS作者选择自行造轮子,这也导致了目前初学者觉得市面上 Java 日志库繁杂的局面. 现在市面流行以 slf4j(Simp ...
- Python GUI之tkinter窗口视窗教程大集合(看这篇就够了) JAVA日志的前世今生 .NET MVC采用SignalR更新在线用户数 C#多线程编程系列(五)- 使用任务并行库 C#多线程编程系列(三)- 线程同步 C#多线程编程系列(二)- 线程基础 C#多线程编程系列(一)- 简介
Python GUI之tkinter窗口视窗教程大集合(看这篇就够了) 一.前言 由于本篇文章较长,所以下面给出内容目录方便跳转阅读,当然也可以用博客页面最右侧的文章目录导航栏进行跳转查阅. 一.前言 ...
- java日志组件介绍(common-logging,log4j,slf4j,logback )
转自:http://www.blogjava.net/daiyongzhi/archive/2014/04/13/412364.html common-logging是apache提供的一个通用的日志 ...
- java日志学习笔记
一.日志家族 Log4j一开始就很强大,在jdk自带日志系统之前,apache就曾经尝试把log4j划为java的一部分,不知为何没能成功,sun还是用了自己很弱的日志系统.为了兼容各个日志系统,ap ...
- [翻译]Java日志终极指南
本文由 ImportNew - Wing 翻译自 loggly.欢迎加入翻译小组.转载请见文末要求. Java日志基础 Java使用了一种自定义的.可扩展的方法来输出日志.虽然Java通过java.u ...
- Java日志框架:SLF4J,Common-Logging,Log4J,Logback说明
Log4j Apache的一个开放源代码项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台.文件.GUI组件.甚至是套接口服务 器.NT的事件记录器.UNIX Syslog守护进程等 ...
- 最受欢迎的Java第三方库
前言 翻译自programcreek: 典型的Java项目通常会依赖一些第三方库,本文总结了一些最受欢迎的Java库,这些类库在各种应用程序中被广泛使用: 当然,Java SDK是最广泛使用的Java ...
- 爆料喽!!!开源日志库Logger的剖析分析
导读 Logger类提供了多种方法来处理日志活动.上一篇介绍了开源日志库Logger的使用,今天我主要来分析Logger实现的原理. 库的整体架构图 详细剖析 我们从使用的角度来对Logger库抽茧剥 ...
随机推荐
- Spark MLlib回归算法LinearRegression
算法说明 线性回归是利用称为线性回归方程的函数对一个或多个自变量和因变量之间关系进行建模的一种回归分析方法,只有一个自变量的情况称为简单回归,大于一个自变量情况的叫做多元回归,在实际情况中大多数都是多 ...
- Android 将HTML5封装成android应用APK文件的几种方法
越来越多的开发者热衷于使用html5+JavaScript开发移动Web App.不过,HTML5 Web APP的出现能否在未来取代移动应用,就目前来说,还是个未知数.一方面,用户在使用习惯上,不喜 ...
- BootStrap_table.js 学习
@{ Layout = null; ViewBag.Title = "基于BootstrapTable的简单应用";} <!--添加相关样式引用--><link ...
- KnockOut下的离开检测
<input type="text" class="form-control" data-bind="event:{ blur:$root.ch ...
- 18/9/22NOIP模拟考
18/9/22NOIP模拟考 其实本来是有多组数据的,出题人忘记在题面上加了 斜眼笑 期望得分:100:实际得分:100 由于种种原因,拿到题的时候已经过去了0.5h+... 然后因为这道题数据范 ...
- CodeForcesGym 100502H Clock Pictures
Clock Pictures Time Limit: 1000ms Memory Limit: 524288KB This problem will be judged on CodeForcesGy ...
- 从头认识java-15.7 Map(4)-介绍HashMap的工作原理-hash碰撞(常常作为面试题)
这一章节我们来讨论一下hash碰撞. 1.什么是hash碰撞? 就是两个对象的key的hashcode是一样的,这个时候怎么get他的value呢? 答案是通过equals遍历table那个位置上面的 ...
- FragMent-通过Arguments方法 跟activity通信
今天主要学习下通过Arguments,实现activity 给fragment传递数据.这个方法也是通过参数bundle来进行数据传输的 直接看如下代码 一,定义一个fragment,在oncreat ...
- C# 中编写函数式代码
http://www.dotnetcurry.com/csharp/1384/functional-programming-fsharp-for-csharp-developers 写给 C# 开发人 ...
- Elasticsearch之es学习工作中遇到的坑(陆续更新)
1:es集群脑裂问题(不要用外网ip,节点角色不要混用) 原因1:阿里云服务器,外网有时候不稳定. 解决方案:单独采购服务器,内网安装 原因2:master和node节点没有分开 解决方案: 分角色: ...