java日志:commons-loging,log4j,slf4j,LogBack的区别

Commons-logging只是一个API,定义了接口,不负责具体的实现,而具体的实现有以下几个:log4j,slf4j,logback

先简单说下Commons-logging的查找日志实现的顺序:

1.   先找org.apache.commons.logging.LogFactory 属性配置

2.  利用service 发现机制,扫描classpah 下的META-INF/services/org.apache.commons.logging.LogFactory 文件,若找到则使用里面的配置。

3.   否则,从Classpath 里寻找commons-logging.properties ,找到则根据里面的配置加载。

4.   否则,使用默认的配置:如果能找到Log4j 则默认使用log4j 实现,如果没有则使用JDK14Logger 实现,再没有则使用commons-logging 内部提供的SimpleLog 实现。

从上述加载流程来看,如果没有做任何配置,只要引入了log4j 并在classpath 配置了log4j.xml ,则commons-logging 就会使log4j ,而代码里不需要依赖任何log4j 的代码。

所以可以看出,使用Log4J主要是log4j.xml配置文件的编写

Log4J配置文件的基本格式如下:


 

其中 [level] 是日志输出级别,共有5级:


 

Appender 为日志输出目的地,Log4j提供的appender有以下几种:


 

Layout:日志输出格式,Log4j提供的layout有以下几种:


 

打印参数: Log4J采用类似C语言中的printf函数的打印格式格式化日志信息,如下:


Slf4j : 全称为Simple Logging Facade for JAVA, 是对不同日志框架提供的一个Facade封装,可以在部署的时候不修改任何配置即可接入一种日志实现方案。

slf4j的代理架构图

slf4j的代理架构图

slf4j的jar 包里存放了配置META-INF/services/org.apache.commons.logging.LogFactory =org.apache.commons.logging.impl.SLF4JLogFactory,而commons-logging 在初始化的时候会找到这个serviceId ,并把它作为LogFactory 。

完成桥接后,那么SLF4J 内部又是如何来装载合适的log 呢?

原理是SLF4J 会在编译时绑定import org.slf4j.impl.StaticLoggerBinder; 该类里面实现对具体日志方案的绑定接入。任何一种基于slf4j 的实现都要有一个这个类。如:

org.slf4j.slf4j-log4j12-1.5.6: 提供对 log4j 的一种适配实现。

Org.slf4j.slf4j-simple-1.5.6: 是一种 simple 实现,会将 log 直接打到控制台。

……

那么这个地方就要注意了:如果有任意两个实现slf4j 的包同时出现,那就有可能酿就悲剧,你可能会发现日志不见了、或都打到控制台了。原因是这两个jar 包里都有各自的org.slf4j.impl.StaticLoggerBinder ,编译时候绑定的是哪个是不确定的。这个地方要特别注意!!

Logback是由log4j创始人设计的又一个开源日志组件,性能进行了优化,log4j和logback的对比:

结果分析:

从测试结果可见:

如果不开启AsyncAppender,Log4j和Logback的性能不相上下;如查开启了AsyncAppender,Logback的性能将有非常大的提升。

关闭immediateFlush,在开启AsyncAppender的情况下,对Logback的影响很小,但对Log4J的影响却很大;但在不开启的AsyncAppender的情况下,对二都影响都很大。

原因是Logback对AsyncAppender做了很多优化,主要对并发处理方式的不同。二者都采用了生产者、消费者模型。

Log4j的实现原理如下:

Logging Event进入AsyncAppender,AsyncAppender会调用append方法,在append方法中会去把logging Event填入Buffer中,当消费能力不如生产能力时,AsyncAppender会把超出Buffer容量的Logging Event放到DiscardSummary中,作为消费速度一旦跟不上生成速度,中转buffer的溢出处理的一种方案。

AsyncAppender有个线程类Dispatcher,它是一个简单的线程类,实现了Runnable接口。它是AsyncAppender的后台线程。

Dispatcher所要做的工作是:

(1)锁定Buffer,让其他要对Buffer进行操作的线程阻塞。
(2)看Buffer的容量是否满了,如果满了就将Buffer中的Logging Event全部取出,并清空Buffer和DiscardSummary;如果没满则等待Buffer填满Logging Event,然后notify Disaptcher线程。

(3)将取出的所有Logging Event交给对应appender进行后面的日志信息推送。

