如何在項目中記錄日誌信息?

大家好,我是3y。在正文之前,先給各位股東彙報下austin項目進度:

img

總的來說,我感覺這次的反響是不錯的,雖然閱讀量不高。但留言的人多了很多,也有很多人都擔心我會不會鴿掉(更新一半中途就斷了)

我只能說:別慌,絕對不鴿,你只管追更就好

img

我已經決定每個週末都扛着電腦回家,有空就往附近的圖書館裏跑(圖書館是學習的YYDS,在家的效率就是要比圖書館要低不少)

不多BB了,今天繼續聊個話題:日誌

01、什麼是日誌

所謂日誌,在我理解下就是:記錄程序運行時的信息

img

在Java最初期又或是我們初學階段,打印日誌全憑System.out.println();

這好用嗎?有待商榷。

對於大部分初學者來說,好用!我想看的信息,直接在console就能看到了,這是多麼地方便阿。學習Java的第一個運行結果都是由System.out.println();出來的,不需要有任何的學習成本。

對於大部分工作者來說,本地調試可以,但如果程序部署到服務器以後,那就算了。

生產環境跟本地環境是有區別的:

  • 生產環境需要記錄的日誌會更多(畢竟是作爲一個系統/項目在線上運行,不可能只打印一點點內容)
  • 生產環境的日誌內容需要保留至文件(作爲留存,線上不會說第一時間發現問題,很多需要查找歷史日誌數據)
  • 生產環境的日誌內容需要有一定的規範格式(至少日誌記錄的時間需要有吧)

上面這些要求,System.out.println();都是不具備的。

所以,我們可以看到在公司裏寫的項目,是沒有用System.out.println();記錄日誌的

img

02、Java日誌體系

工作了以後,你會發現每次引入一個框架,這個框架下幾乎都有對應的日誌包。

我之前在公司裏曾經整合過幾個項目(將原有的幾個工程合併到一個項目內)。

系統分久必合合久必分,當時是認爲以前的同事把項目拆得過於細,造成一定的資源浪費(畢竟每個工程跑在線上至少都會部署兩臺線上機器),所以有段時間公司就希望我們把一些細小的項目進行合併。

至於這做得對與錯,這塊我就不談了。

在合併的過程中,最最最麻煩的就是解決依賴衝突的問題(都是Maven項目,會有Maven仲裁的問題),而這裏邊,最明顯的就是Java日誌包的問題。

如果你有那麼一丟丟了解Java日誌,你就應該多多少少聽說過以下的名字:Log4j(log for java)、JUL(Java Util Logging)、JCL(Jakarta Commons Logging)、Slf4j(Simple Logging Facade for Java)、LogbackLog4j2

img

如果你比較細心,你會發現,不同的技術框架所採用的Java日誌實現都很有可能不一樣的。

既然實現不一樣,那對應的API調用是不是就不一樣?(畢竟它還不像是JDBC,定義了一套接口規範,各個數據庫廠商去實現JDBC規範,程序員面向JDBC接口編程就完事了)

那這這這不是亂套呢?想到這裏,血壓就逐漸就上來了?這別慌,上面提到的Java日誌Slf4j(Simple Logging Facade for Java)乾的就類似JDBC做的事情。

它定義了日誌的接口(門面模式),當項目使用別的日誌框架時,那就適配它!(注意:JDBC是定義接口,數據庫廠商實現。Slf4j也定義了接口,但是它適配其他的Java日誌實現,騷不騷?)

我們看Slf4j官網的一張圖,應該就挺好理解了:

img

扯了這麼久,我想表達的是:我們在項目中,最好是使用Slf4j提供的API,至於真實的LOG實現,都可以用Slf4j進行橋接(這樣一來,或許將來有一天說要從log4j改爲logback,那程序代碼也不用改動)

03、日誌有什麼用?

還沒有過生產環境的開發的同學可能認爲記錄日誌就是用來定位問題的,其實並不完全是。

日誌一方面我們用它來定位問題,一方面我們很多的數據也是來源於日誌

不要覺得存在數據庫裏的數據纔是重要的,我們程序運行時記錄下的日誌數據也同樣重要

在大數據領域裏,數據來源有很多:關係型數據庫、爬蟲、日誌等等

img

舉個例子,我以前的公司就有處理日誌的一套框架:

  1. 我們正常把日誌信息輸出到文件下
  2. 框架提供後臺給予我們配置(文件的路徑以及Kafka Topic Name)

該框架做的事情說白了就是:把我們的日誌文件內容轉成Kafka消息(如果使用方需要將哪個日誌文件的內容轉爲MQ消息,那在平臺上配置下就完事了)

有了Kafka消息,那配合流式處理平臺(Storm/Spark/Flink)再對日誌進行清洗,是不是就能產生有價值的數據

img

04、Austin 日誌

扯了這麼久的日誌基礎,只是想讓還不瞭解日誌的同學有個認知。

不扯別的了,還是回到我們還在「新建文件夾」階段的austin項目吧。

