Java主流日志工具介绍和使用
概述
本文的目的并不是详细介绍日志使用,而在于对现有主流日志系统的一个大致介绍,其目的是让我们更加合理的去使用日志,管理日志依赖关系。因为在开发过程中,我发现应用下面关于log的jar包非常的混乱,而这种混乱常常会带来jar包冲突、多份日志输出等问题,造成不必要的麻烦。比如你应用采用了log4j作为日志实现,但是你又通过间接依赖的方式引入了logback的包,这样开发者往往很难察觉,往往是出现了相应的异常现象才排查出log冲突的问题。
本文会先介绍现在主流日志框架,然后提出一个一些应用日志的规范,如果可行度很高,后续会给出更为严格的检测机制,帮助开发者去发现问题,防范于未然。
日志框架的历史
在Java开发过程中可用的日志工具非常之多,比如:
- Apache Commons Logging(Jakarta Commons Logging,JCL)
- Simple Logging Facade for Java (SLF4J)
- Apache Log4j(Log4j2)
- Java Logging API(JUL)
- Logback
- tinylog
在这些日志组件当中,最早得到广泛应用的是log4j,成为了Java日志的事实上的标准,现在可以看到很多应用都是依赖于log4j的日志实现。然而当时Sun公司在jdk1.4中增加了JUL(java.util.logging),企图对抗log4j,于是造成了混乱,当然此时也有其它的一些日志框架的出现,如simplelog等,简直是乱上加乱。
为了解决这种混乱Commons Logging出现了,他只提供日志的接口,而具体的实现则在运行过程中动态寻找。这样在代码中全部使用Commons Logging的编程接口,而具体日志实现则在外部配置中体现。
这样还有一个好处,由于应用日志并不依赖具体的实现,那么应用日志的实现则可以轻松的切换。所以现在也能看到很多应用基于Commons Logging+Log4j的搭配。
但是呢log4j的作者觉得Commons Loggin不够优秀(缺点在下文中介绍),于是自己实现了一套更为优雅的,这个就是SLF4J,并且还亲自实现了一个日志实现logback。那么现在关于log的局面就更为混乱了。为了让之前使用Commons Logging和JUL的能够很好的转到SLF4J的体系中来,log4j的作者又对其他的日志工具做了桥接......后来该作者又重写了log4j,这就是现在的log4j2,同时log4j2也加进了SLF4J体系中......(ps:这人确实牛逼!)
主流日志工具介绍
Commons-logging
Commons-logging是Apache提供的一个日志抽象,他提供一组通用的日志接口。应用自由选择第三方日志实现,像JUL、log4j等。这样的好处是代码依赖日志抽象接口,并不是具体的日志实现,这样在更换第三方库时带来了很大便利。
工作原理:
1、查找名为org.apache.commons.logging.Log的factory属性配置(可以是java代码配置,也可以是commons-logging.properties配置);
2、查找名为org.apache.commons.logging.Log的系统属性;
3、上述配置不存在则classpath下是否有Log4j日志系统,如有则使用相应的包装类;
3、如果系统运行在JDK 1.4系统上,则使用Jdk14Logger;
4、上述都没有则使用SimpleLog。
所以如果使用commons-logging+log4j的组合只需要在classpath中加入log4j.xml配置即可。commons-logging的动态查找过程是在程序运行时自动完成的。他使用ClassLoader来寻找和载入底层日志库,所以像OSGI这样的框架无法正常工作,因为OSGI的不同插件使用自己的ClassLoader。
SLF4J(Simple logging facade for Java)
SLF4J类似于commons-logging,他也是日志抽象。和commons-logging动态查找不同slf4j是静态绑定,他是在编译时就绑定真正的log实现。同时slf4j还提供桥接器可以将基于commons-loggging、jul的日志重定向到slf4j。比如程序中以前使用的commong-logging,那么你可以通过倒入jcl-over-slf4j包来讲日志重定向到slf4j。
SLF4J与各种日志实现的使用
SLF4J桥接
Log4j
log4j是Apache的开源日志框架,其最新版本是在2012年5月更新的1.2.17版本。他的2.0版本log4j2在其基础之上进行了重写,最新版本为2016年5月发布的2.6版本。log4j2插件式的架构、强大的配置功能、锁的优化、java8支持等特性都是非常吸引人的。如何升级请参考文档
Logback
Logback是由log4j创始人设计的又一个开源日志组件。当前分成三个模块:logback-core,logback- classic和logback-access。logback-core是其它两个模块的基础模块。logback-classic是log4j的一个改良版本,此外logback-classic完整实现SLF4J API。logback-access访问模块与Servlet容器集成提供通过Http来访问日志的功能。Logback是要与SLF4J结合起来用。
最佳实现
二方库使用
二房库中建议不要绑定任何的日志实现,统一使用日志抽象(commons-logging、slf4j)。这里强烈建议使用slf4j。
包依赖如下
<!-- 除此之外不要依赖别的log包 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.21</version> </dependency>
应用使用
新应用
slf4j+logback
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.21</version> </dependency> <!-- logback-classic包含logback-core依赖 --> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.1.7</version> </dependency>
slf4j+log4j
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.21</version> </dependency> <!--slf4j-log4j12包含了log4j依赖 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.21</version> </dependency>
老应用
老应用则没有改变日志的必要,因为会有开发成本。但是开发需要保证三点:
1、应用依赖中同一个log包不能出现多个版本;
2、日志实现框架必须唯一,可以log4j、logback等,但是不能出现既有log4j又有logback的情况;
3、日志桥接不要出现循环重定向,比如你加入了jcl-over-slf4j.jar之后又加入了slf4j-jcl.jar。
满足上述两点我感觉关于日志的问题应该会少很多。
检查机制
很多时候对于间接依赖开发者也不知晓应用存在多个日志实现包。所以采用有效的依赖检查机制很有必要。
想法
通过maven插件的形式,完成编译期依赖检查,通过开发者配置日志实现,检查应用中对日志的依赖。如发现多种日志实现包依赖则报错,这样来提醒开发者解决问题。
参考
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Java线程安全小结
一、引言 Java开发过程中许多的时候都会涉及到各种各样的并发编程的问题,然而说起并发编程总需要格外的关注线程安全的问题。最近呢一直在基于Jstorm开发日志处理程序,由于Jstorm的特性,多线程随处可见。所以程序中也需要特别关注线程安全的问题。这次项目开发过程也遇到了不少的问题,通过不断的查询资料,不断的修改问题也确实收获了不少的知识。因此写一下最近关于并发编程的学习和总结。 二、多线程基础 在并发编程中,线程和锁起着至关重要的作用,要完成健壮的并发程序就必须要正确的使用线程和锁。在我的实战过程中我觉得有两点需要我们特别的注意。 对可变共享资源的操作 当前的锁对象 这两个地方不考虑清楚很容易写出线程不安全的代码。后面我会结合相关的示例来说明这一点。 术语 共享资源:能够被多个线程同时访问的资源 竞态条件:当两个线程竞争同一资源时,如果对资源的访问顺序敏感,就称存在竞态条件 临界区:导致竞态条件发生的代码区 什么是线程安全? 在并发编程中我们最为关心的便是线程安全的问题。只有线程安全的程序在并发编程中才有用武之地。 那么,到底什么是线程安全呢? 当多个线程访问某个类时,这个类始终都能...
- 下一篇
面试 5:手写 Java 的 pow() 实现
我们在处理一道编程面试题的时候,通常除了注意代码规范以外,千万要记得自己心中模拟一个单元测试。主要通过三方面来处理。 功能性测试 边界值测试 负面性测试 不管如何,一定要保证自己代码考虑的全面,而不要简单地猜想用户的输入一定是正确的,只是去实现功能。通常你编写一个能接受住考验的代码,会让面试官对你刮目相看,你可以不厉害,但已经充分说明了你的靠谱。 今天我们的面试题目是: 面试题:尝试实现 Java 的 Math.pow(double base,int exponent) 函数算法,计算 base 的 exponent 次方,不得使用库函数,同时不需要考虑大数问题。 面试题来源于《剑指 Offer》第 11 题,数字的整数次方。 不要介意 Java 真正的方法是 Math.pow(double var1,double var2)。 由于不需要考虑大数问题,不少小伙伴心中暗自窃喜,这题目也太简单了,给我撞上了,运气真好,于是直接写出下面的代码: public class Test11 { private static double power(double base, int expone...
相关文章
文章评论
共有0条评论来说两句吧...