log.Println自动加空格和换行,适合快速调试;log.Printf支持格式化输出、需手动换行,适合结构化日志;二者均写入os.Stderr。

log.Println 和 log.Printf 的区别在哪
两者都写入 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足够轻量 - 如果需统一字段顺序、避免空格歧义、或拼接 JSON 片段,必须用
log.Printf - 注意
log.Printf不自动换行,漏写\n会导致多条日志挤在同一行
如何让 log 输出带时间戳和文件位置
默认的 log 包不带这些信息,需通过 log.SetFlags 启用标志位。关键组合是:
log.SetFlags(log.LstdFlags | log.Lshortfile)
log.LstdFlags 包含日期、时间、微秒;log.Lshortfile 输出 file.go:23 形式,比 Llongfile 更紧凑。
立即学习“go语言免费学习笔记(深入)”;
容易忽略的点:
-
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包不负责关闭底层 Writer - 若需滚动日志(如按天切分),标准库不支持,得换
lumberjack或自行封装 - 多 goroutine 写同一文件安全,
log内部有锁,但高并发下可能成瓶颈
log.Fatal 和 panic 的实际差异是什么
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 依然生效 —— 日志内容仍会写入文件或带上时间戳。










