SpringBoot入門系列(十二)統一日誌收集
- 2020 年 5 月 27 日
- 筆記
- SpringBoot入門系列
前面介紹了Spring Boot 異常處理,不清楚的朋友可以看看之前的文章://www.cnblogs.com/zhangweizhong/category/1657780.html。
今天主要講解Spring Boot中的日誌收集,日誌是追蹤錯誤定位問題的關鍵,特別在生產環境中,我們需要通過日誌快速定位解決問題。
Springboot的日誌的框架比較豐富,而且Springboot本身就內置了日誌功能,不過實際項目中會出現:只記錄想要的日誌,日誌輸出到磁碟,按天歸檔,日誌資訊同步到其他系統等功能。這些是Springboot本身就內置了日誌功能不具備的。所以我推薦使用logback。下面我們就以logback講講Spring Boot中的日誌收集。
為什麼要統一日誌
前面我們說了Springboot 本身就可以日誌功能,為什麼還需要統一規範日誌?
1、日誌統一,方便查閱管理。
2、日誌分割歸檔功能。
3、日誌持久化功能。
4、方便日誌系統(ELK)收集。
配置
在resource下創建logback-spring.xml文件,以下直接貼出配置資訊,介紹資訊可以直接參考備註
<?xml version="1.0" encoding="UTF-8"?> <!-- 日誌級別從低到高分為TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果設置為WARN,則低於WARN的資訊都不會輸出 --> <!-- scan:當此屬性設置為true時,配置文檔如果發生改變,將會被重新載入,默認值為true --> <!-- scanPeriod:設置監測配置文檔是否有修改的時間間隔,如果沒有給出時間單位,默認單位是毫秒。 當scan為true時,此屬性生效。默認的時間間隔為1分鐘。 --> <!-- debug:當此屬性設置為true時,將列印出logback內部日誌資訊,實時查看logback運行狀態。默認值為false。 --> <configuration scan="true" scanPeriod="10 seconds"> <contextName>logback</contextName> <!-- name的值是變數的名稱,value的值時變數定義的值。通過定義的值會被插入到logger上下文中。定義後,可以使「${}」來使用變數。 --> <property name="log.path" value="D:/Documents/logs/edu" /> <!--0. 日誌格式和顏色渲染 --> <!-- 彩色日誌依賴的渲染類 --> <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" /> <conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" /> <conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" /> <!-- 彩色日誌格式 --> <property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/> <!--1. 輸出到控制台--> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <!--此日誌appender是為開發使用,只配置最底級別,控制台輸出的日誌級別是大於或等於此級別的日誌資訊--> <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>debug</level> </filter> <encoder> <Pattern>${CONSOLE_LOG_PATTERN}</Pattern> <!-- 設置字符集 --> <charset>UTF-8</charset> </encoder> </appender> <!--2. 輸出到文檔--> <!-- 2.1 level為 DEBUG 日誌,時間滾動輸出 --> <appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- 正在記錄的日誌文檔的路徑及文檔名 --> <file>${log.path}/edu_debug.log</file> <!--日誌文檔輸出格式--> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> <charset>UTF-8</charset> <!-- 設置字符集 --> </encoder> <!-- 日誌記錄器的滾動策略,按日期,按大小記錄 --> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!-- 日誌歸檔 --> <fileNamePattern>${log.path}/web-debug-%d{yyyy-MM-dd}.%i.log</fileNamePattern> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>100MB</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> <!--日誌文檔保留天數--> <maxHistory>15</maxHistory> </rollingPolicy> <!-- 此日誌文檔只記錄debug級別的 --> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>debug</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> </appender> <!-- 2.2 level為 INFO 日誌,時間滾動輸出 --> <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- 正在記錄的日誌文檔的路徑及文檔名 --> <file>${log.path}/edu_info.log</file> <!--日誌文檔輸出格式--> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> <charset>UTF-8</charset> </encoder> <!-- 日誌記錄器的滾動策略,按日期,按大小記錄 --> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!-- 每天日誌歸檔路徑以及格式 --> <fileNamePattern>${log.path}/web-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>100MB</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> <!--日誌文檔保留天數--> <maxHistory>15</maxHistory> </rollingPolicy> <!-- 此日誌文檔只記錄info級別的 --> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>info</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> </appender> <!-- 2.3 level為 WARN 日誌,時間滾動輸出 --> <appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- 正在記錄的日誌文檔的路徑及文檔名 --> <file>${log.path}/edu_warn.log</file> <!--日誌文檔輸出格式--> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> <charset>UTF-8</charset> <!-- 此處設置字符集 --> </encoder> <!-- 日誌記錄器的滾動策略,按日期,按大小記錄 --> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${log.path}/web-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>100MB</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> <!--日誌文檔保留天數--> <maxHistory>15</maxHistory> </rollingPolicy> <!-- 此日誌文檔只記錄warn級別的 --> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>warn</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> </appender> <!-- 2.4 level為 ERROR 日誌,時間滾動輸出 --> <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- 正在記錄的日誌文檔的路徑及文檔名 --> <file>${log.path}/edu_error.log</file> <!--日誌文檔輸出格式--> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> <charset>UTF-8</charset> <!-- 此處設置字符集 --> </encoder> <!-- 日誌記錄器的滾動策略,按日期,按大小記錄 --> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${log.path}/web-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>100MB</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> <!--日誌文檔保留天數--> <maxHistory>15</maxHistory> </rollingPolicy> <!-- 此日誌文檔只記錄ERROR級別的 --> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>ERROR</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> </appender> <!-- <logger>用來設置某一個包或者具體的某一個類的日誌列印級別、 以及指定<appender>。<logger>僅有一個name屬性, 一個可選的level和一個可選的addtivity屬性。 name:用來指定受此logger約束的某一個包或者具體的某一個類。 level:用來設置列印級別,大小寫無關:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF, 還有一個特俗值INHERITED或者同義詞NULL,代表強制執行上級的級別。 如果未設置此屬性,那麼當前logger將會繼承上級的級別。 addtivity:是否向上級logger傳遞列印資訊。默認是true。 <logger name="org.springframework.web" level="info"/> <logger name="org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor" level="INFO"/> --> <!-- 使用mybatis的時候,sql語句是debug下才會列印,而這裡我們只配置了info,所以想要查看sql語句的話,有以下兩種操作: 第一種把<root level="info">改成<root level="DEBUG">這樣就會列印sql,不過這樣日誌那邊會出現很多其他消息 第二種就是單獨給dao下目錄配置debug模式,程式碼如下,這樣配置sql語句會列印,其他還是正常info級別: 【logging.level.org.mybatis=debug logging.level.dao=debug】 --> <!-- root節點是必選節點,用來指定最基礎的日誌輸出級別,只有一個level屬性 level:用來設置列印級別,大小寫無關:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF, 不能設置為INHERITED或者同義詞NULL。默認是DEBUG 可以包含零個或多個元素,標識這個appender將會添加到這個logger。 --> <!-- 4. 最終的策略 --> <!-- 4.1 開發環境:列印控制台--> <springProfile name="dev"> <logger name="com.cms" level="info"/> <root level="info"> <appender-ref ref="CONSOLE" /> <appender-ref ref="DEBUG_FILE" /> <appender-ref ref="INFO_FILE" /> <appender-ref ref="WARN_FILE" /> <appender-ref ref="ERROR_FILE" /> </root> </springProfile> <!-- 4.2 生產環境:輸出到文檔--> <springProfile name="pro"> <logger name="com.cms" level="warn"/> <root level="info"> <appender-ref ref="ERROR_FILE" /> <appender-ref ref="WARN_FILE" /> </root> </springProfile> </configuration>
注意
-
日誌的環境即spring.profiles.acticve,跟隨項目啟動。
-
啟動後,即可到自定目錄查找到生成的日誌文件。
- 官方推薦使用的xml名字的格式為:logback-spring.xml而不是logback.xml。
配置application.properties
在application.properties配置logback
############################################################ # # logback 配置,日誌管理 # ############################################################ #日誌配置,輸出到文本, logging.config=classpath:logback-spring.xml #idea控制台默認日誌級別修改 # 指定輸出日誌的文件名,默認輸出至當前項目目錄下 #logging.file.path=springboot.log
收集異常日誌
上一篇文章已經講過統一異常處理,請看這篇文章《SpringBoot入門系列(十一)統一異常處理的實現》。修改統一異常處理器GlobalExceptionHandler類,將異常方法中的直接列印改為日誌輸入並列印:
@ExceptionHandler(value = Exception.class) public Object errorHandler(HttpServletRequest reqest, HttpServletResponse response, Exception e) throws Exception { //e.printStackTrace(); // 記錄日誌 logger.error(ExceptionUtils.getMessage(e)); // 是否ajax請求 if (isAjax(reqest)) { return JSONResult.errorException(e.getMessage()); } else { ModelAndView mav = new ModelAndView(); mav.addObject("exception", e); mav.addObject("url", reqest.getRequestURL()); mav.setViewName(ERROR_VIEW); return mav; } }
程式中記錄日誌
在com.weiz.controller 中創建LoggingController 控制器
package com.weiz.controller; import com.weiz.utils.JSONResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/log") public class LoggingController { Logger logger = LoggerFactory.getLogger(getClass()); @RequestMapping("/write") public JSONResult writeLog(){ // 級別由低到高 trace<debug<info<warn<error logger.trace("這是一個trace日誌"); logger.debug("這是一個debug日誌"); logger.info("這是一個info日誌"); logger.warn("這是一個warn日誌"); logger.error("這是一個error日誌"); return JSONResult.ok("write log success"); } }
測試
啟動項目,在瀏覽器輸入://localhost:8080/log/write ,去相關目錄下查看日誌文件
異常日誌:
最後
以上,就把Spring Boot集成logback進行統一日誌收集介紹完了。還是比較簡單的,還有其他實際應用場景中的相關需求如:日誌壓縮,同步kafka等,大家自己去研究吧。
這個系列課程的完整源碼,也會提供給大家。大家關注我的微信公眾號(架構師精進),回復:springboot源碼。獲取這個系列課程的完整源碼。