iT邦幫忙

2025 iThome 鐵人賽

DAY 4
0
Software Development

Go Clean Architecture API 開發全攻略系列 第 4

日誌系統設計:整合 slog 實現高效能的結構化日誌

  • 分享至 

  • xImage
  •  

為什麼需要日誌系統?

日誌系統是任何軟體應用程式中不可或缺的一環。它為開發者和維運人員提供了洞察應用程式內部運作的窗口。一個好的日誌系統能幫助我們:

  • 除錯與故障排除: 當應用程式出現問題時,日誌是追蹤問題根源最重要的線索。
  • 系統監控與警報: 透過分析日誌,我們可以即時監控系統的健康狀況,並在異常發生時觸發警報。
  • 安全審計: 記錄使用者登入、權限變更等敏感操作,有助於追蹤潛在的安全威脅。
  • 性能分析: 記錄請求的處理時間、資料庫查詢效率等資訊,幫助我們找出系統瓶頸並進行優化。

為什麼選擇 slog

slog 是 Go 語言內建的日誌套件,旨在提供高效能且易於使用的日誌記錄功能。與其他第三方日誌庫相比,slog 的優勢在於:

  • 高效能: slog 針對性能進行了優化,能夠在高併發的環境下保持良好的效能表現。
  • 簡單易用: slog 提供了簡潔的 API,讓開發者能夠輕鬆地進行日誌記錄。
  • 靈活的日誌格式: 支援多種日誌格式,包括 JSON 和純文本,方便不同場景的需求。
  • 豐富的 Context 信息: slog 能夠自動捕獲請求的 Context 信息,方便日誌的追蹤與分析。
  • 可擴展性: 支援自定義處理器和過濾器,方便根據需求進行擴展。

在官方尚未提供 slog 之前,第三方的 log 工具如 logrus、zap 等已經廣泛使用,
這些工具各有特點,但在性能和易用性上可能存在差異。選擇適合的日誌工具能夠有效提升開發效率和系統穩定性。
有官方支持的 slog,能夠更好地與 Go 生態系統整合,減少外部依賴帶來的風險。
這就是為什麼選擇 slog 而不是其他的第三方 log 工具。

定義 Logger Interface

// internal/logger/logger.go

// Logger defines the interface for logging with context awareness.
type Logger interface {
	With(ctx context.Context) Logger
	WithAdditionalFields(fields map[string]any) Logger

	Debug(ctx context.Context, msg string, args ...any)
	Info(ctx context.Context, msg string, args ...any)
	Warn(ctx context.Context, msg string, args ...any)
	Error(ctx context.Context, msg string, args ...any)
}

使用 slog 實作 Logger Interface

// internal/logger/slogger.go

type slogger struct {
	logger *slog.Logger
	ctx    context.Context
}

func NewSLogger(ctx context.Context, cfg config.LoggerConfig) Logger {
	return &slogger{
		logger: slog.New(getHandler(cfg)),
		ctx:    ctx,
	}
}

func (l *slogger) With(ctx context.Context) Logger {
	attrs := extractSlogAttributes(ctx)
	return &slogger{
		logger: l.logger.With(attrs...),
		ctx:    ctx,
	}
}

func (l *slogger) WithAdditionalFields(fields map[string]any) Logger {
	return &slogger{
		logger: l.logger.With(l.getAttrs(fields, nil)...),
		ctx:    l.ctx,
	}
}

...

整合

internal/application/application.go

type Application struct {
	Config *config.Config
	Logger logger.Logger
}

func New(cfg *config.Config) (*Application, error) {
	ctx := context.Background()
	logger := logger.NewSLogger(ctx, cfg.Logger)

	return &Application{
		Config: cfg,
		Logger: logger,
	}, nil
}

cmd/api/main.go

// 在 main fucntion 添加
    app, err := application.New(config)
    if err != nil {
        log.Fatalf("failed to create application: %v", err)
    }

使用範例

type Handler struct {
	Logger logger.Logger
}

func NewHandler(logger logger.Logger) *Handler {
	return &Handler{
		Logger: logger,
	}
}

func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	h.Logger.Info(ctx, "Received request", "method", r.Method, "url", r.URL)
	// Handle the request...
}

總結

在本篇文章中,我們探討了 Go 語言中的日誌系統設計問題,並介紹了 slog 作為解決方案。
透過 slog,我們能夠輕鬆地實現高效能的結構化日誌記錄,並在不同場景中保持一致性。
最終,我們實現了一個靈活的日誌系統,能夠支援上下文信息的捕獲與分析,提升了應用程式的可維護性與安全性。

在不斷添加資料夾跟檔案的當下,我們還沒有說明,
我們專案的 internel 資料夾底下的各個資料夾的分類用意
下一篇,我們來講講,專案架構的資料夾

以上程式碼的完整內容可以到 Github 觀看


上一篇
設定管理哲學:使用 Viper 與環境變數打造靈活的 Config
下一篇
目錄結構的藝術:一個清晰的 Go 專案應該長什麼樣子?
系列文
Go Clean Architecture API 開發全攻略5
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言