文件
一個 專案

記錄檔運作方式

Caddy 具有強大且彈性的記錄功能,但可能與您習慣的不同,尤其是當您來自更陳舊的共享主機或其他舊式網頁伺服器時。

概觀

記錄檔有兩個主要方面:發射和消費。

發射 意指產生訊息。它包含三個步驟

  1. 收集相關資訊(上下文)
  2. 建立有用的表示形式(編碼)
  3. 將該表示形式發送到輸出(寫入)

此功能已內建於 Caddy 的核心中,使 Caddy 程式碼庫或模組(外掛程式)的任何部分都能發射記錄檔。

消費 是訊息的接收與處理。為了發揮作用,發射的記錄檔必須被消費。僅僅寫入但從未讀取的記錄檔沒有任何價值。消費記錄檔可以簡單到管理員讀取主控台輸出,也可以進階到附加記錄檔彙總工具或雲端服務來篩選、計數和索引記錄檔訊息。

Caddy 的角色

Caddy 是一個記錄檔發射器。它不消費記錄檔,除了編碼和寫入記錄檔所需的最低限度處理。這很重要,因為它保持 Caddy 的核心更簡單,從而減少錯誤和邊緣案例,同時減輕維護負擔。最終,記錄檔處理超出 Caddy 核心的範圍。

但是,始終存在開發消費記錄檔的 Caddy 應用程式模組的可能性。(據我們所知,它目前還不存在。)

結構化記錄檔

與大多數現代應用程式一樣,Caddy 的記錄檔是結構化的。這表示訊息中的資訊不只是一個不透明的字串或位元組切片。相反,資料保持強型別,並按個別欄位名稱鍵控,直到需要編碼訊息並將其寫出。

比較傳統的非結構化記錄檔,例如傳統 HTTP 伺服器常用的陳舊通用記錄檔格式 (CLF)

127.0.0.1 - - [10/Oct/2000:13:55:36 -0700] "GET /apache_pb.gif HTTP/1.1" 200 2326

此格式「具有結構」,但不是「結構化」的:它只能用於記錄 HTTP 請求。沒有(有效率的)方法可以不同地編碼它,因為它是一個不透明的位元組字串。它也遺失了很多資訊。它甚至不包含請求的 Host 標頭!此記錄檔格式僅在託管單一網站時有用,並且僅適用於取得有關請求的最基本資訊。

現在比較來自 Caddy 的等效結構化記錄檔訊息,編碼為 JSON 並為了顯示而良好格式化

{
	"level": "info",
	"ts": 1646861401.5241024,
	"logger": "http.log.access",
	"msg": "handled request",
	"request": {
		"remote_ip": "127.0.0.1",
		"remote_port": "41342",
		"client_ip": "127.0.0.1",
		"proto": "HTTP/2.0",
		"method": "GET",
		"host": "localhost",
		"uri": "/",
		"headers": {
			"User-Agent": ["curl/7.82.0"],
			"Accept": ["*/*"],
			"Accept-Encoding": ["gzip, deflate, br"],
		},
		"tls": {
			"resumed": false,
			"version": 772,
			"cipher_suite": 4865,
			"proto": "h2",
			"server_name": "example.com"
		}
	},
	"bytes_read": 0,
	"user_id": "",
	"duration": 0.000929675,
	"size": 10900,
	"status": 200,
	"resp_headers": {
		"Server": ["Caddy"],
		"Content-Encoding": ["gzip"],
		"Content-Type": ["text/html; charset=utf-8"],
		"Vary": ["Accept-Encoding"]
	}
}

您可以看到結構化記錄檔更有用,並且包含更多資訊。此記錄檔訊息中豐富的資訊不僅有用,而且幾乎沒有效能開銷:Caddy 的記錄檔是零分配的。結構化記錄檔對資料類型或上下文沒有限制:它們可以用於任何程式碼路徑,並包含任何種類的資訊。

由於記錄檔是結構化且強型別的,因此可以將其編碼為任何格式。因此,如果您不想使用 JSON,則可以將記錄檔編碼為任何其他表示形式。Caddy 透過 記錄檔編碼器模組 支援其他格式,甚至可以新增更多格式。

在結構化記錄檔和舊式格式之間的區別中,最重要的是,結構化記錄檔可以以效能損失為代價 轉換為舊式通用記錄檔格式 ,但反之則不然。從 CLF 轉換為結構化格式並非易事(或至少效率低下),並且考慮到資訊的缺乏,這是不可能的。

