Java日志系统确实比较丰富,常用的有log4j、JUL、logback等等,同时伴随着日志系统的发展,出现了日志框架commons-logging和slf4j。
简短地描述下日志发展,最先出现的是apache开源社区的log4j,这个日志确实是应用最广泛的日志工具,成为了java日志的事实上的标准。然而,当时Sun公司在jdk1.4中增加了JUL日志实现,企图对抗log4j,但是却造成了混乱,这个也是被人诟病的一点。当然也有其他日志工具的出现,这样必然造成开发者的混乱,因为这些日志系统互相没有关联,替换和统一也就变成了比较棘手的一件事。想象下你的应用使用log4j,然后使用了一个其他团队的库,他们使用了JUL,你的应用就得使用两个日志系统了,然后又有第二个库出现了,使用了simplelog。这个时候估计让你崩溃了,这是要闹哪样?这个状况交给你来想想办法,你该如何解决呢?进行抽象,抽象出一个接口层,对每个日志实现都适配或者转接,这样这些提供给别人的库都直接使用抽象层即可。不错,开源社区提供了commons-logging抽象,被称为JCL,也就是日志框架了,确实出色地完成了兼容主流的日志实现(log4j、JUL、simplelog),基本一统江湖,就连顶顶大名的spring也是依赖了JCL。看起来事物确实是美好,但是美好的日子不长,接下来另一个优秀的日志框架slf4j的加入导致了更加混乱的场面。比较巧的是slf4j的作者(Ceki Gülcü)就是log4j的作者,他觉得JCL不够优秀,所以他要自己搞一套更优雅的出来,于是slf4j日志体系诞生了,并为slf4j实现了一个亲子——logback,确实更加优雅,但是由于之前很多代码库已经使用JCL,虽然出现slf4j和JCL之间的桥接转换,但是集成的时候问题依然多多,对很多新手来说确实会很懊恼,因为比单独的log4j时代“复杂”多了,可以关注下这个,抱怨声确实很多。到此本来应该完了,但是Ceki Gülcü觉得还是得回头拯救下自己的“大阿哥”——log4j,于是log4j2诞生了,同样log4j2也参与到了slf4j日志体系中,想必将来会更加混乱。接下来详细解读日志系统的配合使用问题。
JCL的实现原理,使用JCL一般(如果是log4j可以不需要)需要一个配置commons-logging.properties在classpath上,这个文件有一行代码:
org.apache.commons.logging.LogFactory= org.apache.commons.logging.impl.LogFactoryImpl
这个是告诉JCL使用哪个日志实现,JCL会在classpath下去加载对应的日志工厂实现类,具体的日志工厂实现类可以是log4j实现,可以是jul实现等等。用户只需要依赖JCL的api即可,对日志系统的替换只需要修改下commons-logging.properties文件切换到对应的日志工厂实现即可。但是我们也可以看到因为JCL是运行时去加载classpath下的实现类,会有classloader问题。而且因为log4j尚不支持参数占位符打日志的方式,所以JCL也会更加无力。
slf4j的设计确实比较优雅,采用比较熟悉的方式——接口和实现分离,有个纯粹的接口层——slf4j-api工程,这个里边基本完全定义了日志的接口,所以对于开发来说,只需要使用这个即可。有接口就要有实现,比较推崇的实现是logback,logback完全实现了slf4j-api的接口,并且性能也比log4j更好,同时实现了变参占位符日志输出方式等等新特性。刚刚也提到log4j的使用比较普遍,所以支持这批用户依然是必须的,slf4j-log4j12也实现了slf4j-api,这个算是对log4j的适配器。同样推理,也会有对JUL的适配器slf4j-jdk14等等。为了使使用JCL等等其他日志系统后者实现的用户可以很简单地切换到slf4j上来,给出了各种桥接工程,比如:jcl-over-slf4j会把对JCL的调用都桥接到slf4j上来,可以看出jcl-over-slf4j的api和JCL是相同的,所以这两个jar是不能共存的。jul-to-slf4j是把对jul的调用桥接到slf4j上,log4j-over-slf4j是把对log4j的调用桥接到slf4j。下边用一个图来表示下这个家族的大致成员
如上图,最上层表示桥阶层,下层表示具体的实现层,中间是接口层,可以看出这个图中所有的jar都是围绕着slf4j-api活动的,其中slf4j-jul的jar名称是slf4j-jdk14。slf4j-api和具体的实现层是怎么绑定的呢?这个其实是在编译时绑定的,这个可能不好理解,最直接的表达方式是不需要想jcl那样配置一下,只需要把slf4j-api和slf4j-log4j放到classpath上,即实现绑定。原理可以下载slf4j-api的源码查看,这个设计还是很巧妙的,slf4j-api中会去调用StaticLoggerBinder这个类获取绑定的工厂类,而每个日志实现会在自己的jar中提供这样一个类,这样slf4j-api就实现了编译时绑定实现。但是这样接口的源码编译需要依赖具体的实现了,不太合理吧?当时我也有这样的迷惑,因为打开slf4j-api的jar,看不到StaticLoggerBinder,就查看了slf4j-api的源码,在源码中看到了StaticLoggerBinder这个类,猜想应该是slf4j-api在打包过程中有动作,删除了自己保重的那个类,结果不出所料,确实是pom中的ant-task给处理了,pom中处理方式如下:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-antrun-plugin</artifactId> <executions> <execution> <phase>process-classes</phase> <goals> <goal>run</goal> </goals> </execution> </executions> <configuration> <tasks> <echo>Removing slf4j-api's dummy StaticLoggerBinder and StaticMarkerBinder</echo> <delete dir="target/classes/org/slf4j/impl"/> </tasks> </configuration> </plugin>
打出来的slf4j-api是不完整的,只有找到包含StaticLoggerBinder这个类的包才可以,于是slf4j-log4j和logback-classic都提供了这个类。另外,slf4j-log4j和logback以及slf4j-jdk14是不能同时和slf4j共存的,也就是说只能有一个实现存在,不然启动会提示有多个绑定,判断多个实现的代码也很简单,如下:
private static String STATIC_LOGGER_BINDER_PATH = "org/slf4j/impl/StaticLoggerBinder.class"; private static void singleImplementationSanityCheck() { try { ClassLoader loggerFactoryClassLoader = LoggerFactory.class .getClassLoader(); Enumeration paths; if (loggerFactoryClassLoader == null) { paths = ClassLoader.getSystemResources(STATIC_LOGGER_BINDER_PATH); } else { paths = loggerFactoryClassLoader .getResources(STATIC_LOGGER_BINDER_PATH); } // use Set instead of list in order to deal with bug #138 // LinkedHashSet appropriate here because it preserves insertion order during iteration Set implementationSet = new LinkedHashSet(); while (paths.hasMoreElements()) { URL path = (URL) paths.nextElement(); implementationSet.add(path); } if (implementationSet.size() > 1) { Util.report("Class path contains multiple SLF4J bindings."); Iterator iterator = implementationSet.iterator(); while(iterator.hasNext()) { URL path = (URL) iterator.next(); Util.report("Found binding in [" + path + "]"); } Util.report("See " + MULTIPLE_BINDINGS_URL + " for an explanation."); } } catch (IOException ioe) { Util.report("Error getting resources from path", ioe); } }
同时这个图中桥阶层和对应的实现jar是不能共存的,比如log4j-over-slf4j和slf4j-log4j,jul-to-slf4j和slf4j-jdk14,这个很好理解,会有死循环,启动也会报错。这种想象也就是说jar之前有互斥性,怎么使用maven有效解决“全局排除”会在以后的博文中讲解。jcl-over-slf4j是把对jcl的调用桥接到slf4j上,前文说到它和jcl是互斥的。图中的红线就表示互斥关系。
当然slf4j也提供了可以把对slf4j的调用桥接到JCL上的工程包——slf4j-jcl,可以看出slf4j的设计者考虑非常周到,想想这样的情况:遗留系统使用的是JCL+log4j,因为系统功能演进,依赖了其他业务线的库,恰好那个库依赖了slf4j-api,并且应用需要关心这个库的日志,那么就需要转接日志到JCL上即可。细心的你可能一经发现,slf4j-jcl和jcl-over-slf4j也是互斥的,太多互斥的了:(。
对于log4j2的加入,也很简单,和logback是很相似的,如下图:
红线依然表示依赖的互斥,当然log4j-slf4j-impl也会和logback-classic、slf4j-log4j、slf4j-jdk14互斥。
常见的问题:
1.slf4j-api和实现版本不对应,尤其是1.6.x和1.5.x不兼容,如果没有特殊需求,直接升级到最新版本。
2.slf4j的多个实现同时存在,比如slf4j-log4j和logback-classic,排除其中一个即可。
3.log4j和logback不能同时使用?可以同时使用,这两个并不矛盾,遗留系统可能直接使用了log4j的代码,并且不能通过log4j-over-slf4j桥接,那么可以让他继续使用log4j,这里有详细的介绍。
4.该如何选用这些呢?建议在非特殊情况下,都使用slf4j-api+logback,不要直接使用日志实现,性能没什么影响。对于要提供给别人的类库,建议使用slf4j-api,使用方可以自由选择具体的实现,并且建议类库不要依赖具体的日志实现。对于自己的桌面小应用,可以直接使用log4j,毕竟只是随便做做。
5.logback因为木有spring提供的启动listener,所以要自己写?可以看看这里,开源社区已经做好了。
6.日志系统一般不会影响到系统性能,除非你的系统对性能非常苛刻,如果这样你可以考虑使用Blitz4j,这个是Netflix社区对log4j的性能改进版,不过他们依然建议去使用log4j或者logback。
后续继续补充.....
相关推荐
总的来说,这个“java日志文件过滤”项目提供了一个便捷的工具,帮助开发者快速处理和分析日志数据,提高了问题排查和系统监控的效率。通过学习和理解这个项目,你可以深化对Java日志处理、文件I/O和条件过滤等技术...
efk7.13搜集java日志—filebeat配置详解笔记总结
3. **Java日志框架** - Log4j:经典日志框架,提供了灵活的日志配置和多种Appender(如文件、数据库等)。 - Logback:由Log4j作者设计的新一代日志框架,性能优于Log4j。 - SLF4J(Simple Logging Facade for ...
Java日志系统软件源代码是Java开发者常用的工具之一,它用于记录应用程序运行过程中的各种信息,包括错误、警告、调试信息等。对于理解和优化程序,以及排查问题具有至关重要的作用。下面将详细介绍Java日志系统的...
Java日志框架详解.ppt
总的来说,这个Java日志分析工具提供了对日志数据进行处理和分析的功能,通过阅读源代码,开发者可以学习到Java日志处理的最佳实践,以及如何构建此类工具。同时,对于需要排查问题或监控系统运行状态的用户,这个...
Java作为广泛使用的编程语言,提供了强大的正则表达式支持,使得我们能够有效地解析和处理日志文件。本篇将围绕“使用Java正则表达式分析处理日志”这一主题,探讨如何利用Java的正则表达式功能来提取、过滤和操作...
java_java日志详解.pdf
首先,**JAVA日志解析**是整个项目的基础。Java作为一种广泛使用的编程语言,具有丰富的库和工具支持日志处理。通常,我们会使用如`log4j`、`logback`这样的日志框架来生成日志,而解析这些日志文件则需要自定义的...
根据提供的文件信息,我们可以深入探讨Java日志系统的关键概念与技术细节。日志在软件开发过程中扮演着极其重要的角色,它不仅有助于问题诊断,还能帮助我们监控应用的行为和性能。 ### Java日志系统概述 Java日志...
Java日志框架是Java开发中不可或缺的部分,它们用于记录应用程序的运行信息,帮助开发者追踪错误、调试代码以及进行性能分析。Log4j和Logback是其中最常用的两个框架,它们都由Ceki Gülcü创建,并且在设计上有着...
在Java编程中,日志记录是一项非常重要的任务,它帮助开发者追踪程序运行状态,调试问题,以及记录系统事件。在Swing应用中,我们同样可以使用Java内置的`java.util.logging`包来输出错误日志。这篇文档主要讨论了...
在Java开发中,实时日志解析处理是一项至关重要的任务,特别是在大规模分布式系统或者高并发环境中。日志是系统运行状态的记录,对于故障排查、性能优化、安全监控等都有着不可替代的作用。本篇将深入探讨Java如何...
java_java日志详解[汇编].pdf
本文将深入探讨“LinuxLog.rar”压缩包中涉及到的“ConnectBean.JAVA”与Linux日志分析系统的相关知识。 首先,让我们理解"ConnectBean.JAVA"的角色。在Java编程中,"Bean"通常指的是符合JavaBeans规范的类,它是一...
Java发送Syslog日志是一种常见的系统日志管理方式,尤其在分布式系统中,它能帮助开发者收集、分析和处理来自不同节点的日志信息。Syslog协议是一个标准的日志消息传递协议,广泛应用于网络设备、操作系统和其他软件...
《Java日志终极指南》是一篇详细解析Java日志系统的文章,旨在帮助Java开发者更好地理解和使用日志功能。文章主要涵盖了Java日志的基础知识、核心组件、日志框架的选择以及配置方法。 首先,Java日志系统采用自定义...
在Java编程中,性能优化是至关重要的,尤其是在大型系统中,每一行代码的效率都可能影响到整体的系统性能。描述中提到的日志代码优化是一个典型的例子,它使用了条件语句来避免不必要的日志输出,减少计算开销。`if ...
《使用Java开发Windows日志获取程序的深度解析》 在信息技术领域,日志记录是系统监控和故障排查的重要环节。Windows操作系统提供了丰富的日志信息,包括Security、Application等多种类型,这些日志对于开发者和...