log/slog 是 Go 標準庫提供的結構化日誌 package。它的核心用途是把 log 寫成「訊息 + key-value 欄位」,讓人類能讀,也讓工具能搜尋、過濾與聚合。

本章目標

學完本章後,你將能夠:

  1. 建立 text 或 JSON logger
  2. 使用 log level 區分訊號重要性
  3. 用 key-value 欄位保存可查詢資訊
  4. 設計穩定的 log 欄位名稱
  5. 避免把所有資訊塞進自由文字

【觀察】結構化日誌把資訊放進欄位

結構化日誌的核心規則是:穩定資訊放欄位,敘述文字只描述事件。以下範例記錄一筆 user 建立事件:

1logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
2
3logger.Info(
4    "user created",
5    "userID", "u_1",
6    "email", "alice@example.com",
7)

JSON handler 會輸出類似:

1{
2  "time": "2026-04-22T10:00:00Z",
3  "level": "INFO",
4  "msg": "user created",
5  "userID": "u_1",
6  "email": "alice@example.com"
7}

這比 fmt.Printf("user u_1 alice@example.com created") 更容易被查詢。

【判讀】level 表示事件嚴重度

log level 的核心規則是:level 表示事件需要多少注意力,不表示程式碼所在位置。

level適用情境
Debug開發或診斷細節
Info正常但重要的狀態變化
Warn可恢復但需要注意的異常
Error操作失敗或需要處理的錯誤

範例:

1logger.Debug("cache miss", "key", key)
2logger.Info("server started", "addr", addr)
3logger.Warn("queue full", "dropped", count)
4logger.Error("write file failed", "path", path, "error", err)

Error log 應該包含 error 欄位,讓讀者知道失敗原因。

【策略】欄位名稱要穩定

log 欄位設計的核心規則是:同一個概念使用同一個欄位名稱,不要在不同地方混用別名。

概念建議欄位
使用者 IDuserID
request IDrequestID
工作 IDjobID
元件名稱component
錯誤error

不要這樣混用:

1logger.Info("job queued", "id", job.ID)
2logger.Info("job started", "job_id", job.ID)
3logger.Info("job done", "jobID", job.ID)

應該統一:

1logger.Info("job queued", "jobID", job.ID)
2logger.Info("job started", "jobID", job.ID)
3logger.Info("job done", "jobID", job.ID)

欄位穩定後,grep、log query、dashboard 才能可靠。

【執行】建立帶預設欄位的 logger

預設欄位的核心規則是:每筆 log 都需要的上下文,應該掛在 logger 上,而不是每次手動重複。

 1base := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
 2    Level: slog.LevelInfo,
 3}))
 4
 5logger := base.With(
 6    "component", "worker",
 7    "version", "1.0.0",
 8)
 9
10logger.Info("job started", "jobID", "j_1")
11logger.Info("job finished", "jobID", "j_1")

With 會回傳帶有固定欄位的新 logger。這適合 component、version、requestID 這類上下文。

設計檢查

把所有資訊塞進 msg

不佳:

1logger.Info("job j_1 for user u_1 started")

較佳:

1logger.Info("job started", "jobID", "j_1", "userID", "u_1")

第二種寫法可以直接查 jobID=j_1userID=u_1

欄位名稱不穩定

欄位名稱不穩定會讓查詢失效。選定 userID 就一路使用 userID,不要混用 uiduser_iduser

忽略敏感資訊

log 會被保存與轉發。密碼、token、完整信用卡號等敏感資訊不應寫入 log。

延伸閱讀

本章只介紹標準庫 log/slog 的基本用法。服務開始有 domain event、state repository 或查詢需求時,可以接著閱讀 如何新增結構化記錄欄位;進入生產操作後,再閱讀 Go 進階:結構化日誌欄位設計Observability pipeline、metrics 與 tracing