本質上,有效率的結構化記錄通常提倡以下理念

  • 記錄檔太多比太少好
  • 篩選比丟棄好
  • 延遲編碼以獲得更大的彈性和互通性

發射

在程式碼中,記錄檔發射類似於以下內容

logger.Debug("proxy roundtrip",
	zap.String("upstream", di.Upstream.String()),
	zap.Object("request", caddyhttp.LoggableHTTPRequest{Request: req}),
	zap.Object("headers", caddyhttp.LoggableHTTPHeader(res.Header)),
	zap.Duration("duration", duration),
	zap.Int("status", res.StatusCode),
)

您可以看到這個函數呼叫包含記錄層級、訊息和幾個資料欄位。所有這些都是強型別的,並且 Caddy 使用零分配記錄程式庫,因此記錄檔發射快速有效率,幾乎沒有開銷。

logger 變數是一個 zap.Logger,可能具有與之關聯的任何上下文量,其中包括名稱和資料欄位。這允許記錄器從父上下文「繼承」,從而實現進階追蹤和指標。

從那裡,訊息會透過高效的處理管道發送,在其中進行編碼和寫入。

記錄管道

如您在上面看到的,訊息由 記錄器 發射。然後將訊息發送到 記錄檔 以進行處理。

Caddy 允許您 配置多個記錄檔,這些記錄檔可以處理訊息。記錄檔包含編碼器、寫入器、最小層級、取樣率以及要包含或排除的記錄器清單。在 Caddy 中,始終有一個名為 default 的預設記錄檔。您可以透過在 此物件 中的設定中指定鍵為 "default" 的記錄檔來自訂它。

  • 編碼器: 記錄檔的格式。將記憶體中的資料表示形式轉換為位元組切片。編碼器可以存取記錄檔訊息的所有欄位。
  • 寫入器: 記錄檔輸出。可以是任何記錄檔寫入器模組,例如到檔案或網路連線端。它只是寫入位元組。
  • 層級: 記錄檔具有從 DEBUG 到 FATAL 的各種層級。低於指定層級的訊息將被記錄檔忽略。
  • 取樣: 極其熱門的路徑可能會發射比有效處理更多的記錄檔;啟用取樣是一種減少負載的方法,同時仍產生有代表性的訊息樣本。
  • 包含/排除: 每個訊息都由記錄器發射,該記錄器具有名稱(通常源自模組 ID)。記錄檔可以包含或排除來自特定記錄器的訊息。

當從 Caddy 發射記錄檔訊息時

  • 會針對每個記錄檔的包含/排除清單檢查原始記錄器的名稱;如果包含(或未排除),則將其納入該記錄檔。
  • 如果啟用取樣,快速計算會判斷是否保留記錄檔訊息。
  • 訊息使用記錄檔的已配置編碼器進行編碼。
  • 然後將編碼的位元組寫入記錄檔的已配置寫入器。

預設情況下,所有訊息都傳送到所有已配置的記錄檔。這符合上述結構化記錄檔的值。您可以透過設定其包含/排除清單來限制哪些訊息傳送到哪些記錄檔,但這主要是為了篩選來自不同模組的訊息;它並非旨在像記錄檔彙總服務一樣使用。為了保持 Caddy 的記錄管道精簡且有效率,記錄檔訊息的進階處理會延遲到消費。

消費

在訊息發送到輸出後,消費者將讀取它們、解析它們並相應地處理它們。

這是一個與發射記錄檔非常不同的問題領域,Caddy 的核心不處理消費(儘管 Caddy 應用程式模組當然可以)。您可以使用許多工具來處理 JSON 訊息(或其他格式)串流,並檢視、篩選、索引和查詢記錄檔。您甚至可以編寫或實作自己的工具。

例如,如果您執行需要 CLF 的舊版軟體,該 CLF 基於特定欄位(例如主機名稱)分隔到不同的檔案中,則可以使用或編寫一個簡單的工具,該工具讀取 JSON,呼叫 sprintf() 以建立 CLF 字串,然後根據 request.host 欄位中的值將其寫入檔案。

Caddy 的記錄功能也可用於實作指標和追蹤:指標基本上是計算具有特定特徵的訊息,而追蹤則根據訊息之間的共性將多個訊息連結在一起。

您可以透過消費 Caddy 的記錄檔來做無數的事情!