引言
日志记录不仅仅是把事件写入文件。对于在高负载生产环境中运行的Go应用程序,日志是诊断信息的主要来源。没有高质量的日志,调试就变成了猜测,系统监控也变得不可能。
Go为开发者提供了灵活的工具:从内置的log包到强大的第三方库。在本文中,我们将分析Go日志记录的最佳实践,学习如何结构化记录、选择日志级别,并避免典型错误。无论你是初学者还是希望组织日志记录方法的有经验开发者,本文都会对你有所帮助。
标准日志库:基本能力与限制
Go标准库中的log包是开始日志记录最简单的方式。它提供了一个现成的日志记录器,默认写入os.Stderr。
简单使用示例
package main
import ( "log")
func main() { log.Println("Application started") log.Printf("User %s logged in", "admin") log.Fatal("Critical error: database unavailable")}输出看起来像这样:
2023/10/27 14:35:12 Application started2023/10/27 14:35:12 User admin logged in2023/10/27 14:35:12 Critical error: database unavailable标准日志记录器的限制
- 缺乏日志级别 — 没有Info、Warn、Error的区分。所有消息都写入同一个流。
- 结构化差 — 记录是简单的字符串,难以自动解析。
- 缺乏上下文 — 很难添加请求或用户标识符。
- 性能问题 — 标准日志记录器在写入时会阻塞goroutine,这可能成为瓶颈。
对于小型工具或原型,log完全适用。但对于生产应用程序,值得考虑替代方案。
结构化日志:为什么重要?
结构化日志是一种方法,每条消息代表一组键值对(通常是JSON格式)。这使得:
- 在日志收集系统(ELK、Splunk、Grafana Loki)中自动解析日志。
- 通过任意属性(级别、服务、请求ID)过滤记录。
- 基于数字字段构建仪表盘和警报。
流行的结构化日志库
- logrus — 最早且最流行的库之一。提供级别、格式化(JSON、文本)以及用于将日志发送到外部系统的钩子。不过,它内存消耗较高。
- zerolog — 专注于性能的库(零分配)。非常适合微服务和高负载系统。
- zap — 来自Uber。非常快,有两种模式:SugaredLogger(方便)和Logger(最大速度)。
- slog — Go 1.21中新增的标准包。开箱即用提供结构化日志。
使用slog的示例(Go 1.21+)
package main
import ( "log/slog" "os")
func main() { logger := slog.New(slog.NewJSONHandler(os.Stdout, nil)) logger.Info("Server started", "port", 8080, "env", "production") logger.Error("Database connection error", "db", "postgres", "timeout", 5)}JSON输出:
{"time":"2023-10-27T14:40:00.123Z","level":"INFO","msg":"Server started","port":8080,"env":"production"}{"time":"2023-10-27T14:40:01.456Z","level":"ERROR","msg":"Database connection error","db":"postgres","t