聊一聊項目中的日誌配置

相信大家都知道,作爲一個開發,除了要會寫bug之外,還要能夠通過日誌來解決bug。這樣,線上出了問題,有人來找我們解決,有人需要我們,我們才能保住自己的飯碗……可見,要知道怎麼檢測和解決自己搞出來的bug是多麼的重要。

嘿嘿,開個玩笑,引入正題。相信很多人在項目中都有用到日誌管理工具:log4j、logback等。但是不知道有多少人是這樣給項目添加日誌配置的:項目要用到log4j,好的,導入相應的maven包,網上找個配置文件,看着比較靠譜,管他三七二十一,放項目裏面試試,日誌文件正常輸出,ok,關閉配置文件,繼續寫bug去了,但是對於配置文件裏面的配置卻一知半解,反正能用就行。

這樣其實是很不好的習慣,畢竟,只會CV的程序員是沒有錢途的。強哥今天就根據自己之前做過的項目的實際案例簡單的和大家聊聊在項目上線後,日誌配置的一些問題,及處理辦法。

首先,給個項目在最開始項目上線時,配置的log4j2.xml日誌文件內容:


<?xml version="1.0" encoding="UTF-8"?>
<configuration status="WARN" monitorInterval="30">
    <Properties>
        <Property name="fileName">./logs/xxx</Property>
    </Properties>
    <!--先定義所有的appender-->
    <appenders>
        <!--這個輸出控制檯的配置-->
        <console name="Console" target="SYSTEM_OUT">
            <!--輸出日誌的格式-->
            <PatternLayout pattern="[xxx] [%d{yyyy-MM-dd HH:mm:ss,SSS}] [%t] [%p] %c{1}:%L - %m%n"/>
        </console>
        <!-- 這個會打印出所有的info及以下級別的信息,每次大小超過size,則這size大小的日誌會自動存入按年份-月份建立的文件夾下面並進行壓縮,作爲存檔-->
        <RollingFile name="RollingFileDebug" fileName="${fileName}/debug.log"
                     filePattern="${fileName}/$${date:yyyy-MM}/debug-%d{yyyy-MM-dd}-%i.log">
            <!--控制檯只輸出level及以上級別的信息(onMatch),其他的直接拒絕(onMismatch)-->
            <ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="[xxx] [%d{yyyy-MM-dd HH:mm:ss,SSS}] [%t] [%p] %c{1}:%L - %m%n"/>
            <Policies>
                <TimeBasedTriggeringPolicy/>
                <SizeBasedTriggeringPolicy size="10 MB"/>
            </Policies>
            <DefaultRolloverStrategy/>
        </RollingFile>
    <RollingFile name="RollingFileInfo" fileName="${fileName}/info.log"
                     filePattern="${fileName}/$${date:yyyy-MM}/info-%d{yyyy-MM-dd}-%i.log">
            ……//內容類似上面的RollingFileDebug
        </RollingFile>
    <RollingFile ……  </RollingFile>
    </appenders>
    <!--然後定義logger,只有定義了logger並引入的appender,appender纔會生效-->
    <loggers>
        <!--過濾掉spring和mybatis的一些無用的DEBUG信息-->
        <logger name="org.springframework" level="INFO"></logger>
        <logger name="org.mybatis" level="DEBUG"></logger>
        <root level="debug">
            <appender-ref ref="Console"/>
            <appender-ref ref="RollingFileDebug"/>
      ……
        </root>
    </loggers>
</configuration>

這個項目有個功能是通過定時器每分鐘撈取第三方的接口的大量內容,獲取數據後,再對數據進行處理入庫。

日誌配置中的:

<loggers>
  <!--過濾掉spring和mybatis的一些無用的DEBUG信息-->
  <logger name="org.springframework" level="INFO"></logger>
  <logger name="org.mybatis" level="DEBUG"></logger>
  <root level="debug">
    <appender-ref ref="Console"/>
    <appender-ref ref="RollingFileDebug"/>
        ……
  </root>
</loggers>

可以看出,對日誌進行了控制檯輸出之外,還進行了不同級別的日誌輸出及壓縮打包。最終出來的日誌文件目錄類似這樣:

結合項目的特點,不知道大家有沒有覺得哪裏需要改進的地方?

在項目剛上線的一段時間,並沒有發現什麼不妥的地方,日誌文件正常輸出,線上出了問題直接敲命令:tail -f console.log 就可以實時的看到日誌輸出,發現問題所在。而且每天也會自動進行日誌文件歸檔,保留日誌歷史,看起來一切都是那麼的合理且舒適。

可是,當項目運行一段時間之後,問題漸漸凸顯。某天,強哥發現在部署其他項目到同一臺機子的時候,磁盤不夠用了???奇了怪,這個機子沒放幾個項目,怎麼就沒空間啦,直接敲命令看看有哪些大文件佔用了磁盤空間:

find . -type f -size +800M  -print0 | xargs -0 du -h | sort -nr

發現,上線一段時間後的項目,console.log日誌文件居然達到了30多個G,這也太誇張了。不知不覺,項目的控制檯輸出日誌自己膨脹到如此地步,簡直無法無天。看看內容除了獲取第三方接口數據輸出之外,主要的日誌輸出都是sql語句的打印。於是很簡單的處理辦法就是,既然項目已經穩定上線,sql語句的輸出看不看其實影響不大。於是,就先把日誌輸出級別改爲INFO級別,減少sql語句的打印:

