Go 的 log 包默认输出到 os.Stderr,是为兼容 Unix 工具链设计,确保错误和日志不干扰 stdout 的数据流;若日志未显示,常因被 IDE、容器或 shell 重定向掩盖,可用 go run main.go 2>err.log 验证。

Go 的 log 包默认输出为什么只到 stderr?
因为 Go 标准库的 log 包在初始化时默认使用 os.Stderr 作为输出目标,这是为兼容 Unix 工具链设计的——错误和日志理应不干扰 stdout 的数据流。如果你看到日志没出现在终端或文件里,大概率是它确实写到了 stderr,但被 IDE、容器日志采集器或 shell 重定向掩盖了。
- 用
go run main.go 2>err.log可验证是否真在 stderr 输出 - 在 VS Code 或 Goland 中,默认会合并 stdout/stderr,但某些远程调试场景下 stderr 可能被静默丢弃
- 若需强制输出到 stdout,直接调
log.SetOutput(os.Stdout)
如何让 log.Printf 带文件名和行号?
标准 log 包不自动包含源码位置,必须手动开启标志位。这不是格式化问题,而是底层 log.Lshortfile 或 log.Llongfile 控制输出前缀的行为。
- 调用
log.SetFlags(log.Lshortfile | log.LstdFlags)后,log.Printf("msg")会输出类似main.go:12: msg -
log.LstdFlags提供时间戳;log.Lshortfile是相对路径(如main.go),log.Llongfile是绝对路径(慎用,暴露开发机路径) - 注意:
SetFlags影响所有后续日志,包括log.Fatal和log.Panic
为什么 log.Println 换行不可控,而 log.Printf 更灵活?
log.Println 内部会自动在参数末尾加空格并换行,无法关闭;log.Printf 则完全交由格式字符串控制,包括是否换行、缩进、字段对齐。
- 想输出 JSON 片段?用
log.Printf(`{"level":"info","msg":"%s"}`, "hello")—— 不带换行,可拼接 - 要避免日志被截断(比如在某些日志聚合系统中),显式加
\n比依赖Println更可靠 -
log.Println对error类型会调用.Error()方法,但对自定义结构体可能输出空字符串;Printf("%+v", err)才能看到完整字段
用 log.New 创建多个日志器时,常见并发安全陷阱
log.Logger 实例本身是并发安全的,但很多人误以为“自己封装一个全局变量就万事大吉”,结果在中间件或 goroutine 中复用了同一输出 writer(比如 os.File),导致日志错乱或 panic。
立即学习“go语言免费学习笔记(深入)”;
- 不要把
os.OpenFile返回的*os.File直接传给多个log.New实例——文件描述符共享,write 竞态 - 正确做法:每个
log.Logger应搭配独立的io.Writer,或用线程安全的 wrapper(如sync.Mutex+bufio.Writer) - 更稳妥的选择:直接用
log.SetOutput配合io.MultiWriter分发到多个目标(如文件 + stdout),比手写多 logger 更少出错
log 包够轻量,但一旦需要分级、异步、上下文注入(request ID)、采样,就得换 zap 或 zerolog —— 而不是硬改 log.Printf 的参数。



















