Go标准库log默认无时间戳,需调用log.SetFlags(log.LstdFlags)启用;输出目标由log.SetOutput控制,默认os.Stderr;log.Fatal直接退出不执行defer,log.Panic可被recover捕获;日志格式化须正确传参避免panic。

Go 标准库 log 足够轻量、开箱即用,但默认输出格式简陋、不带时间戳和级别标识,直接用于生产环境容易踩坑——别急着封装或换第三方库,先理清它怎么用、为什么这么用。
为什么 log.Println 输出没有时间戳?
因为 log 默认只启用 log.LstdFlags 以外的标志位。它默认只输出内容,不加任何前缀。
- 要显示日期和时间,必须显式调用
log.SetFlags(log.LstdFlags)或组合其他标志,比如log.Ldate | log.Ltime | log.Lshortfile -
log.LstdFlags等价于log.Ldate | log.Ltime,不含文件名/行号;加上log.Lshortfile才能定位到具体代码位置 - 标志设置是全局生效的,一旦
log.SetFlags(...)被调用,所有后续log.Print*调用都会受影响
如何让 log 输出到文件而不是终端?
标准库 log 的输出目标由 log.SetOutput 控制,默认是 os.Stderr。替换为文件句柄即可。
- 用
os.OpenFile以os.O_CREATE | os.O_WRONLY | os.O_APPEND模式打开日志文件 - 传给
log.SetOutput前,确保文件对象非 nil,否则后续日志会静默失败(无 panic,但也不输出) - 注意:
log不自动轮转、不并发安全写入多个文件,高并发场景下需自己加锁或改用io.MultiWriter同时写文件+控制台
file, err := os.OpenFile("app.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
log.Fatal(err) // 这里不能用 log.Printf,否则可能死循环
}
log.SetOutput(file)
log.Fatal 和 log.Panic 有什么实际区别?
它们都终止程序,但退出方式不同,影响错误排查路径。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
-
log.Fatal调用os.Exit(1),不执行 defer,不触发 panic 恢复机制 -
log.Panic先打印日志,再调用panic(...),可被recover()捕获(但一般不建议这么做) - 常见误用:在 init 函数中用
log.Fatal加载配置失败,会导致整个包初始化中断,且无法被上层捕获——此时应返回 error,由 main 控制流程
为什么不能在日志里直接拼接变量?
因为 log.Printf 是格式化函数,如果把变量直接塞进字符串,会丢失类型信息、引发意外格式化,尤其遇到 % 字符时会 panic。
- 错误写法:
log.Printf("user: %s, id: " + userID)—— 若userID含%d,会触发fmt解析错误 - 正确写法:
log.Printf("user: %s, id: %s", username, userID) - 更安全的做法:对不确定内容统一用
%v或%+v,避免因类型变化导致日志截断或 panic
真正麻烦的不是怎么打日志,而是日志是否可检索、时间是否准确、错误上下文是否完整。标准库 log 不提供结构化字段、不支持动态级别开关,这些得靠封装或换 zap/zerolog 补足——但在此之前,先确保你没在 log.SetFlags 后又忘了设 log.SetOutput,或者在 defer 里调了 log.Fatal 却以为它会等 defer 执行完。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