<loggers>
  <!--過濾掉spring和mybatis的一些無用的DEBUG信息-->
  <logger name="org.springframework" level="INFO"></logger>
  <logger name="org.mybatis" level="INFO"></logger>
  <root level="INFO">
    <appender-ref ref="Console"/>
    <appender-ref ref="RollingFileDebug"/>
    <appender-ref ref="RollingFileInfo"/>
    <appender-ref ref="RollingFileWarn"/>
    <appender-ref ref="RollingFileError"/>
  </root>
</loggers>

可是沒過多久發現,雖然改了日誌輸出級別,但是由於項目中定時器調用第三方接口的日誌輸出還是非常多,而且console.log文件並不會自動歸檔,該文件依然還會以極快的速度一天天膨脹,將磁盤耗盡。

沒有辦法,貪心不足蛇吞象,不用說了,今天就要把你這蛇給宰了:

<loggers>
  <!--過濾掉spring和mybatis的一些無用的DEBUG信息-->
  <logger name="org.springframework" level="INFO"></logger>
  <logger name="org.mybatis" level="INFO"></logger>
  <root level="INFO">
    <appender-ref ref="RollingFileDebug"/>
    <appender-ref ref="RollingFileInfo"/>
    <appender-ref ref="RollingFileWarn"/>
    <appender-ref ref="RollingFileError"/>
  </root>
</loggers>

直接把控制檯日誌刪了。有些人可能會問:輸出日誌不看了?出問題了怎麼辦?其實,日誌還是能看得到的。我們看到,在配置中已經有了debug.log,info.log,error.log等文件,而這些文件根據不同的輸出級別也是實時更新的。那麼控制檯日誌在這種情況下其實就完全沒有必要了,這玩意兒更適合開發適合用。

解決了這個問題之後,我們繼續看看日誌的歸檔配置,不知道大家知不知道下面的配置,日誌是如何歸檔的。各種級別的日誌在到達多大後會歸檔爲一個文件?最多一個級別歸檔一天歸檔出幾個文件?文件個數滿了,後面的輸出日誌怎麼辦?


<RollingFile name="RollingFileDebug" fileName="${fileName}/debug.log"
filePattern="${fileName}/$${date:yyyy-MM}/debug-%d{yyyy-MM-dd}-%i.log">
<!--控制檯只輸出level及以上級別的信息(onMatch),其他的直接拒絕(onMismatch)-->
<ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="[xxx] [%d{yyyy-MM-dd HH:mm:ss,SSS}] [%t] [%p] %c{1}:%L - %m%n"/>
<Policies>
  <TimeBasedTriggeringPolicy/>
  <SizeBasedTriggeringPolicy size="10 MB"/>
</Policies>
<DefaultRolloverStrategy />
</RollingFile>

其實,也很簡單,上面配置內容,注意這三行:

<TimeBasedTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="10 MB"/>
<DefaultRolloverStrategy/>

第一行的意思是:通過時間觸發歸檔,即到了某一時間時,不管當前主文件(如:debug.log)有多大,都對其進行歸檔(如輸出:debug-2019-10-01-1.log);

第二行的意思是:當主文件大小達到10M時對文件進行歸檔。

第三行的意思是:根據某個滾動存儲算法,保存同一天該級別的主日誌文件的歸檔個數。上面使用的是默認算法,默認每天最多將主文件歸檔7份。

通過上面的配置後,在./2019-10/目錄下的日誌文件列表如下:

那麼,一個級別的文檔一天默認就保存7份夠嗎?像文章上面提到的,日誌輸出量很大,只保留7份不是依然不夠嗎?對的,我們其實可以通過設置上面第三行的配置,添加參數min,max等屬性對歸檔文件個數進行限制,設置max=20甚至更大便可以保存更多份日誌文件。

當然日誌文件越多,佔用磁盤空間就越大,而且假如設了20個還是不夠呢?剩下新的日誌是不是就丟了?

其實,默認的DefaultRolloverStrategy歸檔策略是:將當天最舊的日誌刪除,然後前面的日誌文件末尾數字-1後重命名,最後新的日誌寫入新生成的數字最大的那個文件中。也就是說,丟掉舊的日誌,保存較新的日誌,沒錯,赤裸裸的渣男特性-->喜新厭舊。不過這個也比較符合我們的查看日誌的情況:一般遇到線上問題之後,我們都喜歡先看看最近的日誌是否有報錯信息,找到錯誤後再解決問題。

當然,如果我們的項目日誌是比較重要的,絲毫不能丟失的,比如支付相關的系統。那麼,上面的處理方式就不是很適合了,日誌文件必須時刻保留,可以考慮通過搭建ELK日誌管理系統對日誌就行處理。不過,因爲這個不是本篇文章的重點,就不多提了,以後有機會可以聊聊。

希望大家以後在使用一個東西的時候,能夠多想一想,多看一看,一個配置是否適用於不同環境,至少做到心中有數,這樣對自己敲出來的代碼也會更有信心。好啦,今天就到這,下期真的要開始設計模式了,哈哈!

關注公衆號獲取更多內容,有問題也可在公衆號提問哦:

強哥叨逼叨

叨逼叨編程、互聯網的見解和新鮮事

發佈了55 篇原創文章 · 獲贊 72 · 訪問量 27萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章