當(dāng)前位置:首頁 > 公眾號(hào)精選 > 架構(gòu)師社區(qū)
[導(dǎo)讀]來源:https://juejin.cn/post/6945753017878577165Logback算是JAVA里一個(gè)老牌的日志框架,從06年開始第一個(gè)版本,迭代至今也十幾年了。不過logback最近一個(gè)穩(wěn)定版本還停留在2017年,好幾年都沒有更新;logback的兄弟slf...



Logback 算是JAVA 里一個(gè)老牌的日志框架,從06年開始第一個(gè)版本,迭代至今也十幾年了。不過logback最近一個(gè)穩(wěn)定版本還停留在 2017 年,好幾年都沒有更新;logback的兄弟 slf4j 最近一個(gè)穩(wěn)定版也是2017年,有點(diǎn)涼涼的意思。


而且 logback的異步性能實(shí)在拉跨,功能簡陋,配置又繁瑣,遠(yuǎn)不及Apache 的新一代日志框架 - Log4j


目前來看,Log4j2 就是王者,其他日志框架都不是對手


Log4j2簡介

Apache Log4j 2是 Log4j(1) 的升級(jí)版,比它的祖先 Log4j 1. x 有了很大的改進(jìn),和logback對比有很大的改進(jìn)。除了內(nèi)部設(shè)計(jì)的調(diào)整外,主要有以下幾點(diǎn)的大升級(jí):


  • 更簡化的配置
  • 更強(qiáng)大的參數(shù)格式化
  • 最夸張的異步性能
Log4j 2中,分為 API(log4j-api)和實(shí)現(xiàn)(log4j-core) 兩個(gè)模塊。API 和slf4j 是一個(gè)類型,屬于日志抽象/門面,而實(shí)現(xiàn)部分,才是Log4j 2的核心。


  • org.apache.logging.log4j ? log4j-api
  • org.apache.logging.log4j ? log4j-core

最牛逼的性能

最強(qiáng)的異步性能

這個(gè)特性,算是Log4j2最強(qiáng)之處了。log4j2 在目前JAVA中的日志框架里,異步日志的性能是最高的,沒有之一。


先來看一下,幾種日志框架benchmark對比結(jié)果(log4j2官方測試結(jié)果):


最牛逼?Java?日志框架?—?Log4j2,性能無敵,橫掃對手.....從圖上可以看出,log4j2的異步(全異步,非混合模式)下的性能,遠(yuǎn)超log4j1和logback,簡直吊打。壓力越大的情況下,吞吐上的差距就越大。在64線程測試下,log4j2的吞吐達(dá)到了180w /s,而logback/log4j1只有不到20w,相差近十倍


零GC(Garbage-free)

從2.6版本開始(2016年),log4j2 默認(rèn)就以零GC模式運(yùn)行了。什么叫零GC呢?就是不會(huì)由于log4j2而導(dǎo)致GC。


log4j2 中各種Message對象,字符串?dāng)?shù)組,字節(jié)數(shù)組等全部復(fù)用,不重復(fù)創(chuàng)建,大大減少了無用對象的創(chuàng)建,從而做到“零GC”。


更高性能 I/O 寫入的支持

log4j 還提供了一個(gè)MemoryMappedFileAppender,I/O 部分使用MemoryMappedFile來實(shí)現(xiàn),可以得到極高的I/O性能。不過在使用MemoryMappedFileAppender之前,得確定你足夠了解MemoryMappedFile的相關(guān)知識(shí),否則不要輕易使用呦。


更強(qiáng)大的參數(shù)格式化

API模塊和slf4j相比,提供了更豐富的參數(shù)格式化功能。


使用{}占位符格式化參數(shù)


在slf4j里,我們可以用{}的方式來實(shí)現(xiàn)“format”的功能(參數(shù)會(huì)直接toString替換占位符),像下面這樣:


logger.debug("Logging in user {} with birthday {}", user.getName(), user.getBirthdayCalendar());

使用String.format的形式格式化參數(shù)

log4j2 中除了支持{}的參數(shù)占位符,還支持String.format的形式:


public static Logger logger = LogManager.getFormatterLogger("Foo");

logger.debug("Logging in user %s with birthday %s", user.getName(), user.getBirthdayCalendar());
logger.debug("Logging in user %1$s with birthday %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格式化參數(shù)

log4j2 的 Logger接口中,還有一個(gè)printf方法,無需創(chuàng)建LogManager.getFormatterLogger,就可以使用String.format的形式


logger.printf(Level.INFO, "Logging in user %1$s with birthday %2$tm %2$te,%2$tY", user.getName(), user.getBirthdayCalendar());

logger.debug("Opening connection to {}...", someDataSource);

“惰性”打日志(lazy logging)

這個(gè)功能雖然小,但非常實(shí)用。


在某些業(yè)務(wù)流程里,為了留根或追溯問題,需要完整的打印入?yún)?,一般是把入?yún)⒔o用JSON/XML序列化后用debug級(jí)別打印:


