# 六角鼠年鐵人賽 Week 12 - Spring Boot - 一次搞懂 LOG 設定 ==大家好,我是 "為了拿到金角獎盃而努力著" 的文毅青年 - Kai== ## 史詩名句 荷馬 :::info 決定問題,需要智慧,貫徹執行時則需要耐心 ::: ## 主題 今天來跟大家分享處理 LOG 這部分,尤其介紹 SpringBoot 有哪些方式或套件可以使用的~ 在上一篇文章中使用 Lombok 有短暫出現關於 LOG 的處理,但實際上還是有很多眉眉角角要設定,才能幫助各位開發者們在 Log 的處理上更順心~ ## What is Log? Why we need Log? 讓我們先來認識 Log,Log 是一種類似寫日記或寫工作日誌的動作,只是我們把這動作交給了系統本身去執行,讓程式在執行特定功能時,會自動寫下 "預設" 應該留下的訊息,可能是日期、執行過程、錯誤、進度條或是回報等等。 :::info 透過這些訊息,開發者們可以得到下述的這些幫助: 1. 開發時,檢視單元測試狀況 2. 開發後,檢視完整流程狀況 3. 產品上線後,供日後發生的問題作追溯 4. ~~撇清責任~~ 釐清責任 ::: 工程師們請不要鐵齒保證系統沒問題,請相信世界上沒有完美的程式。現在留下的Log,是為了日後的Bug布局 。 :::warning 來看看如果沒有 Log,你可能會遇到什麼狀況? 1. 系統出包,追 Bug 得在 Local 從頭錯一次,假設 IDE 也沒有顯示錯誤,只能憑經驗驗錯 2. 修正 Bug 時間拖長,無法在短時間處理完畢,產品在短時間內流失顧客的風險提高 3. 權責無法釐清,無法及時確定問題是出自於系統 or 網路 or 資料庫 4. 權責無法釐清,產品與顧客之間的角力更容易失去立場 ::: 綜觀上述後會發現到,留下 Log 本身是小事,但後續卻像蝴蝶效應一樣,要馬產生莫大效應;要馬產生莫大風險。 工程師們出外工作也要明哲保身,千萬記得什麼模塊都可以不用加,但是 Log 的部分一定要做好處理。 ## Log Level 在簡單介紹完 Log 後,來介紹紀錄 Log 的時候,一般的分級處理,畢竟 - 有些 Log 是用於測試,不需要跟產品一起上線給客戶看; - 有些地方僅是沒有資料的問題,並非影響系統的大錯; - 有些錯誤可以在紀錄後,繼續維持系統運作,不讓其停止; - 有些錯誤會讓系統運作停止,必須加以防範 有這麼多種不同程度的問題,當然在 Log 的處理上也會有分級的方式去幫助工程師們選定適當的級別進行設定,且不同的分級可以幫助產品在不同階段時,顯示各階段需要印出的 Log 級別,不須再為了測試與上線的不同又從頭把 Log 設定作調整。 此外,不同的 Log 套件又會有不同的分級方式,開發者可以選定自己熟悉的套件,或是 Kai 後續推薦的套件,再對自己的系統進行級別的詳細設置即可。 舉例: java.util.logging 階級: |Log 分級|說明|應用| |---|---|---| |SEVERE|嚴重訊息|設置在可能會導致系統無法運作的系統模組中,不論是**測試時期**還是**LIVE**,都要保留的訊息| |WARNING|警示訊息|設置在雖然有問題,但不影響系統運作的功能模組中,同 SEVERE 在任何時期都建議保留| |INFO|一般訊息|通常用作紀錄執行流程,測試時期一定要寫,確保每個動作都OK,LIVE 是建議保留最低限度的流程訊息即可,避免紀錄文件太快吃滿空間| |CONFIG|設定方面的訊息|通常用於紀錄連網、DB連線,或是註冊功能套件等動作時,建議任何時期都要保留,此類訊息不單給開發師,還可以給 MIS、DBA、Deploy 團隊作參考用| |FINE|會印出包含 FINE 以上級別的 Log|詳細資訊設置| |FINER|次低階級,會印出包含 FINEST 以上級別的 Log|更詳細的資訊設置| |FINEST|最低階級,會印出所有級別的 Log|超級無敵詳細的資訊設置| > 依照設定文件的階級,將會只印出 **包含該級別與以上級別** 的 Log 訊息,級別高到低為由上往下。例如設定了 INFO 等級後,將無法看到 CONFIG、FINE、FINER、FINEST 的訊息 舉例: Log4j 階級: |Log 分級|說明|應用| |---|---|---| |OFF |關閉記錄功能|intLevel = 0,將讓所有 Log 記錄功能關閉| |FATAL |嚴重訊息|intLevel = 100,用於會導致系統停止運作的模組中| |ERROR |錯誤訊息|intLevel = 200,用於雖然會出錯,但不至於影響系統運作的功能模組中| |WARN |警示訊息|intLevel = 300,用於警示訊息,如: 沒有資料、格式異常等| |INFO |一般訊息|intLevel = 400,一般訊息| |DEBUG |除錯訊息|intLevel = 500,除錯用的訊息| |TRACE |追蹤訊息|intLevel = 600,追蹤流程的訊息,建議只用於測試時期| |ALL |紀錄全部等級的訊息|intLevel = Integer.MAX_VALUE,不建議開的階級,容易讓Log文件塞滿一大堆無用的資訊| > - intLevel 是 Log4j 用來處理 Log 分級的參數 > - 在文件中設定的級別,會使系統印出 **包含該級別與以上級別** 的所有訊息,級別是用 intLevel 來計算,值越小的排行越高 > - 因此當設定 INFO 的時候,將會印出 INFO、WARN、ERROR、FATAL 四個級別的 LOG 訊息 > - 當然 Log4j 還可以做到只輸出單獨一種階級或不輸出某種階級的設計,後續會提到 ## Log 套件種類 在這裡簡單的介紹 Spring Boot 可以使用的幾個 Log 套件 ### Log (JUL) 套件包: java.util.logging.Logger 簡單易懂,直呼其名,這是在 Log4j 問世後,Java API 決定自行打造的處理 Log 的套件,但因其階級的分設不同於 Log4j 來的易懂,且僅能輸出於 Console 和 文件,在其他部分的功能並沒有達到 Log4j 的強大,加上 Log4j 已取得市場的信任與穩定使用率,因此並沒有太多的開發者會選擇使用 JUL,若只是開發小功能、求快速結束的專案來說,不失為一個選擇。 (~~然而,套入 Log4j 並不會花太多時間,因此Log的使用率上還是非常非常少~~) ### commonsLog 套件包: org.apache.commons.logging.Log 主要用於處理 JUL 與 Log4j 整合的套件,可以讓開發師同時使用 JUL 和 Log4j 的功能,減少了需要同時配置 JUL 和 Log4j 的設定文檔,然而要注意的是,在 commonsLog 中使用的 function,其底層大多還是會使用 Log4j 的 function。 > - Spring 4.X 的話需要設置套件管理工具進行下載與import,但在 Spring 5.X 版本開始便會直接封裝在 spring-jcl 的套件內,可以直接使用。 > - 使用 SpringBoot 框架的開發師不用擔心需要設置套件管理工具進行處理,因為 SpringBoot 的框架原先就有包含 spring-jcl 的套件。 ### Log4j / Log4j2 套件: - Log4j: org.apache.log4j.Logger - Log4j2: org.apache.logging.log4j.Logger Java 史上第一個功能最多的 Log 套件,建立了 Log Level、Loggers、Appenders 等重要觀念,也影響日後其他套件產出許多類似的設計。 以下是四個 Log4j 改變了 Log 處理方式的創新: 1. 透過 Log Level 進行分級處理 2. 透過 Loggers 建立使用 lib 的函式庫的實體 3. 透過 Appenders 建立每個 Loggers 實體的詳細處理,並不限於輸出 Console 和 文件,可以和其他方式整合處理,例如寄信、連網傳輸等 4. 透過 異步寫入方式,不影響程式本身運作的效能,大大提升了寫入日誌的效率與安全性 在 2014 年,Log4j2 釋出了第一個版本,2 繼承了 1 的所有設計,最大的新功能是使用 Java 8 新增的 Lambda 功能去做到 lazy evaluation。 ### SLF4J 套件: org.slf4j.Logger 與其說 SLF4J 是 Log 套件,倒不如說它是在處理 Log 套件與程式之間解耦合的一個套件,其用來記錄 Log 的實體還是得依靠其他 Log 套件,如: Log4j、JUL、commonsLog 等。 因為在 Log4j 開發完成後,Log4j V2 的構思就是要做到 Log 套件與程式解耦合,而 SLF4J 的出現則解決了這個問題。 SLF4J也是目前大多專案使用的套件。 ### Flogger 套件: com.google.common.flogger.FluentLogger 是 Google 近年為 Java 打造的一款開源日誌框架,號稱提供比 **現有 Java Log API** 更多更完善的功能詳細可參考 [Flogger 官方文件](https://google.github.io/flogger/) 整體而言,Flogger 提供了更強可讀性的結構語句,以及將 Logger 實體與 Level 實體分開處理的方式,減少不必要的 Class 產生。 Flogger 也有增加處理 lazy evaluation 的部分,但 SLF4J 已經搶先設計完成這部分的功能,因此這無法成為讓大家捨棄 SLF4J 去使用 Flogger 的亮點。 ## 整合 Lombok [可參考官方設計文件](https://projectlombok.org/features/log) 類同於 Kai 在 Lombok 一文中提到的方式作即可。 [六角鼠年鐵人賽 Week 11 - Spring Boot - Clean Clear - Lombok](/uWHqezBxQ9CPqr5iUOfNFQ) ## 範例 **build.gradle** ```xml= configurations.all { exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging' } dependencies { implementation 'org.springframework.boot:spring-boot-starter-log4j2' } ``` **log4j2.properties** 放置在 resources 資料夾底下 ```xml= appender.console.type = Console appender.console.name = STDOUT appender.console.layout.type = PatternLayout appender.console.layout.pattern = %d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n rootLogger.level = DEBUG rootLogger.appenderRefs = stdout rootLogger.appenderRef.stdout.ref = STDOUT ``` **test.java** ```java= import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class test { private final static Logger logger = LoggerFactory.getLogger(test.class); } ``` ※ Logger 的寫法強烈建議使用 **private final static** 的方式。 不管該CLASS本身是否有實現 Serilaizable,這樣的寫法比較安全,特別在有使用 CACHE 套件的部分要更加注意。 Kai 經歷的專案曾經有同仁沒有正確將 Logger 物件設置為 static,而被包進了 CACHE 中,每一次動作就包了將近 80MB 的東西,不到一小時,200G的記憶體空間完全吃滿 -- 炸 Server。 一般對於這種情況我們可以透過將包入 CACHE 中的物件序列化,減少占用空間,畢竟有些資料本身就需要被 CACHE,但 Logger 一定要設為 static 才能夠避免被包入,CACHE 在執行的過程中是不會去主動包入 static 的物件,除非強制改動判別方式。 以上這部分就當老人碎念了... ## 結語 :::danger 以上內容差不多介紹完 LOG 的部分,下一篇將繼續介紹 **LogBack**,是一個幫助設定 Log 細節的套件,大大的減低開發者在處理不同 Log 套件整合的難度。 [六角鼠年鐵人賽 Week 13 - Spring Boot - 一次搞懂 LogBack 設定](/uXSkOMESTE6QGvRkqwwAaw) ::: 首頁 [Kai 個人技術 Hackmd](/2G-RoB0QTrKzkftH2uLueA) ###### tags: `Spring Boot`,`w3HexSchool`