為什么阿里巴巴禁止工程師直接使用日志系統(tǒng)(Log4j、Logback)中的 API
作為Java程序員,我想很多人都知道日志對(duì)于一個(gè)程序的重要性,尤其是Web應(yīng)用。很多時(shí)候,日志可能是我們了解應(yīng)用程序如何執(zhí)行的唯一方式。
所以,日志在Java Web應(yīng)用中至關(guān)重要,但是,很多人卻以為日志輸出只是一件簡(jiǎn)單的事情,所以會(huì)經(jīng)常忽略和日志相關(guān)的問(wèn)題。
在接下來(lái)的幾篇文章中,我會(huì)來(lái)介紹介紹這個(gè)容易被大家忽視,但同時(shí)也容易導(dǎo)致故障的知識(shí)點(diǎn)。
Java語(yǔ)言之所以強(qiáng)大,就是因?yàn)樗艹墒斓纳鷳B(tài)體系。包括日志這一功能,就有很多成熟的開(kāi)源框架可以被直接使用。
首先,我們先來(lái)看一下目前有哪些框架被廣泛的使用。
常用日志框架
j.u.l
j.u.l是java.util.logging包的簡(jiǎn)稱(chēng),是JDK在1.4版本中引入的Java原生日志框架。Java Logging API提供了七個(gè)日志級(jí)別用來(lái)控制輸出。這七個(gè)級(jí)別分別是:SEVERE、WARNING、INFO、CONFIG、FINE、FINER、FINEST。
Log4j
Log4j是Apache的一個(gè)開(kāi)源項(xiàng)目,通過(guò)使用Log4j,我們可以控制日志信息輸送的目的地是控制臺(tái)、文件、GUI組件,甚至是套接口服務(wù)器、NT的事件記錄器、UNIX Syslog守護(hù)進(jìn)程等;我們也可以控制每一條日志的輸出格式;
通過(guò)定義每一條日志信息的級(jí)別,我們能夠更加細(xì)致地控制日志的生成過(guò)程。Log4也有七種日志級(jí)別:OFF、FATAL、ERROR、WARN、INFO、DEBUG和TRACE。
最令人感興趣的就是,這些可以通過(guò)一個(gè)配置文件來(lái)靈活地進(jìn)行配置,而不需要修改應(yīng)用的代碼。
LogBack
LogBack也是一個(gè)很成熟的日志框架,其實(shí)LogBack和Log4j出自一個(gè)人之手,這個(gè)人就是Ceki Gülcü。
logback當(dāng)前分成三個(gè)模塊:logback-core,logback- classic和logback-access。
logback-core是其它兩個(gè)模塊的基礎(chǔ)模塊。
logback-classic是Log4j的一個(gè)改良版本。此外logback-classic完整實(shí)現(xiàn)SLF4J API使你可以很方便地更換成其它日記系統(tǒng)如Log4j或j.u.l。
logback-access訪(fǎng)問(wèn)模塊與Servlet容器集成提供通過(guò)Http來(lái)訪(fǎng)問(wèn)日記的功能。
Log4j2
前面介紹過(guò)Log4j,這里要單獨(dú)介紹一下Log4j2,之所以要單獨(dú)拿出來(lái)說(shuō),而沒(méi)有和Log4j放在一起介紹,是因?yàn)樽髡哒J(rèn)為,Log4j2已經(jīng)不僅僅是Log4j的一個(gè)升級(jí)版本了,而是從頭到尾被重寫(xiě)的,這可以認(rèn)為這其實(shí)就是完全不同的兩個(gè)框架。
關(guān)于Log4j2解決了Log4j的哪些問(wèn)題,Log4j2相比較于Log4j、j.u.l和logback有哪些優(yōu)勢(shì),我們?cè)诤罄m(xù)的文章中介紹。
禁止直接使用Log框架API?
前面介紹了四種日志框架,也就是說(shuō),我們想要在應(yīng)用中打印日志的時(shí)候,可以使用以上四種類(lèi)庫(kù)中的任意一種。比如想要使用Log4j,那么只要依賴(lài)Log4j的jar包,配置好配置文件并且在代碼中使用其API打印日志就可以了。
不知道有多少人看過(guò)《阿里巴巴Java開(kāi)發(fā)手冊(cè)》,其中有一條規(guī)范做了『強(qiáng)制』要求:
說(shuō)好了以上四種常用的日志框架是給Java應(yīng)用提供的方便進(jìn)行記錄日志的,那為什么又不讓在應(yīng)用中直接使用其API呢?這里面推崇使用的SLF4J是什么呢?所謂的門(mén)面模式又是什么東西呢?
什么是日志門(mén)面
日志門(mén)面,是門(mén)面模式的一個(gè)典型的應(yīng)用。
門(mén)面模式(Facade Pattern),也稱(chēng)之為外觀模式,其核心為:外部與一個(gè)子系統(tǒng)的通信必須通過(guò)一個(gè)統(tǒng)一的外觀對(duì)象進(jìn)行,使得子系統(tǒng)更易于使用。
就像前面介紹的幾種日志框架一樣,每一種日志框架都有自己?jiǎn)为?dú)的API,要使用對(duì)應(yīng)的框架就要使用其對(duì)應(yīng)的API,這就大大的增加應(yīng)用程序代碼對(duì)于日志框架的耦合性。
為了解決這個(gè)問(wèn)題,就是在日志框架和應(yīng)用程序之間架設(shè)一個(gè)溝通的橋梁,對(duì)于應(yīng)用程序來(lái)說(shuō),無(wú)論底層的日志框架如何變,都不需要有任何感知。只要門(mén)面服務(wù)做的足夠好,隨意換另外一個(gè)日志框架,應(yīng)用程序不需要修改任意一行代碼,就可以直接上線(xiàn)。
在軟件開(kāi)發(fā)領(lǐng)域有這樣一句話(huà):計(jì)算機(jī)科學(xué)領(lǐng)域的任何問(wèn)題都可以通過(guò)增加一個(gè)間接的中間層來(lái)解決。而門(mén)面模式就是對(duì)于這句話(huà)的典型實(shí)踐。
為什么需要日志門(mén)面
前面提到過(guò)一個(gè)重要的原因,就是為了在應(yīng)用中屏蔽掉底層日志框架的具體實(shí)現(xiàn)。這樣的話(huà),即使有一天要更換代碼的日志框架,只需要修改jar包,最多再改改日志輸出相關(guān)的配置文件就可以了。這就是解除了應(yīng)用和日志框架之間的耦合。
有人或許會(huì)問(wèn)了,如果我換了日志框架了,應(yīng)用是不需要改了,那日志門(mén)面不還是需要改的嗎?
要回答這個(gè)問(wèn)題,我們先來(lái)舉一個(gè)例子,再把門(mén)面模式揉碎了重新解釋一遍。
日志門(mén)面就像飯店的服務(wù)員,而日志框架就像是后廚的廚師。對(duì)于顧客這個(gè)應(yīng)用來(lái)說(shuō),我到飯店點(diǎn)菜,我只需要告訴服務(wù)員我要一盤(pán)番茄炒蛋即可,我不關(guān)心后廚的所有事情。因?yàn)殡m然主廚從把這道菜稱(chēng)之為『番茄炒蛋』A廚師換成了把這道菜稱(chēng)之為『西紅柿炒雞蛋』的B廚師。但是,顧客不需要關(guān)心,他只要下達(dá)『番茄炒蛋』的命令給到服務(wù)員,由服務(wù)員再去翻譯給廚師就可以了。
所以,對(duì)于一個(gè)了解了”番茄炒蛋的多種叫法”的服務(wù)員來(lái)說(shuō),無(wú)論后廚如何換廚師,他都能準(zhǔn)確的幫用戶(hù)下單。
同理,對(duì)于一個(gè)設(shè)計(jì)的全面、完善的日志門(mén)面來(lái)說(shuō),他也應(yīng)該是天然就兼容了多種日志框架的。所以,底層框架的更換,日志門(mén)面幾乎不需要改動(dòng)。
以上,就是日志門(mén)面的一個(gè)比較重要的好處——解耦。
常用日志門(mén)面
介紹過(guò)了日志門(mén)面的概念和好處之后,我們看看Java生態(tài)體系中有哪些好的日志門(mén)面的實(shí)現(xiàn)可供選擇。
SLF4J
Java簡(jiǎn)易日志門(mén)面(Simple Logging Facade for Java,縮寫(xiě)SLF4J),是一套包裝Logging 框架的界面程式,以外觀模式實(shí)現(xiàn)。可以在軟件部署的時(shí)候決定要使用的 Logging 框架,目前主要支援的有Java Logging API、Log4j及l(fā)ogback等框架。以MIT 授權(quán)方式發(fā)布。
SLF4J 的作者就是 Log4j和Logback 的作者 Ceki Gülcü,他宣稱(chēng) SLF4J 比 Log4j 更有效率,而且比 Apache Commons Logging (JCL) 簡(jiǎn)單、穩(wěn)定。
其實(shí),SLF4J其實(shí)只是一個(gè)門(mén)面服務(wù)而已,他并不是真正的日志框架,真正的日志的輸出相關(guān)的實(shí)現(xiàn)還是要依賴(lài)Log4j、logback等日志框架的。
由于SLF4J比較常用,這里多用一些篇幅,再來(lái)簡(jiǎn)單分析一下SLF4J,主要和Log4J做一下對(duì)比。相比較于Log4J的API,SLF4J有以下幾點(diǎn)優(yōu)勢(shì):
- Log4j 提供 TRACE, DEBUG, INFO, WARN, ERROR 及 FATAL 六種紀(jì)錄等級(jí),但是 SLF4J 認(rèn)為 ERROR 與 FATAL 并沒(méi)有實(shí)質(zhì)上的差別,所以拿掉了 FATAL 等級(jí),只剩下其他五種。
- 大部分人在程序里面會(huì)去寫(xiě)logger.error(exception),其實(shí)這個(gè)時(shí)候Log4j會(huì)去把這個(gè)exception tostring。真正的寫(xiě)法應(yīng)該是logger(message.exception);而SLF4J就不會(huì)使得程序員犯這個(gè)錯(cuò)誤。
- Log4j間接的在鼓勵(lì)程序員使用string相加的寫(xiě)法(這種寫(xiě)法是有性能問(wèn)題的),而SLF4J就不會(huì)有這個(gè)問(wèn)題 ,你可以使用logger.error(“{} is+serviceid”,serviceid);
- 使用SLF4J可以方便的使用其提供的各種集體的實(shí)現(xiàn)的jar。(類(lèi)似commons-logger)
- 從commons–logger和Log4j merge非常方便,SLF4J也提供了一個(gè)swing的tools來(lái)幫助大家完成這個(gè)merge。
- SLF4J 只支持 MDC,不支持 NDC。
- 提供字串內(nèi)容替換的功能,會(huì)比較有效率,說(shuō)明如下:
- // 傳統(tǒng)的字符串產(chǎn)生方式,如果沒(méi)有要記錄Debug等級(jí)的信息,就會(huì)浪費(fèi)時(shí)間在產(chǎn)生不必要的信息上
- logger.debug("There are now " + count + " user accounts: " + userAccountList);
- // 為了避免上述問(wèn)題,我們可以先檢查是不是開(kāi)啟了Debug信息記錄功能,只是程序的編碼會(huì)比較復(fù)雜
- if (logger.isDebugEnabled()) {
- logger.debug("There are now " + count + " user accounts: " + userAccountList);
- }
- // 如果Debug等級(jí)沒(méi)有開(kāi)啟,則不會(huì)產(chǎn)生不必要的字符串,同時(shí)也能保持程序編碼的簡(jiǎn)潔
- logger.debug("There are now {} user accounts: {}", count, userAccountList);
commons-logging
Apache Commons Logging是一個(gè)基于Java的日志記錄實(shí)用程序,是用于日志記錄和其他工具包的編程模型。它通過(guò)其他一些工具提供API,日志實(shí)現(xiàn)和包裝器實(shí)現(xiàn)。
commons-logging和SLF4J的功能是類(lèi)似的,主要是用來(lái)做日志 門(mén)面的。提供更加好友的API工具。
總結(jié)
在Java生態(tài)體系中,圍繞著日志,有很多成熟的解決方案。關(guān)于日志輸出,主要有兩類(lèi)工具。
一類(lèi)是日志框架,主要用來(lái)進(jìn)行日志的輸出的,比如輸出到哪個(gè)文件,日志格式如何等。 另外一類(lèi)是日志門(mén)面,主要一套通用的API,用來(lái)屏蔽各個(gè)日志框架之間的差異的。
所以,對(duì)于Java工程師來(lái)說(shuō),關(guān)于日志工具的使用,***實(shí)踐就是在應(yīng)用中使用如Log4j + SLF4J 這樣的組合來(lái)進(jìn)行日志輸出。
這樣做的***好處,就是業(yè)務(wù)層的開(kāi)發(fā)不需要關(guān)心底層日志框架的實(shí)現(xiàn)及細(xì)節(jié),在編碼的時(shí)候也不需要考慮日后更換框架所帶來(lái)的成本。這也是門(mén)面模式所帶來(lái)的好處。
綜上,請(qǐng)不要在你的Java代碼中出現(xiàn)任何Log4j等日志框架的API的使用,而是應(yīng)該直接使用SLF4J這種日志門(mén)面。
【本文是51CTO專(zhuān)欄作者Hollis的原創(chuàng)文章,作者微信公眾號(hào)Hollis(ID:hollischuang)】