Logback是Java中的老式日志记录框架。它的第一个版本从2006年开始,至今已经迭代了十多年。图片来自Pexels,不过Logback最新的稳定版还是2017年的,好几年没更新了;Logback的兄弟slf4j最新的稳定版也是2017年的,有点爽。而且Logback的异步性能实在捉襟见肘,功能简单,配置繁琐,远不如Apache的新一代日志框架:Log4j2。目前Log4j2为王,其他日志框架不是对手!Log4j2简介ApacheLog4j2是Log4j(1)的升级版,相对于它的祖先Log4j1.x有很大的改进,与Logback相比有很大的改进。除了内部设计的调整外,主要升级有以下几点:简化的配置更强大的参数格式化最夸张的异步性能在Log4j2中,分为API(log4j-api)和实现(log4j-core)**两个模块。API和slf4j是一个类型,属于loggingabstraction/facade。实现部分是Log4j2的核心:org.apache.logging.log4j?log4j-apiorg.apache.logging.log4j?log4j-core最强大的性能①最强的异步性能这个特性是Log4j2最强的地方。在java中log4j2目前的日志框架中,异步日志的性能是最高的,没有之一。再来看看几款日志框架的benchmark对比结果(log4j2官方测试结果):从图中可以看出,Log4j2的异步(全异步,非混合模式)性能远超Log4j1和Logback。.压力越大,吞吐量的差距就越大。64线程测试下,Log4j2的吞吐量达到了180w+/s,而Logback/Log4j1不到20w,相差近十倍。②ZeroGC(Garbage-free)从2.6(2016)版本开始,Log4j2默认运行在zeroGC模式。什么是零GC?这意味着不会有GC由于Log4j2。Log4j2中的各种Message对象、字符串数组、字节数组等都是多路复用的,不重复创建,大大减少了无用对象的创建,从而实现“零GC”。③支持更高性能的I/O编写Log4j还提供了MemoryMappedFileAppender,I/O部分使用MemoryMappedFile实现,可以获得极高的I/O性能。但是在使用MemoryMappedFileAppender之前,请确保您对MemoryMappedFile有足够的了解,否则不要轻易使用它。更强大的参数格式化API模块,提供了比slf4j更丰富的参数格式化功能。①使用{}占位符格式化参数在slf4j中,我们可以使用{}来实现“格式化”功能(参数会直接将占位符替换为toString)。像下面这样:logger.debug("Logginginuser{}withbirthday{}",user.getName(),user.getBirthdayCalendar());②String.format形式的格式化参数在Log4j2中,除了支持{}字符的参数占位符外,还支持String.format形式:publicstaticLoggerlogger=LogManager.getFormatterLogger("Foo");logger.debug("Logginginuser%swithbirthday%s",user.getName(),user.getBirthdayCalendar());logger.debug("登录用户%1$swithbirthday%2$tm%2$te,%2$tY",user.getName(),user.getBirthdayCalendar());logger.debug("Integer.MAX_VALUE=%,d",Integer.MAX_VALUE);logger.debug("Long.MAX_VALUE=%,d",Long.MAX_VALUE);注意:如果要使用String.format,需要使用LogManager.getFormatterLogger,而不是LogManager.getLogger。③使用logger.printf格式化参数在Log4j2的Logger接口中,还有一个printf方法,可以在不创建LogManager.getFormatterLogger的情况下以String.format的形式使用:logger.printf(Level.INFO,"Logginginuser%1$swithbirthday%2$tm%2$te,%2$tY",user.getName(),user.getBirthdayCalendar());logger.debug("Openingconnectionto{}...",someDataSource);④“惰性”日志记录(lazylogging)这个功能虽然小,但是非常好用。在一些业务流程中,为了留根或跟踪问题,需要将输入的参数完整打印出来。通常,输入参数用JSON/XML序列化,然后在debug级别打印:logger.debug("Inputparametermessage:{}",JSON.toJSONString(policyDTO));如果问题需要跟踪,系统会将日志级别调整为debug/trace,以便打印。但是这里有一个问题。debug虽然不会输出info级别的内容,但是序列化代码JSON.toJSONString()肯定会被执行,严重影响正常流程下的执行效率。我们期望的结果是,在info层面,连序列化都不做。这里可以通过isDebugEnable判断当前配置下是否可以输出debug级别:if(logger.isDebugEnabled()){logger.debug("Inputparametermessage:{}",JSON.toJSONString(policyDTO));}虽然这样可以避免不必要的序列化,但是到处这样写还是有点不爽,一行变成三行。Log4j2的logger对象提供了一系列的Lambda支持。通过这些接口,可以实现“惰性”日志记录:.paramSuppliers);voiderror(Stringmessage,Supplier>...paramSuppliers);//相当于下面先判断,然后打印logger.debug("Inputparametermessage:{}",()->JSON.toJSONString(policyDTO));if(logger.isDebugEnabled()){logger.debug("Inputmessage:{}",JSON.toJSONString(policyDTO));}这种Supplier+Lambda的形式相当于上面的方法先判断isDebugEnable再打印,三行代码变成一行。嗯,很好吃。简化配置Log4j2也支持四种形式的配置文件:XML/JSON/YML/Properties,但最主流和直观的方式是XML。下面看一下Logback和Log4j2的配置文件对比,在相同功能的配置下。logback.xml:
