解析Golang中的log包日志记录技巧 Go语言标准日志格式化输出

3次阅读

解析Golang中的log包日志记录技巧 Go语言标准日志格式化输出

log.printf 为什么输出没有时间戳?

go 标准 log 包默认不带时间戳,是因为它用的是最简配置——log.SetFlags(0) 的效果。你看到的纯文本输出,其实是 log.LstdFlags 没被启用。

  • 要加时间戳,必须显式调用 log.SetFlags(log.LstdFlags)(含日期、时间、文件名和行号)或组合使用,比如 log.Ldate | log.Ltime
  • 注意:这个设置是全局生效的,一旦在 init() 或 main 开头设了,所有后续 log.Print* 都会受影响
  • 常见错误:在某个函数里临时改 flags,但忘了恢复,导致其他包的日志格式错乱
  • 如果你用的是 log.New() 创建的自定义 logger,flags 要单独传给它,不会继承全局设置

如何让 log 输出到文件而不是终端?

标准 log 包本身不关心输出目标,只管写入 io.Writer。所以关键不是“怎么输出到文件”,而是“把文件句柄塞给 logger”。

  • os.OpenFile() 打开文件时,记得加 os.O_CREATE | os.O_APPEND | os.O_WRONLY,否则可能报 permission denied 或覆盖旧日志
  • 别直接用 os.Stdoutos.Stderr 做对比测试——它们是终端设备,行为和文件不同(比如缓冲策略)
  • 如果程序长期运行,要考虑日志轮转;标准 log 不支持,得自己封装或换 lumberjack 这类第三方 writer
  • 示例:
    f, _ := os.OpenFile("app.log", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) log.SetOutput(f)

log.Fatal 和 panic 有什么实际区别?

表面看都终止程序,但底层行为完全不同:前者是“记录后调用 os.Exit(1)”,后者是“触发运行时 panic 机制并打印”。

  • log.Fatal("msg") 等价于 log.Println("msg"); os.Exit(1),不会打印 goroutine 堆栈,也不会触发 defer(除非你在它前面手动写了)
  • panic("msg") 会打印完整调用栈,且会执行当前 goroutine 的 defer,适合开发期快速暴露逻辑错误
  • 线上服务慎用 log.Fatal:它会让整个进程退出,而很多服务期望的是单请求失败后继续服务
  • 如果你只是想中止当前 http handler,应该返回 Error 或用 http.Error,而不是 log.Fatal

为什么 log.printf(“%s”, nil) 会 panic?

因为 log.Printf 底层调用的是 fmt.Sprintf,而 fmt 包对 nilInterface{} 或指针类型有严格校验——不是所有 %v 都能安全吞掉 nil

立即学习go语言免费学习笔记(深入)”;

  • 具体错误是:panic: runtime error: invalid memory address or nil pointer dereference,通常发生在你传了个未初始化的 Struct 指针或 map
  • 解决办法不是加 if x != nil 判断,而是统一用 %v%+v,它们对 nil 是安全的;%s 只接受 String 类型,遇到 nil 就崩
  • 更稳妥的做法是在日志前做类型断言或空值检查,尤其当变量来自外部输入(如 json 解析结果)时
  • 示例:log.Printf("user: %+v", user)log.Printf("user: %s", user.Name) 更健壮

日志格式看似简单,但真正上线后,时间戳缺失、文件权限错误、panic 和 fatal 混用、以及对 nil 的盲目格式化,这几个点最容易在凌晨三点把你叫起来。

text=ZqhQzanResources