本文已發布超過一年。較舊的文章可能包含過時的內容。請檢查頁面中的資訊自發布以來是否已變得不正確。

Kubernetes 1.24 中的情境日誌記錄

結構化日誌工作組在 Kubernetes 1.24 中為日誌基礎架構新增了功能。這篇部落格文章說明開發人員如何利用這些功能使日誌輸出更有用,以及他們如何參與改進 Kubernetes。

結構化日誌

結構化日誌的目標是用具有明確語法的日誌條目取代 C 樣式格式化和產生的不透明日誌字串,以分別儲存訊息和參數,例如以 JSON 結構的形式儲存。

當結構化日誌呼叫使用傳統的 klog 文字輸出格式時,字串最初是以 \n 跳脫序列列印的,除非嵌入在結構中。對於結構,日誌條目仍然可以跨越多行,沒有明確的方法將日誌流拆分為個別條目

I1112 14:06:35.783529  328441 structured_logging.go:51] "using InfoS" longData={Name:long Data:Multiple
lines
with quite a bit
of text. internal:0}
I1112 14:06:35.783549  328441 structured_logging.go:52] "using InfoS with\nthe message across multiple lines" int=1 stringData="long: Multiple\nlines\nwith quite a bit\nof text." str="another value"

現在,<> 標記以及縮排被用來確保在行首的 klog 標頭處進行分割是可靠的,並且產生的輸出是人類可讀的

I1126 10:31:50.378204  121736 structured_logging.go:59] "using InfoS" longData=<
	{Name:long Data:Multiple
	lines
	with quite a bit
	of text. internal:0}
 >
I1126 10:31:50.378228  121736 structured_logging.go:60] "using InfoS with\nthe message across multiple lines" int=1 stringData=<
	long: Multiple
	lines
	with quite a bit
	of text.
 > str="another value"

請注意,日誌訊息本身是以引號列印的。它旨在成為識別日誌條目的固定字串,因此應避免在此處使用換行符。

在 Kubernetes 1.24 之前,kube-scheduler 中的某些日誌呼叫仍然對多行字串使用 klog.Info,以避免無法讀取的輸出。現在,所有日誌呼叫都已更新以支援結構化日誌。

上下文日誌

上下文日誌基於 go-logr API。 核心概念是程式庫由其呼叫者傳遞記錄器實例,並使用該實例進行記錄,而不是存取全域記錄器。 二進制檔案決定記錄實作,而不是程式庫。 go-logr API 圍繞結構化記錄設計,並支援將其他資訊附加到記錄器。

這啟用了其他使用案例

  • 呼叫者可以將其他資訊附加到記錄器

    當將此擴充的記錄器傳遞到函數中,並且函數使用它而不是全域記錄器時,其他資訊會包含在所有日誌條目中,而無需修改產生日誌條目的程式碼。 這在高度並行的應用程式中很有用,在這些應用程式中,由於來自不同操作的輸出交錯在一起,因此很難識別特定操作的所有日誌條目。

  • 執行單元測試時,日誌輸出可以與目前的測試關聯。 然後,當測試失敗時,go test 只會顯示失敗測試的日誌輸出。 預設情況下,該輸出也可能更詳細,因為它不會顯示成功的測試。 測試可以並行執行,而不會交錯其輸出。

上下文記錄的設計決策之一是允許將記錄器作為值附加到 context.Context。 由於記錄器封裝了呼叫的預期記錄的所有方面,因此它是上下文的一部分,而不僅僅是使用它。 一個實際的優點是,許多 API 已經有一個 ctx 參數,或者新增一個參數具有額外的優點,例如能夠擺脫函數內部的 context.TODO() 呼叫。

另一個決定是不破壞與 klog v2 的相容性

  • 在已設定上下文記錄的二進制檔案中使用傳統 klog 記錄呼叫的程式庫將透過二進制檔案選擇的記錄後端工作和記錄。 但是,此類日誌輸出將不包含其他資訊,並且在單元測試中無法很好地工作,因此應修改程式庫以支援上下文記錄。 結構化記錄的遷移指南已擴展到也涵蓋上下文記錄。

  • 當程式庫支援上下文記錄並從其上下文檢索記錄器時,它仍然可以在未初始化上下文記錄的二進制檔案中工作,因為它將獲得一個透過 klog 記錄的記錄器。

在 Kubernetes 1.24 中,上下文記錄是一項新的 alpha 功能,以 ContextualLogging 作為功能閘道。 停用(預設)時,用於上下文記錄的新 klog API 呼叫(見下文)將變為無操作,以避免效能或功能回歸。

尚無 Kubernetes 组件被转换。 Kubernetes 存储库中的示例程序演示了如何在二进制文件中启用上下文日志记录,以及输出如何取决于二进制文件的参数