austin項目的搭建技術框架使用的是SpringBoot,SpringBoot默認的日誌組合是:Slf4j + logback

我在公司接觸到的項目幾乎都是這個組合,所以我就不打算動了,就直接用logback作爲austin的日誌實現框架了(要是真有那麼一天要改成別的日誌實現,理論上只要引入對應的橋接包就完事了)。

05、logback日誌初體驗

在無任何配置的前提下,只要我們引入了SpringBoot的包,就能直接使用日誌的功能了。具體效果入下圖

img

SpringBoot是約定大於配置的一個框架

SpringBoot會默認去加載resources下名爲**logback.xml **或者 logback-spring.xml的配置文件( xml 格式也可以改爲 groovy 格式)

如果都不存在,那麼 logback 默認地會調用BasicConfigurator ,創建一個最小化配置。

最小化配置由一個關聯到根 logger 的ConsoleAppender 組成。輸出用模式爲%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 的 PatternLayoutEncoder 進行格式化

img

06、logback配置

從上面可以發現的是,默認的logback配置是不符合我們的要求的(它是打印在console的),我們是希望把日誌記錄在文件下的。

所以,我們會在resources下新建一個logback配置。常見的配置內容如下:


<?xml version="1.0" encoding="UTF-8"?>

<configuration scan="true" scanPeriod="10 seconds">

    <contextName>austin</contextName>

    <!-- 設置日誌輸出路徑  可以使“${}”來使用變量。TODO 這裏後面是需要讀配置的 -->
    <property name="log.path" value="logs"/>


    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化輸出:%d表示日期,%thread表示線程名,%-5level:級別從左顯示5個字符寬度%msg:日誌消息,%n是換行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
            <!-- 設置字符集 -->
            <charset>UTF-8</charset>
        </encoder>
    </appender>


    <!-- 時間滾動輸出 level爲 INFO 日誌 -->
    <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 正在記錄的日誌文件的路徑及文件名 -->
        <file>${log.path}/austin-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}/logs/austin-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>1000MB</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>

    <!-- 時間滾動輸出 level爲 ERROR 日誌 -->
    <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 正在記錄的日誌文件的路徑及文件名 -->
        <file>${log.path}/austin-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}/austin-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>1000MB</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>

    <root level="info">
        <!-- TODO console打印後面可以只針對dev環境的 -->
        <appender-ref ref="CONSOLE"/>
        <appender-ref ref="INFO_FILE"/>
        <appender-ref ref="ERROR_FILE"/>
    </root>

</configuration>

img

日誌的配置不會是一成不變的,現在項目剛搭建出來,就怎麼簡單怎麼來。

07、彩蛋

這篇文章發佈的前一個晚上,ZhenDong突然問我現在用的什麼MQ比較多,我隨口一答:Kafka吧,我接觸MQ基本都是Kafka

他說在寫一個好東西,到時候發出來。我這一聽,就肯定感興趣了啊。

img

ZhenDong發的文章鏈接:https://mp.weixin.qq.com/s/JC51S_bI02npm4CE5NEEow

文章大概就是美團大佬們他們用AOP+動態模板封裝了一套SDK,進而優雅地記錄操作日誌(說人話就是:大佬不想日誌寫在業務代碼上,難以管理。將寫日誌這個動作抽象出來,用註解來統一記錄日誌)

文章還是很精彩的,我推薦閱讀一遍。

ZhenDong大佬看完文章後,自己實現了一套,已經差不多快要完成了。順便我跟他討論了下使用場景,感覺我的項目也可以用那一套東西(有優雅的打日誌方式,誰不愛呢

我已經預定了,到時候他給我發源碼,我就學習下實現思路(後面項目也用他提供的SDK來打日誌,有問題就開噴🐶[狗頭.jpg])。等他忙完寫好文章,我也轉載下跟大家一起學習下。

img

像這種輪子或者說是經驗思路,自己學會了以後,就可以在面試的時候吹了。就說自己對項目系統改造了一把,從原來的破鬼樣(介紹背景) *,變成現在如此優雅(**得到的結果**),並這個過程中穿插自己的實現思路以及遇到的坑(**艱辛的過程**),這種亮點*哪個面試官又不愛呢

08、總結

日誌在一個項目裏,我認爲是在一個比較重要的位置上的。我們的數據和定位問題都離不開日誌,有的項目的日誌相當混亂,那維護起來就特別特別麻煩。

其實我完全可以自己寫個logback配置就把這塊給忽略了,但我還是堅持梳理出來,這篇文章按照「項目」的維度從頭梳理了一遍日誌的知識,希望對大家有幫助吧。

項目源碼Gitee鏈接:https://gitee.com/austin

項目源碼GitHub鏈接:https://github.com/austin

img

關注我的微信公衆號【Java3y】來聊點不一樣的!

【對線面試官+從零編寫Java項目】 持續高強度更新中!求star

原創不易!!求三連!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章