結構化日誌 出錯時你最想要的好朋友

2022-05-26 10:06:14 字數 3988 閱讀 3208

目錄支援不同格式的多寫

開發中類似生產環境的日誌

因果順序

但為什麼要結構化記日誌?

原文:structured logging: the best friend you』ll want when things go wrong

在這篇文章裡,我們重點介紹結構化日誌。我們討論是是什麼,為什麼好,以及如何構建乙個框架更好的與我們當前基於elastic stack的日誌後端整合,使我們更好,更高效記日誌。

結構化日誌是我們竭力做的很大一部分,結構化日誌能讓我們減少bug解決時間(mttr),中斷時幫助開發人員更快地緩解問題。

日誌是包含有關系統中發生一些事件的幾行文字資訊,並且起著幫助我們了解後端正在發生的事情的重要作用。日誌通常放置於重要事件的**中(例如:成功操作某些資料庫,或者指派司機給乘客),或我們感興趣留意的**中。

當有錯誤時,正常開發者做的第一件事情就是檢視日誌——有點像瀏覽系統的歷史,並且找出發生了什麼。因此,在服務中斷、錯誤、構建失敗時,日誌成為開發人員最好的朋友。

現在的日誌具有不同的格式和功能

將此與過多可用的日誌庫結合起來,很容易讓開發人員懵逼,無法決定使用什麼。此外,每個庫都有自己的優缺點,因此討論可能很快變得主觀化和極端化——因此,為你的程式選擇適當的庫和後端非常重要。

我們在grab中使用不同型別的日誌庫。然而,隨著需求的變化——我們也發現我們自己正在重新評估日誌策略。

grab的golang服務的數量持續增長。大多數服務使用syslog鍵值格式的日誌,由於簡單,並且容易讀寫,因此是服務端程式中最常見的格式。所有這些日誌可能是少量的公共庫實現,不同的服務直接引用這些庫來使用。

我們使用基於雲的saas**商作為這些日誌的前端,應用程式產出的日誌寫入檔案中並傳送給我們的日誌**商,從而可以實時檢視和查詢。很長一段時間裡使用的非常不錯,也無任何磕絆。

然而,隨著時間推移,我們的日誌清單上公升到了前所未有的等級,發現我們自己正在重新審視並且重新評估如何記日誌。出現的一些問題:

圖1:使用日誌等級

這個問題不是在單個服務中,而是在所有服務都很普遍。為了緩解,有些服務對日誌抽樣,有些服務完全刪除了日誌。後者會後患無窮,因此我們必須改善日誌等級

如果我們寫個日誌庫,必須要解決這些問題——並鼓勵使用最佳實踐

grab的關鍵路徑(單個訂單流程請求經過的服務數量)大小已經增長了。平均,單個訂單請求涉及的微服務——每乙個都不同。因此,我們大規模的運營時,很有必要對單個請求容易地檢視流經的所有的服務日誌——然而這不是我們的日誌庫自動完成的功能。因此,我們也想要更容易、更好的日誌關聯

日誌是某個時間點的事件。事件的順序給與我們系統發生了什麼的完整歷史。然而,我們golang服務的核心日誌庫沒有儲存日誌的產生時間(而是寫入時間)。這導致了在幾微妙內產生的日誌造成混亂。這不僅使開發者的生活更困難,而且幾乎無法準確的獲得系統的歷史事件。這就是我們想改進和啟用因果排序的日誌——是了解系統事件的關鍵一步。

如上所述,我們知道怎麼記日誌會有些問題。為了最好的解決問題,並且在不影響現有的架構和服務盡量的解決問題,決定從頭啟動乙個新庫。這個庫應該能解決已知的問題,也包含修改現有的庫無法實現的功能。扼要重述,我們想解決的:

調查結構化日誌。結構化日誌在全世界非常受歡迎,廣泛被採用。容易的整合到我們的elastic stack中,也解決了我們的很多痛點。

記住我們之前的問題和需求,我們用golang新建了乙個庫,有一下功能:

動態日誌等級

允許我們在執行時從配置管理系統改變初始化的日誌等級——這是之前無法做到和被鼓勵的。

現在,日誌等級更有實際意義。現在開發人員可以用常用的warn或者info部署,當出現問題時,僅更改配置就能更新日誌的等級到debug,並且除錯時他們的服務能輸出更多的日誌。這也有助於我們控制日誌成本。我們支援和我們的配置管理系統簡單容易低整合。

日誌結構一致性