于是Log4J用ArrayList实现了一个buffer队列(@see org.apache.log4j.AsyncAppender#append)来用户缓冲,并用一堆的synchronized来保证线程安全,对于AppenderAttachable则采用Vector来保证线程安全。

Logback的整体原理与Log4J基本相似,但直接使用了并发包的ArrayBlockingQueue来实现Buffer缓冲,对于AppenderAttachable并没采用加锁的机制来保证线程安全,而是通过读写分离来实现了线程安全,具体用的是CopyOnWriteArrayList。

综于以上原因,不难理解为什么异步下Logback比Log4J性能高出如此多。

结论:

考虑到Logback在多线程情况下的性能优秀表现,加上接口使用的方便性,强烈建议新应用接入Logback。老的应用如果日志没有成为系统的性能瓶颈,考虑到迁移成本较高,可以保留现状。

JMX简介

管 根据百度百科的定义,JMX是一个为应用程序、设备、系统等植入管理功能的框架。JMX可以跨越一系列异构操作系统平台、系统体系结构和网络传输协议,灵活的开发无缝集成的系统、网络和服务管理应用。通常使用JMX来监控系统的运行状态或管理系统的某些方面,比如清空缓存、重新加载配置文件等

JMX体系结构分为以下四个层次:设备层,代理层,分布服务层和附加管理协议API,目前从JDK1.5开始只提供了前2种实现
设备层又叫构件层,具体来说就是MBean,MBean是什么?就是对外暴露,需要被管理的JavaBean.
MBean又分标准MBean,动态MBean,开放MBean,以及模型MBean,其中动态MBean和开放MBean用的比较少,我们重点介绍标准MBean和模型MBean。

标准MBean,只要遵循MBean的规范的JavaBean就是标准MBean,具体是什么规范呢,就是首先定义一个以MBean结尾的XXXMBean接口,此接口内的所有方法都将可以被调用访问,然后定义类XXX实现XXXMBean接口,例子如下:

Count是需要被管理的类 

包含在MBean中方法都将是可以被管理的,MBean起名是有规范的,就是原类名后加上MBean字样

jmxtools.jar的下载地址为下载,运行 Main之前需要先启动rmiregister,命令行输入:rmiregistry 8888

另外需要添加JVM启动参数

-Dcom.sun.management.jmxremote=true
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.port=8880

第一个参数代表是否支持远程链接,第二个参数代表是否使用用户密码验证,如果为true,需要另外2个参数

-Dcom.sun.management.jmxremote.password.file=/home/wdw/jmx/jmxremote.password
-Dcom.sun.management.jmxremote.access.file=/home/wdw/jmx/jmxremote.access

其中jmxremote.password文件内容可以为

monitorRole <Your password>
controlRole <Your password>

jmxremote.access内容为

monitorRole readOnly
controlRole readWrite

ssl的参数是否启用ssl加密支持
然后可以启动jconsole,如果本地启动,可以看到本地进程,如果是远程连接,则可以输入地址URL:service:jmx:rmi:///jndi/rmi://localhost:8888/server
来启动远程管理,然后在JMX管理里可以看到。如果使用http管理,可以直接在浏览器里输入http://localhost:8080就可以了

动态MBean:运行时动态添加管理方法,我们上面的例子里,stop方法执行之后,就会发现count不能再累加了,我们希望stop执行之后,可以手动执行+1的操作,例子如下:

不做过多解释了,看代码应该能看得懂,动态MBean的代码稍微复杂点,但它可以动态组装MBean信息,所以有时候可以动态做一些事情。

模型MBean:经过上面几次折腾之后,你会发现一个问题,被管理的Bean需要实现一个接口,但是,有时候被管理的Bean我们可能无法拿到源代码,对它进行修改的,这时候,我们就需要使用模型MBean了。例子如下

 

Count不需要实现任何接口,比如不能修改源码的Service,我们增加一个Util类来组装RequiredModelMBean

具体代码和动态MBean差不多,不再细述,最后使用方式和之前差不多

 配置的MBean:模型MBean可以动态管理一个已有的JavaBean,但是写起来还是很麻烦,可以不可以采用配置的方式呢?答案是肯定的,配置的方式有2种,一个是采用apache的commons-modeler,另一个是结合Spring,与Spring集成

先来看commons-modeler怎么做

虽然不需要实现接口,但是JavaBean规范还是需要的,比如get set方法

我们使用 XML来描述信息:mbeans.xml

放在和Count同一个包路径下就行

这是基本的Modeler的使用了。

Spring集成:关于和Spring的集成,我只把spring的配置文件写下,由于容器启动需要配合,就不再执行了

结合Spring的配置,很简单是吧?感兴趣的同学可以在应用里自己试试

另外关于JMX知识的最后一点是Notification机制,简单来说,就是一个MBean属性或者方法被调用之后会通知关心这个事件的监听者,来达到消息通知的目的,具体细节,就先不写了,等后续有时间再补充吧。