一袱衷、日志簡介
1.1 日志是什么(WHAT)
日志:記錄程序的運行軌跡,方便查找關(guān)鍵信息笑窜,也方便快速定位解決問題致燥。
通常,Java程序員在開發(fā)項目時都是依賴Eclipse/IDEA等集成開發(fā)工具的Debug 調(diào)試功能來跟蹤解決Bug排截,但項目發(fā)布到了測試篡悟、生產(chǎn)環(huán)境怎么辦?你有可能會說可以使用遠程調(diào)試匾寝,但實際并不能允許讓你這么做搬葬。
所以,日志的作用就是在測試艳悔、生產(chǎn)環(huán)境沒有 Debug 調(diào)試工具時開發(fā)和測試人員定位問題的手段急凰。日志打得好,就能根據(jù)日志的軌跡快速定位并解決線上問題猜年,反之抡锈,日志輸出不好,不僅無法輔助定位問題反而可能會影響到程序的運行性能和穩(wěn)定性乔外。
很多介紹 AOP 的地方都采用日志來作為介紹床三,實際上日志要采用切面的話是極其不科學(xué)的!對于日志來說杨幼,只是在方法開始撇簿、結(jié)束、異常時輸出一些什么差购,那是絕對不夠的四瘫,這樣的日志對于日志分析沒有任何意義。如果在方法的開始和結(jié)束整個日志欲逃,那方法中呢找蜜?如果方法中沒有日志的話,那就完全失去了日志的意義稳析!如果應(yīng)用出現(xiàn)問題要查找由什么原因造成的洗做,也沒有什么作用弓叛。這樣的日志還不如不用!
1.2 日志有什么用(WHY)
不管是使用何種編程語言诚纸,日志輸出幾乎無處不再邪码。總結(jié)起來咬清,日志大致有以下幾種用途:
- 問題追蹤:輔助排查和定位線上問題闭专,優(yōu)化程序運行性能。
- 狀態(tài)監(jiān)控:通過日志分析旧烧,可以監(jiān)控系統(tǒng)的運行狀態(tài)影钉。
- 安全審計:審計主要體現(xiàn)在安全上,可以發(fā)現(xiàn)非授權(quán)的操作掘剪。
1.3 總結(jié)
日志在應(yīng)用程序中是非常非常重要的平委,好的日志信息能有助于我們在程序出現(xiàn) BUG 時能快速進行定位,并能找出其中的原因夺谁。
作為一個有修養(yǎng)的程序猿廉赔,對日志這個東西應(yīng)當引起足夠的重視。
二匾鸥、日志框架(HOW)
2.1 常用的日志框架
log4j蜡塌、Logging、commons-logging勿负、slf4j馏艾、logback,開發(fā)的同學(xué)對這幾個日志相關(guān)的技術(shù)不陌生吧奴愉,為什么有這么多日志技術(shù)琅摩,它們都是什么區(qū)別和聯(lián)系呢?且看下文分解:
2.1.1 Logging
這是 Java 自帶的日志工具類锭硼,在 JDK 1.5 開始就已經(jīng)有了房资,在 java.util.logging 包下。通常情況下檀头,這個基本沒什么人用了轰异,了解一下就行。
2.1.2 commons-logging
commons-logging 是日志的門面接口鳖擒,它也是Apache 最早提供的日志門面接口溉浙,用戶可以根據(jù)喜好選擇不同的日志實現(xiàn)框架,而不必改動日志定義蒋荚,這就是日志門面的好處,符合面對接口抽象編程」萑洌現(xiàn)在已經(jīng)不太流行了期升,了解一下就行惊奇。
2.1.3 Slf4j
slf4j,英文全稱為“Simple Logging Facade for Java”,為java提供的簡單日志Facade播赁。Facade門面颂郎,更底層一點說就是接口。它允許用戶以自己的喜好容为,在工程中通過slf4j接入不同的日志系統(tǒng)乓序。
因此slf4j入口就是眾多接口的集合,它不負責(zé)具體的日志實現(xiàn)坎背,只在編譯時負責(zé)尋找合適的日志系統(tǒng)進行綁定替劈。具體有哪些接口,全部都定義在slf4j-api中得滤。查看slf4j-api源碼就可以發(fā)現(xiàn)陨献,里面除了public final class LoggerFactory類之外,都是接口定義懂更。因此slf4j-api本質(zhì)就是一個接口定義眨业。
2.1.4 Log4j
Log4j 是 Apache 的一個開源日志框架,也是市場占有率最多的一個框架沮协。
注意:log4j 在 2015.08.05 這一天被 Apache 宣布停止維護了龄捡,用戶需要切換到 Log4j2上面去。
下面是官宣原文:
On August 5, 2015 the Logging Services Project Management Committee announced that Log4j 1.x had reached end of life. For complete text of the announcement please see the Apache Blog. Users of Log4j 1 are recommended to upgrade to Apache Log4j 2.
2.1.5 Log4j2
Log4j 2 Apache Log4j 2是apache開發(fā)的一款Log4j的升級產(chǎn)品慷暂。
Log4j2與Log4j1發(fā)生了很大的變化墅茉,log4j2不兼容log4j1。
2.1.6 Logback
Logback 是 Slf4j 的原生實現(xiàn)框架呜呐,同樣也是出自 Log4j 一個人之手就斤,但擁有比 log4j 更多的優(yōu)點、特性和更做強的性能蘑辑,現(xiàn)在基本都用來代替 log4j 成為主流洋机。
Logback相對于log4j擁有更快的執(zhí)行速度⊙蠡辏基于我們先前在log4j上的工作绷旗,logback 重寫了內(nèi)部的實現(xiàn),在某些特定的場景上面副砍,甚至可以比之前的速度快上10倍衔肢。在保證logback的組件更加快速的同時,同時所需的內(nèi)存更加少豁翎。
2.2 日志框架怎么選
選項太多了的后果就是選擇困難癥角骤,我的看法是沒有最好的,只有最合適的:
- commons-loggin、slf4j 只是一種日志抽象門面邦尊,不是具體的日志框架背桐。
log4j、logback 是具體的日志實現(xiàn)框架蝉揍。 - 在比較關(guān)注性能的地方链峭,選擇Logback或自己實現(xiàn)高性能Logging API可能更合適。推薦:
slf4j + logback
. - 在已經(jīng)使用了Log4j的項目中又沾,如果沒有發(fā)現(xiàn)問題弊仪,繼續(xù)使用可能是更合適的方式:推薦組合為:
slf4j + log4j2
. - 如果不想有依賴則使用java.util.logging或框架容器已經(jīng)提供的日志接口。
三杖刷、記錄日志的時機
在看線上日志的時候励饵,我們可曾陷入到日志泥潭?該出現(xiàn)的日志沒有挺勿,無用的日志一大堆曲横,或者需要的信息分散在各個角落,特別是遇到緊急的在線bug時不瓶,有效的日志被大量無意義的日志信息淹沒禾嫉,焦急且無奈地浪費大量精力查詢?nèi)罩尽D鞘裁词怯涗浫罩镜暮线m時機呢蚊丐?
總結(jié)幾個需要寫日志的點:
編程語言提示異常:如今各類主流的編程語言都包括異常機制熙参,業(yè)務(wù)相關(guān)的流行框架有完整的異常模塊麦备。這類捕獲的異常是系統(tǒng)告知開發(fā)人員需要加以關(guān)注的,是質(zhì)量非常高的報錯。應(yīng)當適當記錄日志滞磺,根據(jù)實際結(jié)合業(yè)務(wù)的情況使用warn或者error級別。
業(yè)務(wù)流程預(yù)期不符:除開平臺以及編程語言異常之外目派,項目代碼中結(jié)果與期望不符時也是日志場景之一徒河,簡單來說所有流程分支都可以加入考慮代兵。取決于開發(fā)人員判斷能否容忍情形發(fā)生。常見的合適場景包括外部參數(shù)不正確妈倔,數(shù)據(jù)處理問題導(dǎo)致返回碼不在合理范圍內(nèi)等等。
系統(tǒng)核心角色启涯,組件關(guān)鍵動作:系統(tǒng)中核心角色觸發(fā)的業(yè)務(wù)動作是需要多加關(guān)注的贬堵,是衡量系統(tǒng)正常運行的重要指標,建議記錄INFO級別日志结洼,比如電商系統(tǒng)用戶從登錄到下單的整個流程黎做;微服務(wù)各服務(wù)節(jié)點交互;核心數(shù)據(jù)表增刪改松忍;核心組件運行等等蒸殿,如果日志頻度高或者打印量特別大,可以提煉關(guān)鍵點INFO記錄,其余酌情考慮DEBUG級別宏所。
系統(tǒng)初始化:系統(tǒng)或者服務(wù)的啟動參數(shù)酥艳。核心模塊或者組件初始化過程中往往依賴一些關(guān)鍵配置,根據(jù)參數(shù)不同會提供不一樣的服務(wù)爬骤。務(wù)必在這里記錄INFO日志充石,打印出參數(shù)以及啟動完成態(tài)服務(wù)表述。
四霞玄、日志打印最佳實踐
4.1 日志變量定義
日志變量往往不變骤铃,最好定義成final static,變量名用大寫坷剧。
private static final Logger log = LoggerFactory.getLogger({SimpleClassName}.getClass());
通常一個類只有一個 log
對象惰爬,如果有父類可以將 log
定義在父類中。
日志變量類型定義為門面接口(如 slf4j 的 Logger)惫企,實現(xiàn)類可以是 Log4j撕瞧、Logback 等日志實現(xiàn)框架,不要把實現(xiàn)類定義為變量類型狞尔,否則日志切換不方便丛版,也不符合抽象編程思想。
另外沪么,推薦引入lombok的依賴硼婿,在類的頭部加上@Slf4j
的注解,之后便可以在程序的任意位置使用log
變量打印日志信息了禽车,使用起來更加簡潔一點寇漫,在重構(gòu)代碼尤其是修改類名的時候無需改動原有代碼。
4.2 參數(shù)占位格式
使用參數(shù)化形式{}占位殉摔,[]進行參數(shù)隔離
log.debug("Save order with order no:[{}], and order amount:[{}]");
log.debug("Save order with order no:[{}], and order amount:[{}]");
這種可讀性好州胳,這樣一看就知道[]里面是輸出的動態(tài)參數(shù),{}用來占位類似綁定變量逸月,而且只有真正準備打印的時候才會處理參數(shù)栓撞,方便定位問題。
如果日志框架不支持參數(shù)化形式碗硬,且日志輸出時不支持該日志級別時會導(dǎo)致對象冗余創(chuàng)建瓤湘,浪費內(nèi)存,此時就需要使用 isXXEnabled 判斷恩尾,如:
if(log.isDebugEnabled()){
// 如果日志不支持參數(shù)化形式弛说,debug又沒開啟,那字符串拼接就是無用的代碼拼接翰意,影響系統(tǒng)性能
log.debug("Save order with order no:" + orderNo + ", and order amount:" + orderAmount);
}
至少 debug 級別是需要開啟判斷的木人,線上日志級別至少應(yīng)該是 info 以上的信柿。
這里推薦大家用 SLF4J 的門面接口,可以用參數(shù)化形式輸出日志醒第,debug 級別也不必用 if 判斷渔嚷,簡化代碼。
4.3 日志的基本格式
日志輸出主要在文件中稠曼,應(yīng)包括以下內(nèi)容:
- 日志時間
- 日志級別主要使用
- 調(diào)用鏈標識(可選)
- 線程名稱
- 日志記錄器名稱
- 日志內(nèi)容
- 異常堆棧(不一定有)
11:44:44.827 WARN [93ef3E0120160803114444] [main] [ClassPathXmlApplicationContext] Exception encountered during context initialization - cancelling refresh attempt
4.3.1 日志時間
作為日志產(chǎn)生的日期和時間形病,這個數(shù)據(jù)非常重要啃奴,一般精確到毫秒。由于線上一般配置為按天滾動日志文件檬姥,日期標識在文件名上岩遗,所以可以不放在這個時間中,使用 HH:mm:ss.SSS
格式即可碉输。非要加上也未嘗不可,格式推薦:yyyy-MM-dd HH:mm:ss.SSS
。
4.3.2 日志級別
日志的輸出都是分級別的抵赢,不同的設(shè)置不同的場合打印不同的日志。下面拿最普遍用的 Log4j 日志框架來做個日志級別的說明唧取,這個也比較齊全铅鲤,其他的日志框架也都大同小異。
主要使用如下的四個級別:
- DEBUG:DEUBG 級別的主要輸出調(diào)試性質(zhì)的內(nèi)容枫弟,該級別日志主要用于在開發(fā)邢享、測試階段輸出。該級別的日志應(yīng)盡可能地詳盡淡诗,開發(fā)人員可以將各類詳細信息記錄到DEBUG里骇塘,起到調(diào)試的作用,包括參數(shù)信息韩容,調(diào)試細節(jié)信息款违,返回值信息等等,便于在開發(fā)群凶、測試階段出現(xiàn)問題或者異常時插爹,對其進行分析。
- INFO:INFO日志主要記錄系統(tǒng)關(guān)鍵信息请梢,旨在保留系統(tǒng)正常工作期間關(guān)鍵運行指標赠尾,開發(fā)人員可以將初始化系統(tǒng)配置、業(yè)務(wù)狀態(tài)變化信息毅弧,或者用戶業(yè)務(wù)流程中的核心處理記錄到INFO日志中气嫁,方便日常運維工作以及錯誤回溯時上下文場景復(fù)現(xiàn)。建議在項目完成后形真,在測試環(huán)境將日志級別調(diào)成 INFO杉编,然后通過 INFO 級別的信息看看是否能了解這個應(yīng)用的運用情況超全,如果出現(xiàn)問題后是否這些日志能否提供有用的排查問題的信息。
- WARN:WARN 級別的主要輸出警告性質(zhì)的內(nèi)容邓馒,這些內(nèi)容是可以預(yù)知且是有規(guī)劃的嘶朱,比如,某個方法入?yún)榭栈蛘咴搮?shù)的值不滿足運行該方法的條件時光酣。在 WARN 級別的時應(yīng)輸出較為詳盡的信息疏遏,以便于事后對日志進行分析
- ERROR:ERROR 級別主要針對于一些不可預(yù)知的信息,諸如:錯誤救军、異常等财异,比如,在 catch 塊中抓獲的網(wǎng)絡(luò)通信唱遭、數(shù)據(jù)庫連接等異常戳寸,若異常對系統(tǒng)的整個流程影響不大,可以使用 WARN 級別日志輸出拷泽。在輸出 ERROR 級別的日志時疫鹊,盡量多地輸出方法入?yún)?shù)、方法執(zhí)行過程中產(chǎn)生的對象等數(shù)據(jù)司致,在帶有錯誤拆吆、異常對象的數(shù)據(jù)時,需要將該對象一并輸出
4.3.2.1 INFO和DEBUG的選擇
DEBUG級別比INFO低脂矫,包含調(diào)試時更詳細的了解系統(tǒng)運行狀態(tài)的東西枣耀,比如變量的值等等,都可以輸出到DEBUG日志里庭再。 INFO是在線日志默認的輸出級別捞奕,反饋系統(tǒng)的當前狀態(tài)給最終用戶看的。輸出的信息佩微,應(yīng)該對最終用戶具有實際意義的缝彬。從功能角度上說,Info輸出的信息可以看作是軟件產(chǎn)品的一部分哺眯,所以需要謹慎對待谷浅,不可隨便輸出。嘗試記錄INFO日志時不妨在頭腦中模擬線上運行奶卓,如果這條日志會被頻繁打印或者大部分時間對于糾錯起不到作用一疯,就應(yīng)當考慮下調(diào)為DEBUG級別。
- 由于info及debug日志打印量遠大于ERROR夺姑,出于前文日志性能的考慮墩邀,如果代碼為核心代碼,執(zhí)行頻率非常高盏浙,務(wù)必推敲日志設(shè)計是否合理眉睹,是否需要下調(diào)為DEBUG級別日志荔茬。
- 注意日志的可讀性,不妨在寫完代碼review這條日志是否通順竹海,能否提供真正有意義的信息慕蔚。
- 日志輸出是多線程公用的,如果有另外一個線程正在輸出日志斋配,上面的記錄就會被打斷孔飒,最終顯示輸出和預(yù)想的就會不一致。
4.3.2.2 WARN,ERROR的選擇
當方法或者功能處理過程中產(chǎn)生不符合預(yù)期結(jié)果或者有框架報錯時可以考慮使用艰争,常見問題處理方法包括:
- 增加判斷處理邏輯坏瞄,嘗試本地解決:增加邏輯判斷吞掉報警永遠是最優(yōu)選擇。
- 拋出異常甩卓,交給上層邏輯解決
- 記錄日志鸠匀,報警提醒
- 使用返回碼包裝錯誤做返回
一般來說,WARN級別不會短信報警猛频,ERROR級別則會短信報警甚至電話報警狮崩,ERROR級別的日志意味著系統(tǒng)中發(fā)生了非常嚴重的問題,必須有人馬上處理鹿寻,比如數(shù)據(jù)庫不可用,系統(tǒng)的關(guān)鍵業(yè)務(wù)流程走不下去等等诽凌。錯誤的使用反而帶來嚴重的后果毡熏,不區(qū)分問題的重要程度,只要有問題就error記錄下來侣诵,其實這樣是非常不負責(zé)任的痢法,因為對于成熟的系統(tǒng),都會有一套完整的報錯機制杜顺,那這個錯誤信息什么時候需要發(fā)出來财搁,很多都是依據(jù)單位時間內(nèi)ERROR日志的數(shù)量來確定的。因此如果我們不分輕重緩急躬络,一律ERROR對待尖奔,就會徒增報錯的頻率,久而久之穷当,我們的救火隊員對錯誤警報就不會那么在意提茁,這個警報也就失去了原始的意義。
WARN代表可恢復(fù)的異常馁菜,此次失敗不影響下次業(yè)務(wù)的執(zhí)行茴扁,開發(fā)人員會苦惱某些場景下幾次失敗可容忍,頻率高的時候需要提醒汪疮,記錄ERROR的結(jié)果是線上時不時出現(xiàn)容忍范圍內(nèi)的報警峭火,這時報警是無意義的毁习。但反之不記錄ERROR日志,真正出現(xiàn)問題則不會有實時報警卖丸,錯過最佳處理時機蜓洪。
強調(diào)ERROR報警
- ERROR級別的日志打印通常伴隨報警通知。ERROR的報出應(yīng)該伴隨著業(yè)務(wù)功能受損坯苹,即上面提到的系統(tǒng)中發(fā)生了非常嚴重的問題隆檀,必須有人馬上處理。
ERROR日志目標
- 給處理者直接準確的信息:error信息形成自身閉環(huán)粹湃。
問題定位:
- 發(fā)生了什么問題恐仑,哪些功能受到影響
- 獲取幫助信息:直接幫助信息或幫助信息的存儲位置
- 通過報警知道解決方案或者找何人解決
日志模板
log.error(“[接口名或操作名] [Some Error Msg] happens. [Probably Because]. [Probably need to do] [params] .”);
log.error(“[接口名或操作名] [Some Error Msg] happens. [Probably Because]. [please contact xxx@xxx] [params] .”);
4.3.3 調(diào)用鏈標識
在分布式應(yīng)用中,用戶的一個請求會調(diào)用若干個服務(wù)完成为鳄,這些服務(wù)可能還是嵌套調(diào)用的裳仆,因此完成一個請求的日志并不在一個應(yīng)用的日志文件,而是分散在不同服務(wù)器上不同應(yīng)用節(jié)點的日志文件中孤钦。該標識是為了串聯(lián)一個請求在整個系統(tǒng)中的調(diào)用日志歧斟。
調(diào)用鏈標識格式:
- 唯一字符串(trace ID)
- 調(diào)用層級(span ID)
調(diào)用鏈標識作為可選項,無該數(shù)據(jù)時只輸出 [] 即可偏形。
4.3.4 線程名稱
輸出該日志的線程名稱静袖,一般在一個應(yīng)用中一個同步請求由同一線程完成,輸出線程名稱可以在各個請求產(chǎn)生的日志中進行分類俊扭,便于分清當前請求上下文的日志队橙。
4.3.5 日志記錄器名稱
日志記錄器名稱一般使用類名,日志文件中可以輸出簡單的類名即可萨惑,看實際情況是否需要使用包名和行號等信息捐康。主要用于看到日志后到哪個類中去找這個日志輸出,便于定位問題所在庸蔼。
4.3.6 日志內(nèi)容
- 禁用 System.out.println和System.err.println
- 變參替換日志拼接
- 輸出日志的對象解总,應(yīng)在其類中實現(xiàn)快速的 toString 方法,以便于在日志輸出時僅輸出這個對象類名和 hashCode
- 預(yù)防空指針:不要在日志中調(diào)用對象的方法獲取值姐仅,除非確保該對象肯定不為 null花枫,否則很有可能會因為日志的問題而導(dǎo)致應(yīng)用產(chǎn)生空指針異常。
// 不推薦
log.debug( "Load student(id={}), name: {}" , id , student.getName() );
// 推薦
log.debug( "Load student(id={}), student: {}" , id , student );
對于一些一定需要進行拼接字符串萍嬉,或者需要耗費時間乌昔、浪費內(nèi)存才能產(chǎn)生的日志內(nèi)容作為日志輸出時,應(yīng)使用 log.isXxxxxEnable() 進行判斷后再進行拼接處理壤追,比如:
if (log.isDebugEnable()) {
StringBuilder builder = new StringBuilder();
for (Student student : students) {
builder.append("student: ").append(student);
}
builder.append("value: ").append(JSON.toJSONString(object));
log.debug( "debug log example, detail: {}" , builder );
}
4.3.7 異常堆棧
異常堆棧一般會出現(xiàn)在 ERROR 或者 WARN 級別的日志中磕道,異常堆棧含有方法調(diào)用鏈的系統(tǒng),以及異常產(chǎn)生的根源行冰。異常堆棧的日志屬于上一行日志的溺蕉,在日志收集時需要將其劃至上一行中伶丐。
4.4 日志文件
日志文件放置于固定的目錄中,按照一定的模板進行命名疯特,推薦的日志文件名稱:
當前正在寫入的日志文件名:<應(yīng)用名>[-<功能名>].log
已經(jīng)滾入歷史的日志文件名:<應(yīng)用名>[-<功能名>].log.<yyyy-MM-dd>
4.5 日志配置
根據(jù)不同的環(huán)境配置不同的日志輸出方式:
- 本地調(diào)試可以將日志輸出到控制臺上
- 測試環(huán)境或者生產(chǎn)環(huán)境輸出到文件中哗魂,每天產(chǎn)生一個文件,如果日志量龐大可以每個小時產(chǎn)生一個日志文件
- 生產(chǎn)環(huán)境中的文件輸出漓雅,可以考慮使用異步文件輸出录别,該種方式日志并不會馬上刷新到文件中去,會產(chǎn)生日志延時邻吞,在停止應(yīng)用時可能會導(dǎo)致一些還在內(nèi)存中的日志未能及時刷新到文件中去而產(chǎn)生丟失组题,如果對于應(yīng)用的要求并不是非常高的話,可暫不考慮異步日志
logback 日志工具可以在日志文件滾動后將前一文件進行壓縮抱冷,以減少磁盤空間占用崔列,若使用 logback 對于日志量龐大的應(yīng)用建議開啟該功能。
具體的配置示例旺遮,由于篇幅較長赵讯,單獨開一篇介紹。詳情可移步:日志使用項目實戰(zhàn)
4.6 日志使用規(guī)范
- 在一個對象中通常只使用一個Logger對象耿眉,Logger應(yīng)該是static final的边翼,只有在少數(shù)需要在構(gòu)造函數(shù)中傳遞logger的情況下才使用private final。
private static final Logger log = LoggerFactory.getLogger(Main.class);
- 不要使用具體的日志實現(xiàn)類
InterfaceImpl interface = new InterfaceImpl();
這段代碼大家都看得懂吧跷敬?應(yīng)該面向接口的對象編程讯私,而不是面向?qū)崿F(xiàn),這也是軟件設(shè)計模式的原則西傀,正確的做法應(yīng)該是。
Interface interface = new InterfaceImpl();
日志框架里面也是如此桶癣,上面也說了拥褂,日志有門面接口,有具體實現(xiàn)的實現(xiàn)框架牙寞,所以大家不要面向?qū)崿F(xiàn)編程饺鹃。
- 輸出Exceptions的全部Throwable信息。因為
log.error(msg)
和log.error(msg,e.getMessage())
這樣的日志輸出方法會丟失掉最重要的StackTrace信息间雀。
void foo(){
try{
//do somehing
}catch(Exception e){
log.error(e.getMessage());//錯誤示范
log.erroe("Bad Things",e.getMessage());//錯誤示范
log.error("Bad Things",e);//正確演示
}
}
- 不允許記錄日志后又拋出異常悔详。如捕獲異常后又拋出了自定義業(yè)務(wù)異常,此時無需記錄錯誤日志惹挟,由最終捕獲方進行異常處理茄螃。不能又拋出異常,又打印錯誤日志连锯,不然會造成重復(fù)輸出日志归苍。
void foo() throws LogException{
try{
//do somehing
}catch(Exception e){
log.error("Bad Things",e);//正確
throw new LogException("Bad Things",e);
}
}
- 不允許使用標準輸出
包括System.out.println()
和System.error.println()
語句用狱。因為這個只會打印到控制臺,而不會記錄到日志文件中拼弃,不方便管理日志夏伊。此外,標準輸出不會顯示類名和行號信息吻氧,一旦代碼中大量出現(xiàn)標準輸出的代碼溺忧,且日志中打印有標準輸出的內(nèi)容,很難定位日志內(nèi)容和日志打印的位置盯孙,根本無法排查問題鲁森,想刪除無用日志輸出也改不動,這個是筆者在重構(gòu)古董代碼的時候親自踩過的一個坑镀梭。
void foo(){
try{
//do somehing
}catch(Exception e){
Syste.out.println(e.getMessage());//錯誤
System.error.println(e.getMessage());//錯誤
log.error("Bad Things",e);//正確
}
}
- 不允許出現(xiàn)printStackTrace
void foo(){
try{
//do somehing
}catch(Exception e){
e.printStacktrace();//錯誤
log.error("Bad Things",e);//正確
}
}
來看一下它的源碼:
public void printStackTrace() {
printStackTrace(System.err);
}
它其實也是利用 System.err
輸出到了Tomcat控制臺刀森。
- 禁止在線上環(huán)境開啟debug級別日志輸出
出于日志性能的考慮,如果代碼為核心代碼报账,執(zhí)行頻率非常高研底,則輸出日志建議增加判斷,尤其是低級別的輸出<debug透罢、info榜晦、warn>。
一是因為項目本身 debug 日志太多羽圃,二是各種框架中也大量使用 debug 的日志乾胶,線上開啟 debug 不久就會打滿磁盤,影響業(yè)務(wù)系統(tǒng)的正常運行朽寞。
- 不要在大循環(huán)中打印日志
如果你的框架使用了性能不高的 Log4j 框架识窿,那就不要在上千個 for 循環(huán)中打印日志,這樣可能會拖垮你的應(yīng)用程序脑融,如果你的程序響應(yīng)時間變慢喻频,那要考慮是不是日志打印的過多了。
for(int i=0; i<2000; i++){
log.info("XX");
}
最好的辦法是在循環(huán)中記錄要點肘迎,在循環(huán)外面總結(jié)打印出來甥温。
- 打印有意義的日志
通常情況下在程序日志里記錄一些比較有意義的狀態(tài)數(shù)據(jù):程序啟動,退出的時間點妓布;程序運行消耗時間姻蚓;耗時程序的執(zhí)行進度;重要變量的狀態(tài)變化匣沼。