網站結構分析(6000字講明白大型網站架構技術細節)

日志對於一個大型網站系統而言,日志是後端應用程序必須要引入的模塊,日志有利於追查Bug和記錄用戶操作。每個編程語言都有很多現成的日志框架,而Java的日志框架也有很多,如log4j2、Logback、SLF4J等,這些日志框架都可以讓後端應用程序按照一定的規則輸出日志文件。在大型網站系統當中,僅僅把日志記錄下來是遠遠不夠的。大型網站系統需要一個完整的日志系統,日志系統除瞭需要收集日志並將其記錄下來以外,還需要做日志篩選、用戶行為記錄追溯及風險預警等工作。不過,在項目中前期,或者單獨調試某個後端應用程序時,仍然需要使用日志模塊。這裡以log4j2為例,通過日志模塊引入和規范化日志記錄兩個方面對日志模塊的使用進行介紹。1.日志模塊引入引入日志模塊的具體操作步驟如下:(1)引入log4j2依賴包。需要在工程配置文件(build.gradle)中添加log4j2依賴包,如代碼4.32所示。需要註意的是,如果不去除Spring Boot中原有日志模塊的話,那麼新引入的日志模塊與原有日志模塊會產生沖突。代碼4.32 build.gradle中添加log4j2依賴包…configurations {//去除Spring Boot中原有的日志模塊all*.exclude group: 'org.springframework.boot', module: 'spring-bootstarter-logging'}…dependencies {…//dependencies中添加log4j2的依賴包implementation 'org.springframework.boot:spring-boot-starter-log4j2'…}…(2)同步工程配置。修改完build.gradle文件後,log4j2的依賴包在同步工程配置後才會被下載和引入。在IntelliJ IDEA中單擊“同步”按鈕即可同步工程配置,如圖4.59所示。圖4.59 在IntelliJ IDEA中同步build.gradle配置(3)創建log4j2的配置文件log4j2.xml,其內容及設置說明如代碼4.33所示,更詳細的說明請參考官方說明(https://logging.apache.org/log4j/2.x/manual/index.html)。另外,日志配置文件一般與後端應用程序的配置文件放在一起,如圖4.60所示。圖4.60 日志配置文件存放位置說明:圖4.60中的“配置文件目錄位置”和“後端應用程序配置文件名”都不是默認設置。關於“配置文件目錄位置”和“後端應用程序配置文件名”的設置可參考前面小節中的講解。代碼4.33 log4j2.xml文件的內容及其配置<?xml version="1.0" encoding="UTF-8"?><!– monitorInterval:檢查更新的時間間隔,單位為s。在程序運行期間,log4j2能夠自動檢測日志配置文件是否有更新,如果有更新則自動加載新設置–><configuration monitorInterval="1800"><!–配置變量,變量會被後續設置使用–><properties><!– 設置日志格式的變量:%d:獲取日期時間;%level:日志等級;%msg:日志消息,如ERROR、INFO、DEBUG等%n:換行符–><property name="LOG_PATTERN"value="[%d{yyyy-MM-dd}][%d{HH:mm:ss}][%level]%msg%n" /><!– 設置日志存儲路徑的變量 –><property name="FILE_PATH" value="D:/logs/backend/demo" /></properties><!– 設置日志輸出源,如設置日志輸出格式、設置日志文件名等 –><appenders><!– 設置Console(控制臺)輸出日志格式,一般在開發工具調試時使用 –><console name="Console" target="SYSTEM_OUT"><!–輸出日志的格式,采用properties中設置的LOG_PATTERN變量–><patternLayout pattern="${LOG_PATTERN}"/></console><!– 設置記錄INFO和DEBUG日志等級的日志文件,當符合存檔策略時在(<policies></policies>中設置),則會自動壓縮並另存為存檔文件。fileName:日志文件名,使用properties中設置的FILE_PATH變量。在此例中,輸出文件名為D:/logs/backend/demo/web-info.log。immediateFlush:接收到日志後,是否立即輸出到文件中。這個一般設置為false,設置為true會嚴重影響接口的並發能力。filePattern:存檔文件名,在此例中,歸檔文件名為(以2020-2-23為例)D:/logs/backend/demo/web-info/web-info-2020-2-23_1.log.gz–><rollingFile name="RollingFileInfo" fileName="${FILE_PATH}/webinfo.log"immediateFlush="false"filePattern="${FILE_PATH}/web-info/web-info-%d{yyyy-MMdd}_%i.log.gz"><!– 輸出日志的格式,采用properties中設置的LOG_PATTERN變量 –><patternLayout pattern="${LOG_PATTERN}"/><!– 篩選接收的日志等級,接收INFO和DEBUG等級的日志 –><filters><thresholdFilter level="error" onMatch="DENY" onMismatch="NEUTRAL"/><thresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/><thresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY"/></filters><!– 設置存檔策略,此例為:每天自動存檔,日志文件超過20MB也會存檔 –><policies><!– 設置時間的存檔策略,interval的時間精度與filePattern的時間精度一致,因為filePattern隻設置到日期,所以這裡的interval="1"指的是1天–><timeBasedTriggeringPolicy interval="1"/><!– 設置文件大小的存檔策略–><sizeBasedTriggeringPolicy size="20MB"/></policies><!– 設置保留多少個日志文件,日志文件個數超過max的值會自動覆蓋 –><defaultRolloverStrategy max="15"/></rollingFile><!– 設置記錄error日志等級的日志文件,配置格式與上面“設置記錄INFO和DEBUG日志等級的日志文件”相同,這裡不展開介紹。在此例子中,ERROR日志的日志文件名為D:/logs/backend/demo/web-error.log,歸檔文件名為(以2020-2-23為例)D:/logs/backend/demo/web-error/web-error-2020-2-23_1.log.gz–><rollingFile name="RollingFileError" fileName="${FILE_PATH}/weberror.log"immediateFlush="false"filePattern="${FILE_PATH}/web-error/web-error-%d{yyyy-MMdd}_%i.log.gz"><patternLayout pattern="${LOG_PATTERN}"/><filters><thresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/></filters><policies><timeBasedTriggeringPolicy interval="1"/><sizeBasedTriggeringPolicy size="20MB"/></policies><defaultRolloverStrategy max="15"/></rollingFile></appenders><!– 設置日志源,需要在這裡關聯日志輸出源(<appenders></appenders>)才能輸出到對應文件當中 –><loggers><!– 設置輸出日志等級,默認情況下,不會輸出比該日志等級低的日志。在此例中,隻輸出FATAL、ERROR、WARN、INFO的日志。日志級別以及優先級排序為OFF > FATAL > ERROR > WARN > INFO > DEBUG >TRACE > ALL,其中OFF是不輸出所有日志–><root level="info"><!– 關聯輸出源,其中ref中的值需要與<rollingFile></rollingFile>中的name對應 –><!– 在非調試環境下,需要關閉控制臺的日志輸出(去掉下面第一行就可以瞭) –><AppenderRef ref="Console" /><AppenderRef ref="RollingFileInfo" /><AppenderRef ref="RollingFileError" /></root></loggers></configuration>(4)引入日志配置文件。在後端應用程序配置文件中指定日志配置文件路徑後,才能生效。在如圖4.60所示的工程目錄結構中,需要在demo.properties文件中添加日志配置文件的路徑,如代碼4.34所示,其中,classpath:log4j2.xml為具體的路徑。代碼4.34 在後端應用程序的配置文件中添加日志配置文件的路徑#設置日志配置文件的路徑logging.config=classpath:log4j2.xml(5)程序中記錄日志,如代碼4.35所示。對應的日志輸出結果如代碼4.36所示,其中,由於log4j2.xml(日志配置文件)設置的日志輸出等級為INFO,所以DEBUG等級的日志沒有被記錄下來。說明:日志配置文件的相關說明請參照代碼4.33,其中,設置輸出日志等級的位置為<root level="info">…</root>。代碼4.35 代碼中記錄日志//需要引入的日志依賴類import org.apache.logging.log4j.LogManager;import org.apache.logging.log4j.Logger;public class XX{…//獲取日志對象private static final Logger LOGGER = LogManager.getLogger();…public void Function(){…//輸出INFO級別的日志LOGGER.info("info log test.");//輸出ERROR級別的日志LOGGER.error("error log test.");//輸出DEBUG級別的日志LOGGER.debug("debug log test.");…}…}代碼4.36 日志輸出結果[2020-04-20][17:46:17][INFO]info log test.[2020-04-20][17:46:17][ERROR]error log test.(6)如果實現瞭4.3.3小節中的後端應用程序與配置文件分離,那麼日志配置文件也應該從後端應用程序中分離出來。按常理來說,隻要在配置文件中設置日志配置文件路徑就可以瞭(如logging.config=/home/tomcat/appconfig/log4j2.xml),但是由於某種沖突,這樣的配置是不起作用的。因此,要想實現後端應用程序引用外部的日志配置文件,需要通過“添加啟動參數”和“修改代碼(ServletInitializer.java)”才能實現。具體做法是,“添加啟動參數”需要在Tomcat目錄下的/conf/catalina.properties文件中添加如代碼4.37所示的設置,修改後的代碼如代碼4.38所示,其中,xxx_log4j2.xml為日志配置文件名。最終,配置文件和日志配置文件集中管理的效果如圖4.61所示。圖4.61 配置文件和日志配置文件集中管理代碼4.37 設置日志配置文件所在目錄…#後端應用程序的外部配置文件所在目錄,詳見4.3.3節的介紹spring.config.location=${catalina.home}/appconfig/#新增代碼,設置日志配置文件所在目錄,一般與後端應用程序的外部配置文件目錄相同logging.config=${catalina.home}/appconfig/…代碼4.38 修改後的ServletInitializer.java文件…public class ServletInitializer extends SpringBootServletInitializer {@Overrideprotected SpringApplicationBuilder configure(SpringApplicationBuilderapplication) {//新增代碼,補全啟動參數(logging.config)的文件名String loggingConfig = System.getProperty("logging.config");if(!loggingConfig.isEmpty()) {System.setProperty("logging.config", loggingConfig+"xxx_log4j2.xml");}//設置後端應用程序的外部配置文件名,詳見4.3.3小節的介紹return application.properties("spring.config.name=xxx").sources(xxApplication.class);//初始工程的代碼,需要去除//return application.sources(xxApplication.class);}}2.規范化日志記錄日志記錄是一個十分開放的行為,原則上,隻要記錄的日志能“定位問題發生的位置”和“記錄某些重要的用戶操作”就可以瞭。但是,在實際項目當中,由於日志對功能的實現是不產生影響的,所以日志通常都是通過一次次問題修復而得到補充的。而這種“補丁型”日志,通常是混亂的。混亂的日志對於“定位問題發生的位置”和“追查某些重要的用戶操作”都不能起到很好的作用,因此,日志記錄需要規范化。註意:日志記錄的規則需要在項目前期定好,在開發過程中吸收規整化日志的工作量。項目後期再整理日志是很不理智的行為,因為後期整理日志需要花費大量的時間去梳理和理解業務代碼,而這部分工作量是很難預估且十分枯燥的,最後整理日志的結果往往也是不盡人意的。規范化日志記錄可以從限制日志等級、明確日志記錄位置、添加日志跟蹤碼等方面進行考慮。(1)限制日志等級。日志模塊一般都有等級劃分,以log4j2為例,其日志等級有6種,分別為TRACE(追蹤調試)、DEBUG(調試)、INFO(信息)、WARNING(警告)、ERROR(錯誤)和FATAL(致命錯誤)。每個日志等級看上去都有相對明確的分工和含義,但是在實際應用當中,這些日志等級的具體用途其實相當模糊,很多時候,都很難界定一個日志應該歸類為哪一個等級。一旦出現這種模糊規則,就會出現一人一個樣的做法,最後導致“五花八門”的日志等級劃分原則。因此,規整化日志需要限制日志等級。一般情況下,後端應用程序使用DEBUG、INFO和ERROR三個日志等級就足夠瞭,這三個日志等級的分工和協助如圖4.62所示。圖4.62 DEBUG、INFO和ERROR日志等級的分工和協助其中,DEBUG日志在運行時不生效,需要打開調試模式(修改日志輸出等級)後,才能記錄DEBUG日志。(2)明確日志記錄位置。在限制瞭日志等級後,需要解決“補丁式日志”的問題,避免日志不夠全面的情況。而解決“補丁式日志”的關鍵,是明確日志記錄位置。但是,明確日志記錄位置是一件很難實現的事情,因為接口程序與接口程序之間很難找到共性。不過,如果實現瞭4.3.2小節中介紹的“限制函數調用層級”“公共模塊”和“錯誤機制”,那麼接口程序會變成流水線式的處理方式。在流水線式的接口程序中明確日志記錄位置是相對容易的,如圖4.63所示。圖4.63 明確日志位置其中,每個接口隻需要在“接收請求”“數據庫操作”和“返回結果”這三部分添加日志就可以瞭,其餘日志都在公共模塊裡,而且公共模塊裡的日志是一次添加全局有效的。說明:Dao層(數據庫操作)其實也可以做成一個公共模塊,這樣可以省掉一些日志工作量。另外,雖然數據庫本身可以自動記錄日志,但是數據庫自身的日志不能包含用戶身份信息,即不能追溯用戶操作,所以Dao層(數據庫操作)的日志是有必要記錄的。(3)添加日志跟蹤碼。即使日志被記錄得十分詳細,分析日志也是一件很麻煩的事情。同一時刻,後端應用程序可能會同時處理多個請求,以至於多個請求的日志是混合在一起的,在不經過特殊處理的情況下,根本沒法分辨哪幾條日志是屬於同一個請求的。像這種無法區分請求的日志,被記錄下來也是浪費資源。因此,需要在每條日志中添加日志跟蹤碼,標記同一請求的日志。跟蹤碼的本質,就是同一請求輸出日志時,都多加一個相同的字符串。如果使用的是log4j2日志模塊,可以在不改變原有日志輸出代碼的前提下,添加日志跟蹤碼。首先,需要修改日志配置文件中的日志輸出格式,如代碼4.39所示,其中[%X{requestId}]為新增的跟蹤碼格式。代碼4.39 修改日志輸出格式…<!– 此段設置截取自代碼4.33的,單獨設置是不起作用的 –><!– 設置日志格式的變量:%d:獲取日期時間;%level:日志等級;%X{requestId}:跟蹤碼;%msg:日志消息,如ERROR、INFO、DEBUG等;%n:換行符–><property name="LOG_PATTERN"value="[%d{yyyy-MM-dd}][%d{HH:mm:ss}][%level][%X{requestId}]%msg%n" />…修改完日志配置文件之後,需要在每個接口程序中添加“生成跟蹤碼”的代碼,如代碼4.40所示,其中,代碼中的函數為Controller層中的接口的入口函數,requestId對應代碼4.39中的跟蹤碼標識。代碼4.40 添加“生成跟蹤碼”的代碼…@Controller…@RequestMapping(value="…",method = RequestMethod.POST)@ResponseBodypublic JSONObject XXX(@RequestBody String requestParam, HttpServletResponse response) {//在每個接口的入口函數都需要添加以下“生成跟蹤碼”代碼ThreadContext.put("requestId", UUID.randomUUID().toString());…}…修改日志跟蹤碼後,能清晰地識別不同請求的日志,日志輸出結果如代碼4.41所示,其中,62e3300c-e0a0-40cd-be80-4320d40ddc2c和00000000-0000-0000-0000-000000000000是日志追蹤碼。代碼4.41 添加“日志跟蹤碼”後的日志輸出結果[2020-04-20][17:46:17][INFO][62e3300c-e0a0-40cd-be80-4320d40ddc2c]infolog test_1.[2020-04-20][17:46:17][INFO][00000000-0000-0000-0000-000000000000]infolog test_1.[2020-04-20][17:46:17][INFO][00000000-0000-0000-0000-000000000000]infolog test_2.[2020-04-20][17:46:17][INFO][62e3300c-e0a0-40cd-be80-4320d40ddc2c]infolog test_2.[2020-04-20][17:46:17][INFO][62e3300c-e0a0-40cd-be80-4320d40ddc2c]infolog test_3.本文給大傢講解的內容是大型網站架構的技術細節:後端架構規整化java日志框架下篇文章給大傢講解的內容是大型網站架構的技術細節:後端架構規整化自研框架Once感謝大傢的支持!

本文出自快速备案,转载时请注明出处及相应链接。

本文永久链接: https://www.175ku.com/41593.html