Go标准库log仅支持前缀开关和输出重定向,不支持结构化日志;需用zap等专用库实现JSON、level等字段化功能。

Go log.SetFlags() 控制日志前缀和格式
Go 标准库 log 包本身不支持自定义字段(如 JSON、level 字段),但能通过 log.SetFlags() 控制时间、文件名、行号等前缀开关。默认只输出内容,没时间戳——这是新手最常忽略的“日志看起来没格式”的原因。
-
log.Ldate | log.Ltime:输出类似2024/05/12 14:23:56 message -
log.Lshortfile:显示main.go:23;log.Llongfile显示完整路径 - 多个标志用按位或组合:
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile) - 设为
0可完全禁用所有前缀,只留原始字符串
Go log.SetOutput() 重定向日志到文件或 stderr
默认输出到 os.Stderr,但容易和错误流混在一起。生产环境通常要写入文件,或区分 info/warn/error 流。注意:直接传 *os.File 是安全的,但别传已关闭的文件句柄,否则后续 log.Print() 会 panic 并报 write /dev/stderr: bad file descriptor。
- 写入文件:
log.SetOutput(newFile),记得用os.OpenFile(..., os.O_CREATE|os.O_APPEND|os.O_WRONLY) - 同时输出到控制台和文件?需封装
io.MultiWriter - 避免在 goroutine 中反复调用
SetOutput—— 它不是并发安全的,且会覆盖全局实例
为什么不要直接用标准 log 做结构化日志
当你需要 {"level":"info","msg":"user login","uid":1001} 这种格式时,log 包无能为力。它不解析内容,只拼接字符串前缀 + fmt.Sprint() 结果。强行用 log.Printf("{\"level\":\"info\",...}") 会导致转义混乱、无法统一字段顺序、缺失堆栈上下文。
- 推荐替代方案:
zap(高性能)、zerolog(零分配)、logrus(易上手) - 如果必须用标准库,可封装一个函数生成 JSON 字符串再交给
log.Print(),但无法享受结构化解析优势 - 注意:
log.SetPrefix()只加固定前缀(如[INFO]),不能动态插入变量
package main
import (
"log"
"os"
)
func main() {
// 启用日期、时间、短文件名
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
// 写入文件而非 stderr
f, _ := os.OpenFile("app.log", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
log.SetOutput(f)
defer f.Close()
log.Println("server started")
log.Printf("listening on %s", ":8080")
}
标准库 log 的格式能力仅限于前缀开关和输出目标,真要字段化、分级、采样、Hook,得换专用日志库——这点在项目初期就该明确,别等到日志查不出问题才回头重构。