$ cd $GOPATH/src/k8s.io/kubernetes/staging/src/k8s.io/component-base/logs/example/cmd/
$ go run . --help
...
      --feature-gates mapStringBool  A set of key=value pairs that describe feature gates for alpha/experimental features. Options are:
                                     AllAlpha=true|false (ALPHA - default=false)
                                     AllBeta=true|false (BETA - default=false)
                                     ContextualLogging=true|false (ALPHA - default=false)
$ go run . --feature-gates ContextualLogging=true
...
I0404 18:00:02.916429  451895 logger.go:94] "example/myname: runtime" foo="bar" duration="1m0s"
I0404 18:00:02.916447  451895 logger.go:95] "example: another runtime" foo="bar" duration="1m0s"

example 前缀和 foo="bar" 由记录 runtime 消息和 duration="1m0s" 值的函数的调用者添加。

klog 的示例代码包含一个示例,用于进行单元测试,其中包含每个测试的输出。

klog 增強功能

上下文日誌記錄 API

以下呼叫管理記錄器的查找

FromContext
context 參數,回退到全域記錄器
Background
全域回退,無意支援上下文日誌記錄
TODO
全域回退,但僅作為臨時解決方案,直到函數擴展為通過其參數接受記錄器
SetLoggerWithOptions
更改回退記錄器;當使用 ContextualLogger(true) 呼叫時,記錄器已準備好直接呼叫,在這種情況下,日誌記錄將在不通過 klog 的情況下完成

為了支援 Kubernetes 中的功能門機制,klog 具有相應 go-logr 呼叫的包裝器呼叫和一個控制其行為的全域布林值

在 Kubernetes 程式碼中使用這些函數是通過 linter 檢查強制執行的。 klog 上下文日誌記錄的預設設定是啟用該功能,因為它在 klog 中被認為是穩定的。 只有在 Kubernetes 二進制檔案中,該預設設定才會被覆蓋,並且(在某些二進制檔案中)通過 --feature-gate 參數進行控制。

ktesting 記錄器

新的 ktesting 包通過 testing.T 使用 klog 的文字輸出格式實現日誌記錄。 它具有用於檢測測試用例的單個 API 呼叫對命令行標誌的支援

klogr

klog/klogr 繼續得到支援,並且它的預設行為保持不變:它使用自己的自訂格式格式化結構化日誌條目,並通過 klog 列印結果。

但是,不建議使用此用法,因為該格式既不是機器可讀的(與 zapr 生成的真實 JSON 輸出相反,zapr 是 Kubernetes 使用的 go-logr 實現),也不是人類友好的(與 klog 文字格式相反)。

相反,應使用 WithFormat(FormatKlog) 建立 klogr 實例,它選擇 klog 文字格式。 具有相同結果的更簡單的構造方法是新的 klog.NewKlogr。 這是 klog 在未配置任何其他內容時作為回退返回的記錄器。

可重用輸出測試

許多 go-logr 實現具有非常相似的單元測試,他們在其中檢查某些日誌呼叫的結果。 如果開發人員不知道某些注意事項,例如在呼叫時會發生 panic 的 String 函數,則很可能缺少對此類注意事項的處理和單元測試。

klog.test 是一組可重用的測試用例,可以應用於 go-logr 實現。

輸出刷新

klog 過去常常在 init 期間無條件啟動一個 goroutine,該 goroutine 以硬編碼的時間間隔刷新緩衝資料。 現在,該 goroutine 僅按需啟動(即,當寫入帶有緩衝的文件時),並且可以使用 StopFlushDaemonStartFlushDaemon 進行控制。

當 go-logr 實現緩衝資料時,可以通過使用 FlushLogger 選項註冊記錄器,將該資料的刷新整合到 klog.Flush 中。

各種其他更改

有關所有其他增強功能的描述,請參見發行說明

logcheck

最初設計為結構化日誌呼叫的 linter,logcheck 工具已增強,以支援上下文日誌記錄和傳統 klog 日誌呼叫。這些增強的檢查已經在 Kubernetes 中發現錯誤,例如使用格式字串和參數呼叫 klog.Info 而不是 klog.Infof

它可以作為外掛程式包含在 golangci-lint 調用中,Kubernetes 現在就是這樣使用它,或者可以獨立調用。

我們正在 將該工具移至 一個新的儲存庫中,因為它實際上與 klog 無關,並且其發布應該被正確地追蹤和標記。

後續步驟

結構化日誌記錄 WG 始終在尋找新的貢獻者。從 C 樣式日誌記錄的遷移現在將一步到位地針對結構化、上下文日誌記錄,以減少總體的程式碼變動和 PR 數量。更改日誌呼叫是為 Kubernetes 做出貢獻的良好起點,也是了解不同領域程式碼的機會。