引言:为什么 Go 中的日志记录不仅仅是 fmt.Println
日志记录是任何在生产环境中运行的应用程序的眼睛和耳朵。在 Go (Golang) 世界中,日志记录方法经历了重大演变:从通过 fmt.Println 简单输出到控制台,发展到能够为集中式日志收集系统(ELK、Grafana Loki、Datadog)生成 JSON 兼容记录的强大结构化日志记录器。
在本文中,我们将通过实践而非理论来探讨 Go 中日志记录的最佳实践。您将学习如何配置日志级别、使用结构化日志、避免“噪音”以及正确处理错误。所有代码示例都可在您的项目中直接使用。
1. 标准库 log:何时足够,何时不足
Go 提供了标准库中的内置包 log。它很简单,但有明显的局限性。
基本示例
package main
import ( "log" "os")
func main() { // 配置输出到文件 file, _ := os.OpenFile("app.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) defer file.Close() log.SetOutput(file)
log.Println("应用程序已启动") log.Printf("用户 %s 已授权", "admin") log.Fatal("严重错误:无法连接到数据库")}优点:
- 无需安装第三方依赖。
- 对于小型脚本或 CLI 工具使用简单。
缺点:
- 没有日志级别(只有 Print、Fatal、Panic)。
- 输出格式固定(日期、时间、消息)。
- 没有结构化字段(键值对)。
- 难以被日志收集系统解析。
结论: 对于生产服务、微服务架构或任何需要监控的应用程序,标准 log 都不合适。请转向结构化日志记录。
2. 使用 zerolog 进行结构化日志记录:现代标准
zerolog 库是 Go 中最流行且最快的日志记录器之一。它支持日志级别、默认 JSON 输出以及最小的内存分配。
安装
go get -u github.com/rs/zerolog/log配置和使用示例
package main
import ( "os" "time"
"github.com/rs/zerolog" "github.com/rs/zerolog/log")
func main() { // 配置时间格式 zerolog.TimeFieldFormat = time.RFC3339
// 输出到控制台并带颜色(用于开发) log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339})
// 对于生产环境 — JSON 输出到 stdout // log.Logger = zerolog.New(os.Stdout).With().Timestamp().Logger()
log.Info().Msg("服务器已在端口 8080 上启动") log.Debug().Str("module", "auth").Msg("用户尝试登录") log.Error().Err(err).Str("user_id", "12345").Msg("授权错误") log.Fatal().Int("attempt", 3).Msg("已超过尝试次数")}zerolog 中的日志级别
- Trace (
log.Trace()) — 用于深度调试。 - Debug (
log.Debug()) — 调试信息。 - Info (
log.Info()) — 常规事件。 - Warn (
log.Warn()) — 警告(非错误)。 - Error (
log.Error()) — 需要关注的错误。 - Fatal (
log.Fatal()) — 致命错误(终止程序)。 - Panic (
log.Panic()) — 带有堆栈输出的恐慌。
最佳实践: 在生产环境中,设置最小