log.printf 不显示前缀是因为标准 log 包默认不启用时间、文件或级别标识,需用 log.setflags() 开启基础前缀,级别标记如 [info] 必须手动拼接或封装不同前缀的 logger 实例。

Log.Printf 为什么没显示前缀?
Go 标准库 log 包默认不带时间、文件名或级别前缀,Log.Printf 只是简单格式化输出。它本身不处理“级别”,也不自动加 [INFO] 这类标记——那是你得自己拼的。
常见错误现象:log.Printf("user login failed") 输出纯文本,看不出是哪条日志、什么时间、严重程度如何。
- 必须用
log.SetFlags()开启基础前缀(比如log.Ldate | log.Ltime | log.Lshortfile) - 级别标识(如
[WARN])只能手动写进字符串里,log包原生不支持Warnf/Infof等方法 - 如果调用了
log.SetPrefix(),它只影响每行开头的固定字符串(如"[MYAPP] "),和时间/文件等动态信息无关
如何模拟 Info/Warn/Error 级别输出?
标准 log 包没有分级函数,但你可以封装一层:用不同前缀 + 同一个 *log.Logger 实例,避免重复创建开销。
使用场景:不想引入第三方日志库(如 zap 或 logrus),又需要快速区分日志语义。
立即学习“go语言免费学习笔记(深入)”;
- 定义三个变量:
infoLog、warnLog、errorLog,都基于log.New(os.Stderr, "", log.Ldate|log.Ltime|log.Lshortfile) - 分别用
SetPrefix("[INFO] ")、SetPrefix("[WARN] ")、SetPrefix("[ERROR] ")设置前缀 - 注意:不要对每个日志调用都 new 一个 logger,否则
SetFlags和SetPrefix会互相覆盖
示例:
infoLog := log.New(os.Stderr, "[INFO] ", log.Ldate|log.Ltime|log.Lshortfile) warnLog := log.New(os.Stderr, "[WARN] ", log.Ldate|log.Ltime|log.Lshortfile) errorLog := log.New(os.Stderr, "[ERROR] ", log.Ldate|log.Ltime|log.Lshortfile)
log.SetOutput(os.Stdout) 后日志突然不见了?
这通常不是消失了,而是输出到了别的地方——比如被重定向、缓冲未刷新、或被上层进程截获。
性能 / 兼容性影响:标准 log 是同步写入的,SetOutput 换成文件或管道后,若目标不可写(如磁盘满、管道断开),会 panic;换成 os.Stdout 表面看安全,但某些环境(Docker、systemd)会把 stdout 当作非终端流,导致缓冲行为变化。
- 确保在
SetOutput后仍保留必要 flag,例如log.SetFlags(log.LstdFlags),否则时间/文件信息丢失 - 如果日志“消失”,先试
log.Println("test")看是否真没输出,再检查运行时环境是否重定向了 stdout/stderr - 生产环境慎用
os.Stdout;更稳妥的是os.Stderr,多数日志收集器默认捕获 stderr
自定义 prefix 与 Lshortfile 冲突怎么办?
log.Lshortfile 会在每条日志末尾自动加 file:line,而 SetPrefix 是加在最前面。两者不冲突,但视觉上可能混乱:比如 [INFO] main.go:12 user login failed,看起来像前缀和路径混在一起。
容易踩的坑:有人误以为 SetPrefix 能替代 Lshortfile,结果关掉 flag 后所有日志都失去位置信息,排查时抓瞎。
- 若要统一控制格式(比如把文件位置移到前面),只能放弃
Lshortfile,改用手动获取:runtime.Caller(1)提取文件名和行号 - 但这样会增加调用栈开销,且需自行处理错误(如
runtime.Caller返回 false) - 日常开发建议保留
Lshortfile,接受它的固定位置;真有强格式需求,再考虑封装或换日志库
复杂点在于:前缀是静态的,文件行号是动态的,标准包不提供中间插入点。你得在“够用”和“可控”之间选一个——大多数项目选前者。










