原文:Structured Logging: The Best Friend You’ll Want When Things Go Wrongdocker
在這篇文章裏,咱們重點介紹結構化日誌。咱們討論是是什麼,爲何好,以及如何構建一個框架更好的與咱們當前基於Elastic stack
的日誌後端集成,使咱們更好,更高效記日誌。數據庫
結構化日誌是咱們竭力作的很大一部分,結構化日誌能讓咱們減小bug解決時間(MTTR),中斷時幫助開發人員更快地緩解問題。後端
日誌是包含有關係統中發生一些事件的幾行文本信息,而且起着幫助咱們瞭解後端正在發生的事情的重要做用。日誌一般放置於重要事件的代碼中(例如:成功操做某些數據庫,或者指派司機給乘客),或咱們感興趣留意的代碼中。架構
當有錯誤時,正常開發者作的第一件事情就是查看日誌——有點像瀏覽系統的歷史,而且找出發生了什麼。所以,在服務中斷、錯誤、構建失敗時,日誌成爲開發人員最好的朋友。併發
如今的日誌具備不一樣的格式和功能框架
Splunk
, Kibana
等)決定了日誌的樣式或者用他們能作些什麼。一些人可能比其餘人使用的更多。將此與過多可用的日誌庫結合起來,很容易讓開發人員懵逼,沒法決定使用什麼。此外,每一個庫都有本身的優缺點,所以討論可能很快變得主觀化和極端化——所以,爲你的程序選擇適當的庫和後端很是重要。異步
咱們在Grab中使用不一樣類型的日誌庫。然而,隨着需求的變化——咱們也發現咱們本身正在從新評估日誌策略。分佈式
Grab的Golang服務的數量持續增加。大多數服務使用syslog
鍵值格式的日誌,因爲簡單,而且容易讀寫,所以是服務端程序中最多見的格式。全部這些日誌多是少許的公共庫實現,不一樣的服務直接引用這些庫來使用。函數
咱們使用基於雲的SaaS供應商做爲這些日誌的前端,應用程序產出的日誌寫入文件中併發送給咱們的日誌供應商,從而能夠實時查看和查詢。很長一段時間裏使用的很是不錯,也無任何磕絆。
然而,隨着時間推移,咱們的日誌清單上升到了史無前例的等級,發現咱們本身正在從新審視而且從新評估如何記日誌。出現的一些問題:
INFO
這個問題不是在單個服務中,而是在全部服務都很廣泛。爲了緩解,有些服務對日誌抽樣,有些服務徹底刪除了日誌。後者會後患無窮,所以咱們必須改善日誌等級。
Elastic stack
是其中的一個。咱們的工程師確信咱們能夠管理咱們的日誌基礎架構並更好地管理成本——這致使了提議構建Elastic堆棧日誌集羣。 Elasticsearch比咱們當時的供應商強得多,並且咱們當前的庫不足以充分利用其功能,所以咱們須要在日誌中有更好的結構並輕鬆與Elastic堆棧集成的庫。如上所述,咱們知道怎麼記日誌會有些問題。爲了最好的解決問題,而且在不影響現有的架構和服務儘可能的解決問題,決定從頭啓動一個新庫。這個庫應該能解決已知的問題,也包含修改現有的庫沒法實現的功能。扼要重述,咱們想解決的:
調查結構化日誌。結構化日誌在全世界很是受歡迎,普遍被採用。容易的集成到咱們的Elastic stack中,也解決了咱們的不少痛點。
記住咱們以前的問題和需求,咱們用Golang新建了一個庫,有一下功能:
容許咱們在運行時從配置管理系統改變初始化的日誌等級——這是以前沒法作到和被鼓勵的。
如今,日誌等級更有實際意義。如今開發人員能夠用經常使用的WARN
或者INFO
部署,當出現問題時,僅更改配置就能更新日誌的等級到DEBUG
,而且調試時他們的服務能輸出更多的日誌。這也有助於咱們控制日誌成本。咱們支持和咱們的配置管理系統簡單容易低集成。
日誌天生是無結構化的,不像數據庫模式的死板或者自由格式的文本那樣無結構化。咱們 Elastic stack
後端主要基於帶有映射(像鬆散的模式)的索引(相似於表)。爲此,咱們須要用一致性結構的JSON輸出(例如,在相同JSON字段下不能輸出整數和字符串,由於這會致使Elasticsearch索引失敗)。另外,咱們意識到咱們的主要目標之一是控制日誌成本,由於幾乎每一個字段的結構和索引都沒有意義——只添加對咱們有用的結構。
爲了解決這些,咱們構建一個容許咱們肯定地爲日誌添加結構。這是創建在咱們能夠用特殊的字段名和類型添加鍵值對的架構之上。根據該模式生成代碼,並使用生成的代碼確保事物的一致的格式且不會中斷。咱們稱這種模式(鍵名和類型對的集合)爲Common Grab Log Schema (CGLS)。咱們僅向CGLS中添加結構是很重要的——CGLS中包含的全部內容在不一樣的字段中格式化,其餘內容在生成的JSON中的單個字段中格式化。這有助於保持咱們的結構一直而且易於使用Elastic stack
。
Grab-Kit
即插即用 咱們經過對Grab-Kit
內部支持進行初始化,使用簡單而且開箱即用,所以,開發者無需修改便可使用。此外,做爲總體的一部分,咱們基於追蹤中存在的請求ID添加了自動的日誌關聯,這確保了具備該跟蹤ID的特定請求生成全部日誌。
咱們主要的需求是構建一個有足夠的表現和一致性,以便更好的與Elastic stack
後端集成,在下游無需通過花哨的解析。所以,該日誌庫具備的表現力和可配置性足以容許任何日誌格式(咱們能夠對不一樣功能的用例寫不一樣的日誌格式,例如,開發設置中的可讀格式和產品設置中的輸出JSON
格式),默認是輸出JSON
格式。這確保了咱們能夠生成與Elastic stack兼容的日誌輸出,但仍然能夠針對不一樣的用例進行配置。
做爲日誌庫功能擴展的一部分,咱們須要足夠的可配置性,以便可以發送不一樣的日誌到有不同的設置的不一樣地方。例如,異步發送易讀的格式的FATAL
日誌到Slack
,同時將全部的經常使用日誌發送的咱們的Elastic stack
後端。該日誌庫包括支持將這些」核心「鏈接到任意可能的程度——確保這些日誌器被用在此類高度專業化的狀況。
開發者從一開始就看到了控制檯日誌,然而,有結構化的JSON
日誌通常認爲是產品的日誌,並且更易於搜索。爲了更好的在開發過程當中利用,而且讓開發者直接在Kibana
中看到他們的日誌,咱們提供了docker化版本的Kibana
,能夠在本地運行以接收結構化日誌。這可讓開發者直接使用結構化日誌而且在Kibana
中看到——就像生產環境中那樣。
這個日誌庫讓咱們用更好的方式打日誌。最顯而易見的影響是咱們能簡單的訪問日誌,可以使用更好的過濾和條件來更好的查詢。
有精確歷史記錄的事件讓在生產環境的系統中調試問題更容易——由於僅看到歷史記錄就能很快的猜想出錯誤緣由而且修復。爲此,結構化日誌庫在日誌器中添加了精確的寫入時的納秒時間戳。這與相似JSON結構化的格式結合讓根據這些字段排序全部的日誌成爲可能——所以咱們以他們發生的確切的順序看日誌——在日誌中實現因果順序。這是看起來的低調,可是使調試容易的強大功能。
如今你已經知道了咱們日誌策略背後的歷史和緣由,讓咱們總結下從中得到的福利。
一開始,有明肯定義和結構化(像JSON)的日誌有不少好處,包括但不只限於:
更好的根本緣由分析: 使用結構化日誌,咱們能夠提取和執行更強大的查詢,這對於非結構化的日誌是不可能的。開發人員能夠在查找與狀況相關的日誌時進行更多信息性查詢。不只於此,日誌關聯和因果順序對更好的理解分佈式日誌成爲可能。不像無結構的數據,咱們僅侷限於全文或者少數的日誌類型,結構化日誌達到了全新的水平。
更好的標準化:使用單一,定義明確,結構化的方式去記日誌使咱們的日誌標準化——這減小了經過日誌肯定系統發生的事件的認知,而且更易採用。而不是經過100種不一樣類型的日誌,而是隻有一種格式。這也是日誌庫的目標之一——經過Golang後端服務日誌庫的標準化使用。
咱們還得到額外的好處:
日誌中面向將來的一致性: 經過採用通用模式,確保咱們堅持使用相同的模式,即便明天咱們的日誌基礎架構改變——咱們作好將來的準備。咱們能夠簡單在咱們的日誌器中暴露一個函數,而不是手動地指定要記錄的內容。
開發中相似生產的日誌環境: docker化的Kibana使開發人員享受到與生產環境中的Kibana一樣的好處。這也更激勵開發認識使用Elastic stack
並探索它的功能,像基於日誌數據構建儀表盤,有更好的查看方式,等等。
但願你喜歡這篇文章並發現它的有用之處。歡迎提出意見和更正。