可視化全鏈路日志追蹤通用設計:問題排查從小時級降至5分鐘
可觀測性作為系統高可用的重要保障,已經成為系統建設中不可或缺的一環。然而隨著業務邏輯的日益復雜,傳統的ELK方案在日志搜集、篩選和分析等方面愈加耗時耗力,而分布式會話跟蹤方案雖然基于追蹤能力完善了日志的串聯,但更聚焦于調用鏈路,也難以直接應用于高效的業務追蹤。
一、背景
1、業務系統日益復雜
隨著互聯網產品的快速發展,不斷變化的商業環境和用戶訴求帶來了紛繁復雜的業務需求。業務系統需要支撐的業務場景越來越廣、涵蓋的業務邏輯越來越多,系統的復雜度也跟著快速提升。與此同時,由于微服務架構的演進,業務邏輯的實現往往需要依賴多個服務間的共同協作。總而言之,業務系統的日益復雜已經成為一種常態。
?2、業務追蹤面臨挑戰
業務系統往往面臨著多樣的日常客訴和突發問題,“業務追蹤”就成為了關鍵的應對手段。業務追蹤可以看做一次業務執行的現場還原過程,通過執行中的各種記錄還原出原始現場,可用于業務邏輯執行情況的分析和問題的定位,是整個系統建設中重要的一環。
目前在分布式場景下,業務追蹤的主流實現方式包括兩類,一類是基于日志的ELK方案,一類是基于單次請求調用的會話跟蹤方案。然而隨著業務邏輯的日益復雜,上述方案越來越不適用于當下的業務系統。
1)傳統的ELK方案
日志作為業務系統的必備能力,職責就是記錄程序運行期間發生的離散事件,并且在事后階段用于程序的行為分析,比如曾經調用過什么方法、操作過哪些數據等等。在分布式系統中,ELK技術棧已經成為日志收集和分析的通用解決方案。如下圖1所示,伴隨著業務邏輯的執行,業務日志會被打印,統一收集并存儲至Elasticsearch(下稱ES)。
圖1 業務系統ELK案例
傳統的ELK方案需要開發者在編寫代碼時盡可能全地打印日志,再通過關鍵字段從ES中搜集篩選出與業務邏輯相關的日志數據,進而拼湊出業務執行的現場信息。然而該方案存在如下的痛點:
- 日志搜集繁瑣:雖然ES提供了日志檢索的能力,但是日志數據往往是缺乏結構性的文本段,很難快速完整地搜集到全部相關的日志。
- 日志篩選困難:不同業務場景、業務邏輯之間存在重疊,重疊邏輯打印的業務日志可能相互干擾,難以從中篩選出正確的關聯日志。
- 日志分析耗時:搜集到的日志只是一條條離散的數據,只能閱讀代碼,再結合邏輯,由人工對日志進行串聯分析,盡可能地還原出現場。
綜上所述,隨著業務邏輯和系統復雜度的攀升,傳統的ELK方案在日志搜集、日志篩選和日志分析方面愈加的耗時耗力,很難快速實現對業務的追蹤。
2)分布式會話跟蹤方案
在分布式系統,尤其是微服務系統中,業務場景的某次請求往往需要經過多個服務、多個中間件、多臺機器的復雜鏈路處理才能完成。為了解決復雜鏈路排查困難的問題,“分布式會話跟蹤方案”誕生。該方案的理論知識由Google在2010年《Dapper》論文中發表,隨后Twitter開發出了一個開源版本Zipkin。
市面上的同類型框架幾乎都是以Google Dapper論文為基礎進行實現,整體大同小異,都是通過一個分布式全局唯一的id(即traceId),將分布在各個服務節點上的同一次請求串聯起來,還原調用關系、追蹤系統問題、分析調用數據、統計系統指標。分布式會話跟蹤,是一種會話級別的追蹤能力,如下圖2所示,單個分布式請求被還原成一條調用鏈路,從客戶端發起請求抵達系統的邊界開始,記錄請求流經的每一個服務,直到向客戶端返回響應為止。
圖2 一次典型的請求全過程(摘自《Dapper》)
分布式會話跟蹤的主要作用是分析分布式系統的調用行為,并不能很好地應用于業務邏輯的追蹤。下圖3是一個審核業務場景的追蹤案例,業務系統對外提供審核能力,待審對象的審核需要經過“初審”和“復審”兩個環節(兩個環節關聯相同的taskId),因此整個審核環節的執行調用了兩次審核接口。如圖左側所示,完整的審核場景涉及眾多“業務邏輯”的執行,而分布式會話跟蹤只是根據兩次RPC調用生成了右側的兩條調用鏈路,并沒有辦法準確地描述審核場景業務邏輯的執行,問題主要體現在以下幾個方面:
圖3 分布式會話跟蹤案例
- 無法同時追蹤多條調用鏈路
分布式會話跟蹤僅支持單個請求的調用追蹤,當業務場景包含了多個調用時,將生成多條調用鏈路;由于調用鏈路通過traceId串聯,不同鏈路之間相互獨立,因此給完整的業務追蹤增加了難度。例如當排查審核場景的業務問題時,由于初審和復審是不同的RPC請求,所以無法直接同時獲取到2條調用鏈路,通常需要額外存儲2個traceId的映射關系。
- 無法準確描述業務邏輯的全景
分布式會話跟蹤生成的調用鏈路,只包含單次請求的實際調用情況,部分未執行的調用以及本地邏輯無法體現在鏈路中,導致無法準確描述業務邏輯的全景。例如同樣是審核接口,初審鏈路1包含了服務b的調用,而復審鏈路2卻并沒有包含,這是因為審核場景中存在“判斷邏輯”,而該邏輯無法體現在調用鏈路中,還是需要人工結合代碼進行分析。
- 無法聚焦于當前業務系統的邏輯執行
分布式會話跟蹤覆蓋了單個請求流經的所有服務、組件、機器等等,不僅包含當前業務系統,還涉及了眾多的下游服務,當接口內部邏輯復雜時,調用鏈路的深度和復雜度都會明顯增加,而業務追蹤其實僅需要聚焦于當前業務系統的邏輯執行情況。例如審核場景生成的調用鏈路,就涉及了眾多下游服務的內部調用情況,反而給當前業務系統的問題排查增加了復雜度。
3)總結
傳統的ELK方案是一種滯后的業務追蹤,需要事后從大量離散的日志中搜集和篩選出需要的日志,并人工進行日志的串聯分析,其過程必然耗時耗力。而分布式會話跟蹤方案則是在調用執行的同時,實時地完成了鏈路的動態串聯,但由于是會話級別且僅關注于調用關系等問題,導致其無法很好地應用于業務追蹤。
因此,無論是傳統的ELK方案還是分布式會話跟蹤方案,都難以滿足日益復雜的業務追蹤需求。本文希望能夠實現聚焦于業務邏輯追蹤的高效解決方案,將業務執行的日志以業務鏈路為載體進行高效組織和串聯,并支持業務執行現場的還原和可視化查看,從而提升定位問題的效率,即可視化全鏈路日志追蹤。
下文將介紹可視化全鏈路日志追蹤的設計思路和通用方案,同時介紹新方案在大眾點評內容平臺的落地情況,旨在幫助有類似需求的業務系統開發需求的同學提供一些思路。
二、可視化全鏈路日志追蹤
?1、設計思路
可視化全鏈路日志追蹤考慮在前置階段,即業務執行的同時實現業務日志的高效組織和動態串聯,如下圖4所示,此時離散的日志數據將會根據業務邏輯進行組織,繪制出執行現場,從而可以實現高效的業務追蹤。
圖4 業務系統日志追蹤案例
新方案需要回答兩個關鍵問題:如何高效組織業務日志,以及如何動態串聯業務日志。下文將逐一進行回答。
1)如何高效組織業務日志?
為了實現高效的業務追蹤,首先需要準確完整地描述出業務邏輯,形成業務邏輯的全景圖,而業務追蹤其實就是通過執行時的日志數據,在全景圖中還原出業務執行的現場。
新方案對業務邏輯進行了抽象,定義出業務邏輯鏈路,下面還是以“審核業務場景”為例,來說明業務邏輯鏈路的抽象過程:
- 邏輯節點:業務系統的眾多邏輯可以按照業務功能進行拆分,形成一個個相互獨立的業務邏輯單元,即邏輯節點,可以是本地方法(如下圖5的“判斷邏輯”節點)也可以是RPC等遠程調用方法(如下圖5的“邏輯A”節點)。
- 邏輯鏈路:業務系統對外支撐著眾多的業務場景,每個業務場景對應一個完整的業務流程,可以抽象為由邏輯節點組合而成的邏輯鏈路,如下圖5中的邏輯鏈路就準確完整地描述了“審核業務場景”。
一次業務追蹤就是邏輯鏈路的某一次執行情況的還原,邏輯鏈路完整準確地描述了業務邏輯全景,同時作為載體可以實現業務日志的高效組織。
圖5 業務邏輯鏈路案例
2)如何動態串聯業務日志?
業務邏輯執行時的日志數據原本是離散存儲的,而此時需要實現的是,隨著業務邏輯的執行動態串聯各個邏輯節點的日志,進而還原出完整的業務邏輯執行現場。
由于邏輯節點之間、邏輯節點內部往往通過MQ或者RPC等進行交互,新方案可以采用分布式會話跟蹤提供的分布式參數透傳能力實現業務日志的動態串聯:
- 通過在執行線程和網絡通信中持續地透傳參數,實現在業務邏輯執行的同時,不中斷地傳遞鏈路和節點的標識,實現離散日志的染色。
- 基于標識,染色的離散日志會被動態串聯至正在執行的節點,逐漸匯聚出完整的邏輯鏈路,最終實現業務執行現場的高效組織和可視化展示。
與分布式會話跟蹤方案不同的是,當同時串聯多次分布式調用時,新方案需要結合業務邏輯選取一個公共id作為標識,例如圖5的審核場景涉及2次RPC調用,為了保證2次執行被串聯至同一條邏輯鏈路,此時結合審核業務場景,選擇初審和復審相同的“任務id”作為標識,完整地實現審核場景的邏輯鏈路串聯和執行現場還原。
?2、通用方案
明確日志的高效組織和動態串聯這兩個基本問題后,本文選取圖4業務系統中的“邏輯鏈路1”進行通用方案的詳細說明,方案可以拆解為以下步驟:
圖6 通用方案拆解
1)鏈路定義
“鏈路定義”的含義為:使用特定語言,靜態描述完整的邏輯鏈路,鏈路通常由多個邏輯節點,按照一定的業務規則組合而成,業務規則即各個邏輯節點之間存在的執行關系,包括串行、并行、條件分支。
DSL(Domain Specific Language)是為了解決某一類任務而專門設計的計算機語言,可以通過JSON或XML定義出一系列節點(邏輯節點)的組合關系(業務規則)。因此,本方案選擇使用DSL描述邏輯鏈路,實現邏輯鏈路從抽象定義到具體實現。
圖7 鏈路的抽象定義和具體實現
邏輯鏈路1-DSL
[
{
"nodeName": "A",
"nodeType": "rpc"
},
{
"nodeName": "Fork",
"nodeType": "fork",
"forkNodes": [
[
{
"nodeName": "B",
"nodeType": "rpc"
}
],
[
{
"nodeName": "C",
"nodeType": "local"
}
]
]
},
{
"nodeName": "Join",
"nodeType": "join",
"joinOnList": [
"B",
"C"
]
},
{
"nodeName": "D",
"nodeType": "decision",
"decisionCases": {
"true": [
{
"nodeName": "E",
"nodeType": "rpc"
}
]
},
"defaultCase": [
{
"nodeName": "F",
"nodeType": "rpc"
}
]
}
]
?2)鏈路染色
“鏈路染色”的含義為:在鏈路執行過程中,通過透傳串聯標識,明確具體是哪條鏈路在執行,執行到了哪個節點。
鏈路染色包括兩個步驟:
①步驟一
確定串聯標識,當邏輯鏈路開啟時,確定唯一標識,能夠明確后續待執行的鏈路和節點。
鏈路唯一標識 = 業務標識 + 場景標識 + 執行標識 (三個標識共同決定“某個業務場景下的某次執行”)
- 業務標識:賦予鏈路業務含義,例如“用戶id”、“活動id”等等。
- 場景標識:賦予鏈路場景含義,例如當前場景是“邏輯鏈路1”。
- 執行標識:賦予鏈路執行含義,例如只涉及單次調用時,可以直接選擇“traceId”;涉及多次調用時則,根據業務邏輯選取多次調用相同的“公共id”。
節點唯一標識 = 鏈路唯一標識 + 節點名稱 (兩個標識共同決定“某個業務場景下的某次執行中的某個邏輯節點”)
- 節點名稱:DSL中預設的節點唯一名稱,如“A”。
②步驟二
傳遞串聯標識,當邏輯鏈路執行時,在分布式的完整鏈路中透傳串聯標識,動態串聯鏈路中已執行的節點,實現鏈路的染色。例如在“邏輯鏈路1”中:
- 當“A”節點觸發執行,則開始在后續鏈路和節點中傳遞串聯標識,隨著業務流程的執行,逐步完成整個鏈路的染色。
- 當標識傳遞至“E”節點時,則表示“D”條件分支的判斷結果是“true”,同時動態地將“E”節點串聯至已執行的鏈路中。
3)鏈路上報
“鏈路上報”的含義為:在鏈路執行過程中,將日志以鏈路的組織形式進行上報,實現業務現場的準確保存。
圖8 鏈路上報圖示
如上圖8所示,上報的日志數據包括:節點日志和業務日志。其中節點日志的作用是繪制鏈路中的已執行節點,記錄了節點的開始、結束、輸入、輸出;業務日志的作用是展示鏈路節點具體業務邏輯的執行情況,記錄了任何對業務邏輯起到解釋作用的數據,包括與上下游交互的入參出參、復雜邏輯的中間變量、邏輯執行拋出的異常。
4)鏈路存儲
“鏈路存儲”的含義為:將鏈路執行中上報的日志落地存儲,并用于后續的“現場還原”。上報日志可以拆分為鏈路日志、節點日志和業務日志三類:
- 鏈路日志:鏈路單次執行中,從開始節點和結束節點的日志中提取的鏈路基本信息,包含鏈路類型、鏈路元信息、鏈路開始/結束時間等。
- 節點日志:鏈路單次執行中,已執行節點的基本信息,包含節點名稱、節點狀態、節點開始/結束時間等。
- 業務日志:鏈路單次執行中,已執行節點中的業務日志信息,包含日志級別、日志時間、日志數據等。
下圖就是鏈路存儲的存儲模型,包含了鏈路日志,節點日志,業務日志、鏈路元數據(配置數據),并且是如下圖9所示的樹狀結構,其中業務標識作為根節點,用于后續的鏈路查詢。
圖9 鏈路的樹狀存儲結構
三、大眾點評內容平臺實踐
?1、業務特點與挑戰
互聯網時代,內容為王。內容型平臺的核心打法就是搭建內容流水線,保障內容可持續、健康且有價值地流轉到內容消費者,并最終形成內容“生產→治理→消費→生產”的良性循環。
大眾點評和美團App擁有豐富多樣的內容,站內外業務方、合作方有著眾多的消費場景。對于內容流水線中的三方,分別有如下需求:
- 內容的生產方:希望生產的內容能在更多的渠道分發,收獲更多的流量,被消費者所喜愛。
- 內容的治理方:希望作為“防火墻”過濾出合法合規的內容,同時整合機器和人工能力,豐富內容屬性。
- 內容的消費方:希望獲得滿足其個性化需求的內容,能夠吸引其種草,或輔助其做出消費決策。
生產方的內容模型各異、所需處理手段各不相同,消費方對于內容也有著個性化的要求。如果由各個生產方和消費方單獨對接,內容模型異構、處理流程和輸出門檻各異的問題將帶來對接的高成本和低效率。在此背景下,點評內容平臺應運而生,作為內容流水線的“治理方”,承上啟下實現了內容的統一接入、統一處理和統一輸出:
圖10 點評內容平臺業務形態
- 統一接入:統一內容數據模型,對接不同的內容生產方,將異構的內容轉化為內容平臺通用的數據模型。
- 統一處理:統一處理能力建設,積累并完善通用的機器處理和人工運營能力,保證內容合法合規,屬性豐富。
- 統一輸出:統一輸出門檻建設,對接不同的內容消費方,為下游提供規范且滿足其個性化需求的內容數據。
如下圖11所示,是點評內容平臺的核心業務流程,每一條內容都會經過這個流程,最終決定在各個渠道下是否分發。
圖11 點評內容平臺業務流程
內容是否及時、準確經過內容平臺的處理,是內容生產方和消費方的核心關注,也是日常值班的主要客訴類型。而內容平臺的業務追蹤建設,主要面臨以下的困難與復雜性:
- 業務場景多:業務流程涉及多個不同的業務場景,且邏輯各異,例如實時接入、人工運營、分發重算等圖中列出的部分場景。
- 邏輯節點多:業務場景涉及眾多的邏輯節點,且不同內容類型節點各異,例如同樣是實時接入場景,筆記內容和直播內容在執行的邏輯節點上存在較大差異。
- 觸發執行多:業務場景會被多次觸發執行,且由于來源不同,邏輯也會存在差異,例如筆記內容被作者編輯、被系統審核等等后,都會觸發實時接入場景的重新執行。
點評內容平臺日均處理百萬條內容,涉及百萬次業務場景的執行、高達億級的邏輯節點的執行,而業務日志分散在不同的應用中,并且不同內容,不同場景,不同節點以及多次執行的日志混雜在一起,無論是日志的搜集還是現場的還原都相當繁瑣耗時,傳統的業務追蹤方案越來越不適用于內容平臺。
點評內容平臺亟需新的解決方案,實現高效的業務追蹤,因此我們進行了可視化全鏈路日志追蹤的建設,下面本文將介紹一下相關的實踐和成果。
?2、實踐與成果
1)實踐
點評內容平臺是一個復雜的業務系統,對外支撐著眾多的業務場景,通過對于業務場景的梳理和抽象,可以定義出實時接入、人工運營、任務導入、分發重算等多個業務邏輯鏈路。由于點評內容平臺涉及眾多的內部服務和下游依賴服務,每天支撐著大量的內容處理業務,伴隨著業務的執行將生成大量的日志數據,與此同時鏈路上報還需要對眾多的服務進行改造。因此在通用的全鏈路日志追蹤方案的基礎上,點評內容平臺進行了如下的具體實踐。
①支持大數據量日志的上報和存儲
點評內容平臺實現了圖12所示的日志上報架構,支持眾多服務統一的日志收集、處理和存儲,能夠很好地支撐大數據量下的日志追蹤建設。
圖12 點評內容平臺日志上報架構
日志收集:各應用服務通過機器上部署的log_agent收集異步上報的日志數據,并統一傳輸至Kafka通道中,此外針對少量不支持log_agent的服務,搭建了如圖所示的中轉應用。
日志解析:收集的日志通過Kafka接入到Flink中,統一進行解析和處理,根據日志類型對日志進行分類和聚合,解析為鏈路日志、節點日志和業務日志。
日志存儲:完成日志解析后,日志會按照樹狀的存儲模型進行落地存儲,結合存儲的需求分析以及各個存儲選項的特點,點評內容平臺最終選擇HBase作為存儲選型。
整體而言,log_agent + Kafka + Flink + HBase的日志上報和存儲架構能夠很好地支持復雜的業務系統,天然支持分布式場景下眾多應用的日志上報,同時適用于高流量的數據寫入。
②實現眾多后端服務的低成本改造
點評內容平臺實現了“自定義日志工具包”(即下圖13的TraceLogger工具包),屏蔽鏈路追蹤中的上報細節,實現眾多服務改造的成本最小化。TraceLogger工具包的功能包括:
- 模仿slf4j-api:工具包的實現在slf4j框架之上,并模仿slf4j-api對外提供相同的API,因此使用方無學習成本。
- 屏蔽內部細節,內部封裝一系列的鏈路日志上報邏輯,屏蔽染色等細節,降低使用方的開發成本。
a. 上報判斷:
判斷鏈路標識:無標識時,進行兜底的日志上報,防止日志丟失。
判斷上報方式:有標識時,支持日志和RPC中轉兩種上報方式。
b. 日志組裝:實現參數占位、異常堆棧輸出等功能,并將相關數據組裝為Trace對象,便于進行統一的收集和處理。
c. 異常上報:通過ErrorAPI主動上報異常,兼容原日志上報中ErrorAppender。
d. 日志上報:適配Log4j2日志框架實現最終的日志上報。
圖13 TraceLogger日志工具包
下面是TraceLogger工具包分別進行業務日志和節點日志上報的使用案例,整體的改造成本較低。
- 業務日志上報:無學習成本,基本無改造成本。?
// 替換前:原日志上報
LOGGER.error("update struct failed, param:{}", GsonUtils.toJson(structRequest), e);
// 替換后:全鏈路日志上報
TraceLogger.error("update struct failed, param:{}", GsonUtils.toJson(structRequest), e);
- 節點日志上報:支持API、AOP兩種上報方式,靈活且成本低。
public Response realTimeInputLink(long contentId) {
// 鏈路開始:傳遞串聯標識(業務標識 + 場景標識 + 執行標識)
TraceUtils.passLinkMark("contentId_type_uuid");
// ...
// 本地調用(API上報節點日志)
TraceUtils.reportNode("contentStore", contentId, StatusEnums.RUNNING)
contentStore(contentId);
TraceUtils.reportNode("contentStore", structResp, StatusEnums.COMPLETED)
// ...
// 遠程調用
Response processResp = picProcess(contentId);
// ...
}
// AOP上報節點日志
@TraceNode(nodeName="picProcess")
public Response picProcess(long contentId) {
// 圖片處理業務邏輯
// 業務日志數據上報
TraceLogger.warn("picProcess failed, contentId:{}", contentId);
}
?2)成果
基于上述實踐,點評內容平臺實現了可視化全鏈路日志追蹤,能夠一鍵追蹤任意一條內容所有業務場景的執行,并通過可視化的鏈路進行執行現場的還原,追蹤效果如下圖所示:
【鏈路查詢功能】:根據內容id實時查詢該內容所有的邏輯鏈路執行,覆蓋所有的業務場景。
圖14 鏈路查詢
【鏈路展示功能】:通過鏈路圖可視化展示業務邏輯的全景,同時展示各個節點的執行情況。
圖15 鏈路展示
【節點詳情查詢功能】:支持展示任意已執行節點的詳情,包括節點輸入、輸出,以及節點執行過程中的關鍵業務日志。
圖16 節點詳情
目前,可視化全鏈路日志追蹤系統已經成為點評內容平臺的“問題排查工具”,我們可以將問題排查耗時從小時級降低到5分鐘內;同時也是“測試輔助工具”,利用可視化的日志串聯和展示,明顯提升了RD自測、QA測試的效率。最后總結一下可視化全鏈路日志追蹤的優點:
- 接入成本低:DSL配置配合簡單的日志上報改造,即可快速接入。
- 追蹤范圍廣:任意一條內容的所有邏輯鏈路,均可被追蹤。
- 使用效率高:管理后臺支持鏈路和日志的可視化查詢展示,簡單快捷。
四、總結與展望
隨著分布式業務系統的日益復雜,可觀測性對于業務系統的穩定運行也愈發重要。作為大眾點評內容流水線中的復雜業務系統,為了保障內容流轉的穩定可靠,點評內容平臺落地了全鏈路的可觀測建設,在日志(Logging)、指標(Metrics)和追蹤(Tracing)的三個具體方向上都進行了一定的探索和建設。
其中之一就是本文的“可視化全鏈路日志追蹤”,結合日志(Logging)與追蹤(Tracing),我們提出了一套新的業務追蹤通用方案,通過在業務執行階段,結合完整的業務邏輯動態完成日志的組織串聯,替代了傳統方案低效且滯后的人工日志串聯,最終可以實現業務全流程的高效追蹤以及業務問題的高效定位。此外,在指標(Metrics)方向上,點評內容平臺實踐落地了“可視化全鏈路指標監控”,支持實時、多維度地展示業務系統的關鍵業務和技術指標,同時支持相應的告警和異常歸因能力,實現了對業務系統整體運行狀況的有效把控。
未來,點評內容平臺會持續深耕,實現覆蓋告警、概況、排錯和剖析等功能的可觀測體系,持續沉淀和輸出相關的通用方案,希望可以為業務系統(特別是復雜的業務系統),提供一些可觀測性建設的借鑒和啟發。