日誌天生是無結構化的,不像資料庫模式的死板或者自由格式的文字那樣無結構化。我們elastic stack後端主要基於帶有對映(像鬆散的模式)的索引(類似於表)。為此,我們需要用一致性結構的json輸出(例如,在相同json欄位下不能輸出整數和字串,因為這會導致elasticsearch索引失敗)。另外,我們意識到我們的主要目標之一是控制日誌成本,因為幾乎每個欄位的結構和索引都沒有意義——只新增對我們有用的結構。

為了解決這些,我們構建乙個允許我們確定地為日誌新增結構。這是建立在我們可以用特殊的欄位名和型別新增鍵值對的架構之上。根據該模式生成**,並使用生成的**確保事物的一致的格式且不會中斷。我們稱這種模式(鍵名和型別對的集合)為common grab log schema (cgls)。我們僅向cgls中新增結構是很重要的——cgls中包含的所有內容在不同的字段中格式化,其他內容在生成的json中的單個欄位中格式化。這有助於保持我們的結構一直並且易於使用elastic stack

圖2:golang後端服務的通用抓取日誌架構概述

支援使用grab-kit即插即用

我們通過對grab-kit內部支援進行初始化,使用簡單並且開箱即用,因此,開發者無需修改即可使用。此外,作為整體的一部分,我們基於追蹤中存在的請求id新增了自動的日誌關聯,這確保了具有該跟蹤id的特定請求生成所有日誌。

可配置的日誌格式

我們主要的需求是構建乙個有足夠的表現和一致性,以便更好的與elastic stack後端整合,在下游無需經過花哨的解析。因此,該日誌庫具有的表現力和可配置性足以允許任何日誌格式(我們可以對不同功能的用例寫不同的日誌格式,例如,開發設定中的可讀格式和產品設定中的輸出json格式),預設是輸出json格式。這確保了我們可以生成與elastic stack相容的日誌輸出,但仍然可以針對不同的用例進行配置。

作為日誌庫功能擴充套件的一部分,我們需要足夠的可配置性,以便能夠傳送不同的日誌到有不一樣的設定的不同地方。例如,非同步傳送易讀的格式的fatal日誌到slack,同時將所有的常用日誌傳送的我們的elastic stack後端。該日誌庫包括支援將這些」核心「連線到任意可能的程度——確保這些日誌器被用在此類高度專業化的情況。

開發者從一開始就看到了控制台日誌,然而,有結構化的json日誌一般認為是產品的日誌,而且更易於搜尋。為了更好的在開發過程中利用,並且讓開發者直接在kibana中看到他們的日誌,我們提供了docker化版本的kibana,可以在本地執行以接收結構化日誌。這可以讓開發者直接使用結構化日誌並且在kibana中看到——就像生產環境中那樣。

這個日誌庫讓我們用更好的方式打日誌。最顯而易見的影響是我們能簡單的訪問日誌,能夠使用更好的過濾和條件來更好的查詢。

圖3:開發中類似生產環境的日誌有精確歷史記錄的事件讓在生產環境的系統中除錯問題更容易——因為僅看到歷史記錄就能很快的猜測出錯誤原因並且修復。為此,結構化日誌庫在日誌器中新增了精確的寫入時的納秒時間戳。這與類似json結構化的格式結合讓根據這些字段排序所有的日誌成為可能——因此我們以他們發生的確切的順序看日誌——在日誌中實現因果順序。這是看起來的低調,但是使除錯容易的強大功能。

圖4:使用y'all的因果順序日誌現在你已經知道了我們日誌策略背後的歷史和原因,讓我們總結下從中獲得的福利。

一開始,有明確定義和結構化(像json)的日誌有很多好處,包括但不僅限於:

我們還獲得額外的好處:

結構化 半結構化和非結構化資料

在實際應用中,我們會遇到各式各樣的資料庫如nosql非關聯式資料庫 memcached,redis,mangodb rdbms關聯式資料庫 oracle,mysql等 還有一些其它的資料庫如hbase,在這些資料庫中,又會出現結構化資料,非結構化資料,半結構化資料,下面列出各種資料型別 結構化資料 ...

結構化 半結構化和非結構化資料

在實際應用中,我們會遇到各式各樣的資料庫如nosql非關聯式資料庫 memcached,redis。mangodb rdbms關聯式資料庫 oracle,mysql等 另一些其他的資料庫如hbase,在這些資料庫中。又會出現結構化資料。非結構化資料。半結構化資料,以下列出各種資料型別 結構化資料 可...

結構化 半結構化和非結構化問題

結構化程度 是指對某一決策問題的決策過程 決策環境和規律,能否用明確的語言 數學的或邏輯學的 形式的或非形式的 定量的或定性的 給予說明或描述清晰程度或準確程度。按照決策問題的結構化程度不同把決策問題分成結構化問題 半結構化問題和非結構化問題三種型別。1 結構化決策問題 結構化決策問題相對比較簡單 ...