logger.debug("入?yún)?bào)文:{}",JSON.toJSONString(policyDTO));
如果需要追溯問題時(shí),會(huì)將系統(tǒng)的日志級(jí)別調(diào)到debug/trace,這樣就可以打印。但是這里有個(gè)問題,雖然在info級(jí)別下debug不會(huì)輸出內(nèi)容,但JSON.toJSONString()這個(gè)序列化的代碼一定會(huì)執(zhí)行,嚴(yán)重影響正常流程下的執(zhí)行效率。


我們期望的結(jié)果是info級(jí)別下,連序列化都不執(zhí)行。這里可以通過isDebugEnable來判斷當(dāng)前配置下debug級(jí)別是否可以輸出:


if(logger.isDebugEnabled()){
logger.debug("入?yún)?bào)文:{}",JSON.toJSONString(policyDTO));
}
這樣雖然可以避免不必要的序列化,但每個(gè)地方都這么寫還是有點(diǎn)難受的,一行變成了三行。


log4j2 的 logger 對象,提供了一系列l(wèi)ambda的支持,通過這些接口可以實(shí)現(xiàn)“惰性”打日志:


void debug(String message, Supplier... paramSuppliers);
void info(String message, Supplier... paramSuppliers);
void trace(String message, Supplier... paramSuppliers);
void error(String message, Supplier... paramSuppliers);

//等同于下面的先判斷,后打印
logger.debug("入?yún)?bào)文:{}",() -> JSON.toJSONString(policyDTO));

if(logger.isDebugEnabled()){
logger.debug("入?yún)?bào)文:{}",JSON.toJSONString(policyDTO));
}
這種 Supplier Lambda 的形式,等同于上面的先判斷 isDebugEnable 然后打印,三行的代碼變成了一行。嗯,真香。


更簡化的配置

Log4j 2 同時(shí)支持XML/JSON/YML/Properties 四種形式的配置文件,不過最主流的還是XML的方式,最直觀。(搜索公眾號(hào)Java知音,回復(fù)“2021”,送你一份Java面試題寶典)


來看一下logback和log4j2的配置文件對比,同樣功能的配置下:


logback.xml



<configuration>
<appender name = "File" class= "ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/app.log
<maxFileSize>1 GB
<Configuration xmlns:xi="http://www.w3.org/2001/XInclude"
status="warn" name="XInclude">

<Appenders>
<RollingFile name="File" fileName="logs/app.log" filePattern="logs/archives/app-%d{yyyy-MM-dd}-%i.log.gz">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} %5p [%t] %-40.40c{1.} : %m%n"/>
<Policies>
<TimeBasedTriggeringPolicy />

<SizeBasedTriggeringPolicy size="1 GB"/>

<appender name = "File" class= "ch.qos.logback.core.rolling.RollingFileAppender">

與其他日志抽象/門面適配

log4j2 由于拆分為 API 和 實(shí)現(xiàn)兩部分,所以可能也需要和其他日志框架進(jìn)行適配


最牛逼?Java?日志框架?—?Log4j2,性能無敵,橫掃對手.....

其他的特點(diǎn)

  • 異步隊(duì)列使用高性能隊(duì)列 - LMAX Disruptor
  • Appender豐富,有JMS/JPA/KAFKA/Http/MONGODB/CouchDB/Socket/Script等各種Appender的支持
  • 支持自定義日志級(jí)別 ……

基本用法

終于介紹完了Log4j2的強(qiáng)大,現(xiàn)在來介紹下Log4j2的基本使用。


引用log4j2的maven依賴

log4j-api在log4j-core中已經(jīng)有依賴了,直接依賴core即可


<dependency>
<groupId>org.apache.logging.log4j
<Configuration xmlns:xi="http://www.w3.org/2001/XInclude"
status="warn" name="XInclude">

<Properties>
<Property name="PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %5p [%t] %-40.40c{1.} : %m%n"/>

<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="${PATTERN}"/>

<RollingFile name="File" fileName="logs/app.log" filePattern="logs/archives/app-%d{yyyy-MM-dd}-%i.log.gz">
<PatternLayout pattern="${PATTERN}"/>
<Policies>

<TimeBasedTriggeringPolicy />

<SizeBasedTriggeringPolicy size="1 GB"/>


<Logger name="your logger/package name" level="debug" additivity="false"/>

<Root level="INFO">


<AppenderRef ref="Console"/>
<AppenderRef ref="File"/>
;
<Configuration>
<Properties>
<Property name="name1">value
本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時(shí)聯(lián)系本站刪除。
關(guān)閉
關(guān)閉