log.Println自动加空格和换行,适合快速调试;log.Printf支持格式化输出、需手动换行,适合结构化日志;二者均写入os.Stderr。
两者都写入 os.Stderr,但语义和格式控制不同:log.Println 自动加空格、换行,适合快速调试;log.Printf 支持格式化字符串,类似 fmt.Printf,适合结构化输出。
常见错误是混用:比如用 log.Println("user_id:", id, "error:", err) 看似方便,但一旦 err 是 nil,输出会变成 user_id: 123 error: ,可读性差;而 log.Printf("user_id: %d, error: %v", id, err) 能明确控制 nil 的显示方式(默认为 ,也可提前判空)。
log.Println 足够轻量log.Printf
log.Printf 不自动换行,漏写 \n 会导致多条日志挤在同一行默认的 log 包不带这些信息,需通过 log.SetFlags 启用标志位。关键组合是:
log.SetFlags(log.LstdFlags | log.Lshortfile)
log.LstdFlags 包含日期、时间、微秒;log.Lshortfile 输出 file.go:23 形式,比 Llongfile 更紧凑。
容易忽略的点:
log.SetFlags 必须在首次调用 log.Print* 前设置,否则无效Ldate 和 Ltime —— LstdFlags 已包含它们,重复设置会导致时间重复打印Lshortfile 在交叉编译或使用 go run 时可能显示 command-line-arguments,这是正常行为,不影响运行时定位
能否把 log 输出重定向到文件而不是终端可以,核心是替换默认的输出目标:把 log.SetOutput 指向一个 *os.File。
典型做法:
file, err := os.OpenFile("app.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
log.Fatal(err)
}
log.SetOutput(file)
注意事项:
err,否则日志静默丢失,极难排查file.Close() —— log 包不负责关闭底层 Writerlumberjack 或自行封装log 内部有锁,但高并发下可能成瓶颈log.Fatal 本质是 log.Print + os.Exit(1),它不会触发 defer,也不会被 recover 捕获;panic 则会执行 defer 并可被 recover 拦截。
使用场景判断:
log.Fatal —— 明确表示“这个进程必须停”panic + recover,但需谨慎,Golang 官方更倾向显式错误返回log.Fatal 在 HTTP handler 中会导致整个服务退出,这是严重事故最常被忽略的一点:无论 log.Fatal 还是 log.Panic,它们调用的都是 log.Output,所以之前设置的 Flags 和 Output 依然生效 —— 日志内容仍会写入文件或带上时间戳。