日志是運維武翎、排錯的一個重要助手,很多人應該都維護過沒有日志的項目榜掌,知道排查問題是什么感覺优妙。所以搭建基礎項目框架時,自然不能少了日志憎账。
日志組件選擇
從網上各種搜索對比套硼,在log4j2和logback之間選擇了log4j2,綜合各處評價,log4j2在性能方法有一定優(yōu)勢胞皱。但是在一個項目內使用后就發(fā)現邪意,spring boot內log4j2不支持spring profile機制,也就是在本地環(huán)境反砌、測試環(huán)境雾鬼、預發(fā)布環(huán)境、正式環(huán)境需要手動切換配置宴树,當前公司的多個環(huán)境在相同的服務器上策菜,所以這種方式會導致多個環(huán)境的日志生成在了同一個文件內,很不利于問題排查酒贬。因此又將日志組件換回了logback又憨,因為對當前公司的項目來說,日志支持profile機制更重要锭吨,性能瓶頸絕不在日志這塊蠢莺。
logback配置
spring boot內配置logback還是很簡單的,只需要在src/main/resources目錄下創(chuàng)建logback-spring.xml零如,在xml內添加自己的日志配置即可躏将。支持三個環(huán)境local、dev埠况、prod的日志配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="30 seconds">
<property name="LOG_PATH" value="/mnt/diskb/logs"/>
<springProfile name="local">
<logger name="com.onecoderspace" level="debug" additivity="true"/>
<appender name="logfile" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ssS} %5p [%c]:%L-%m%n</pattern>
</encoder>
</appender>
</springProfile>
<springProfile name="dev">
<logger name="com.onecoderspace" level="info" additivity="true"/>
<appender name="logfile" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/projectName/projectName_dev.log</file>
<append>true</append>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ssS} %5p [%c{5}#%M]:%L-%m%n%caller{0}</pattern>
</encoder>
<prudent>false</prudent>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- daily rollover -->
<fileNamePattern>${LOG_PATH}/projectName/projectName_dev.%d{yyyy-MM-dd}.log.gz
</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
</appender>
</springProfile>
<springProfile name="prod">
<logger name="com.onecoderspace" level="info" additivity="true"/>
<appender name="logfile" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/projectName/projectName.log</file>
<append>true</append>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ssS} %5p [%c{5}#%M]:%L-%m%n%caller{0}</pattern>
</encoder>
<prudent>false</prudent>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- daily rollover -->
<fileNamePattern>${LOG_PATH}/projectName/projectName.%d{yyyy-MM-dd}.log.gz
</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
</appender>
</springProfile>
<root level="info">
<appender-ref ref="logfile" />
</root>
</configuration>
spring-boot-starter-web內已經包含了logback和slf4j的依賴耸携,所以只要項目依賴了spring-boot-starter-web,就不需要做其他額外的配置了辕翰。
日志使用
調用日志時建議使用slf4j夺衍,雖然基本不會在后續(xù)變更日志組件,但使用slf4j是一個好的習慣喜命。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private static Logger logger = LoggerFactory.getLogger(LoginController.class);
debug日志
debug日志建議添加logger.isDebugEnabled()判斷沟沙,在重要的流程上都留下日志河劝,這樣當系統出現問題時,可以通過debug日志矛紫,快速定位問題赎瞎,能代替很多斷點調試的時間。
if(logger.isDebugEnabled()){
logger.debug("user={} login success",username);
}
info日志
比較重要的信息颊咬,對于系統運行有比較重要的參考意義务甥,同時不會對性能造成影響,可以在正式環(huán)境展示的信息喳篇,使用info基本打印敞临,如定時任務運行時間等。
logger.info("end fetcher proxy use time={}", System.currentTimeMillis()- t);
error日志
error日志相對來說是最重要的麸澜,但使用時需要注意使用方式挺尿,不正確的方式會導致很多信息被隱藏〈栋睿可以參考如下方式:
logger.error(String.format("error msg ,arg1=%s,arg2=%s",arg1,arg2), e);
- 盡可能的帶上異常發(fā)生時的參數编矾,這個對排查問題很有意義
- 打印異常的完整堆棧信息,僅打印e.getMessage()會導致很多信息被隱藏
- 只在異常發(fā)生時或明確的業(yè)務錯誤時使用error馁害,不要用error來打印調試窄俏、普通信息
總結
- spring boot項目內日志組件選擇logback比較好,內嵌的日志組件蜗细,支持profile機制裆操;
- logback配置方式為在src/main/resources目錄下創(chuàng)建logback-spring.xml怒详,配置內容參考上文炉媒;
- 調用日志時使用slf4j,注意合理使用日志級別
- 注意以下幾點tips
tips
1昆烁、 應用日志盡量放在數據盤上吊骤,不要放在系統盤上,遇到了不止一次日志寫滿系統盤導致服務暫停的情況
2静尼、 技術負責人定好日志規(guī)范白粉,在代碼review時指出幾次日志使用的問題,能夠很快讓良好使用日志成為團隊的習慣
3鼠渺、 正式環(huán)境的日志基本最低為info鸭巴,通常可以調整為warn或error
4拦盹、 在while循環(huán)內有異常捕獲時鹃祖,注意當異常發(fā)生時,不能無限打印日志普舆,如下代碼:
while (flag) {
try {
byte[] bb = _queue.poll(1, TimeUnit.SECONDS);
if (bb != null) {
@SuppressWarnings("unchecked")
Map<String, Object> m = JacksonSupport.decode1(new ByteArrayInputStream(bb), Map.class);
E event = _consumer.getEventType().newInstance();
event.fromMap(m);
_consumer.onEvent(event);
}
} catch (Exception e) {
logger.error("redis queue poll due to error", e);
}
}
從基于redis開發(fā)的一個blockingQueue內獲取元素進行消費恬口,代碼運行了一年多十分正常校读,但是有一次幾乎把磁盤寫滿了,因為當時運維調整祖能,redis停掉了歉秫, _queue.poll這里就開始拋異常,然后下面就狂寫日志养铸,一直把磁盤寫滿雁芙。類似這樣的地方,可以進行一個計數钞螟,連續(xù)錯誤達到多少次却特,就終止循環(huán)并以某些方式提醒運維人員。
5筛圆、不要使用System.out.println()裂明,建議隔段時間全局搜索一次,發(fā)現了就在小組會議上提一下太援,很快這種現象就會杜絕
本人搭建好的spring boot web后端開發(fā)框架已上傳至GitHub闽晦,歡迎吐槽!
https://github.com/q7322068/rest-base,已用于多個正式項目提岔,當前可能因為版本問題不是很完善仙蛉,后續(xù)持續(xù)優(yōu)化,希望你能有所收獲碱蒙!