
log.Printf 不能直接替代 error 处理
Go 的 error 是值,不是异常;log.Printf 只是输出,不中断控制流,也不传递错误上下文。你写 log.Printf("failed: %v", err) 后继续用 data,程序大概率 panic 或返回脏数据。
- 必须先判断
err != nil,再决定是否记录、返回或重试 -
log.Printf适合辅助诊断,但不能代替return err或return fmt.Errorf("xxx: %w", err) - 如果用了
log.Fatal,进程会立刻退出——这在 HTTP handler 或 goroutine 中非常危险
用 log.New 配合 prefix 实现模块化日志前缀
标准库 log 默认输出无上下文,查问题时分不清是 database 还是 cache 出的错。用 log.New 创建带前缀的 logger 更轻量,比引入第三方更可控。
- 例如:
dbLog := log.New(os.Stderr, "[DB] ", log.LstdFlags|log.Lshortfile) - 前缀字符串(如
"[DB] ")要简短,避免日志行过长;log.Lshortfile能定位到具体文件行,但注意它有性能开销 - 别把
log.SetPrefix和log.SetFlags用在全局 logger 上——多个包并发修改会导致日志格式混乱
error 包装时用 %w 而非 %v 才能保留栈追踪
从 Go 1.13 开始,fmt.Errorf("xxx: %w", err) 是唯一能保留原始错误链的方式。用 %v 或字符串拼接会切断错误链,errors.Is 和 errors.As 全部失效。
- 正确:
return fmt.Errorf("reading config: %w", os.Open("config.yaml")) - 错误:
return fmt.Errorf("reading config: %v", err)—— 此时errors.Is(err, os.ErrNotExist)返回 false - 搭配
log使用时,建议先包装再记录:err = fmt.Errorf("service init: %w", err); log.Printf("error: %v", err)
log 输出到 io.MultiWriter 实现双写(stderr + file)
开发时想看控制台,上线又需要落盘,不用改代码逻辑,只需换掉 logger 的 io.Writer。
立即学习“go语言免费学习笔记(深入)”;
- 示例:
log.SetOutput(io.MultiWriter(os.Stderr, f)),其中f是已打开的*os.File - 注意文件句柄生命周期:如果
f在程序中途关闭,后续log写入会静默失败(不会 panic,但日志丢失) - 不要用
os.Stdout做日志输出——它和fmt.Println共享缓冲区,可能被重定向或截断
error、每个调用点都检查它,而不是靠日志“看着像处理过了”。










