标准库log包不能直接用于框架,因其默认输出到os.stderr,不支持字段注入、结构化日志、多级输出或动态切换写入目标,且缺乏context绑定能力,无法透传request id等上下文信息,导致日志混杂、调试困难。

为什么标准 log 包不能直接用在框架里
Go 标准库的 log 包默认输出到 os.Stderr,不支持字段注入、结构化日志、多级输出或动态切换写入目标(比如同时写文件和 Kafka)。框架场景下,你没法靠 log.SetOutput() 或 log.SetFlags() 解决上下文透传(如 request ID)、采样控制、异步刷盘这些刚需。
更实际的问题是:一旦多个中间件或 handler 都调用 log.Printf,日志行会混在一起,无法区分来源;没有 context.Context 绑定能力,调试时根本找不到某条日志对应哪个请求。
用 zap 替代 log 时必须初始化一次全局 logger
zap 是目前 Go 生产环境最常用的高性能结构化日志库,但它不是开箱即用——你得在应用启动时就配置好,而不是每个包里都 zap.NewProduction()。重复初始化会导致资源泄漏(比如多个 zapcore.Core 同时写同一个文件)。
- 必须只调用一次
zap.New(...)或zap.NewProduction(),通常放在main()或框架初始化函数里 - 把返回的
*zap.Logger注入到 router、middleware、service 等各层,而不是用包级变量(否则测试难 mock) - 如果要用不同 level 或 sink(比如 debug 日志写文件,error 写 Sentry),得用
zap.WrapCore()+ 自定义zapcore.Core,而不是新建多个 logger 实例
示例:在 Gin 中注入 logger
router := gin.New()
router.Use(func(c *gin.Context) {
c.Set("logger", logger.With(
zap.String("request_id", uuid.New().String()),
zap.String("path", c.Request.URL.Path),
))
c.Next()
})
中间件里怎么安全地拿到并使用 logger
Gin/echo/fiber 这类框架的中间件生命周期短,但 logger 必须能随请求携带上下文字段(比如 user_id、trace_id),又不能每次新建一个 logger 实例(性能差、内存泄漏)。
- 别在中间件里调用
logger.With(...).Info(...)后丢弃——那只是临时 logger,字段不会自动继承到后续 handler - 正确做法是把带字段的 logger 存进
c.Request.Context()或框架上下文(如 Gin 的c.Set()),然后在 handler 里取出来复用 - 注意字段名别冲突:
zap.String("user_id", ...)和zap.String("user_id", ...)重复加会覆盖,不是合并 - 避免在循环里反复调用
.With(),尤其高 QPS 场景;优先用logger.With(...).Sugar()做一次性格式化
自定义 writer 要小心 goroutine 泄漏和 panic 捕获
当你需要把日志发到 HTTP 接口、Kafka 或数据库时,得实现 io.Writer。但直接用 http.Post 或 kafka.Producer.WriteMessages 在日志回调里同步调用,会阻塞主线程,还可能因网络超时拖垮整个服务。
- 必须用带 buffer 的 channel + 单独 goroutine 消费,比如
chan *zapcore.Entry,buffer 大小建议设为 1024 或根据吞吐压测调整 - goroutine 里要 recover panic,否则日志 writer 崩溃会导致整个 logger 不再输出(
zap默认静默失败) - 别在 writer 里做重试逻辑——重试应由下游服务保证,writer 只负责“尽力而为”,失败时打一条本地 error 日志即可
- 注意关闭时机:程序退出前要 drain channel 并 wait goroutine 结束,否则日志丢失
复杂点在于:字段序列化格式(JSON / Protobuf)、时间戳精度、采样率控制这些细节,往往比 writer 本身更影响稳定性。别为了“统一日志平台”强行把所有字段都发过去,先保核心字段(level、ts、msg、req_id、err)可用。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











