log.Println 和 log.Printf 的换行行为不同:前者自动加换行符,后者严格按格式输出,需显式添加\n;SetOutput 必须在首次 log.Print* 前调用;多级别日志应使用多个 log.New 实例;文件日志必须配置轮转。
log.Println 和 log.Printf 的换行行为差异必须搞清
很多人写
log.Printf
时漏掉换行符,结果多条日志挤在一行,排查时根本分不清哪条属于哪个请求。这不是 bug,是设计如此:
log.Print
和
log.Println
内部自动加
\n
;
log.Printf
则严格按格式字符串输出,不额外补换行。
错误写法:
log.Printf("failed to connect: %v", err)
→ 后续
log.Println("shutting down")
可能连在一行
正确做法:显式加
\n
,如
log.Printf("failed to connect: %v\n", err)
,或统一用
log.Println(fmt.Sprintf("..."))
生产环境建议避免裸用
log.Printf
,尤其在错误路径中——容易因换行缺失导致日志解析失败
SetOutput 必须在首次 log.Print* 调用前设置
想把日志写进文件却还是打到终端?大概率是
log.SetOutput
调晚了。Go 的标准
log
包只在第一次调用
log.Print*
时绑定输出目标,之后再调
SetOutput
对已初始化的内部 logger 无效。
安全顺序:先
os.OpenFile
→ 检查
err
→
log.SetOutput(file)
→ 再调
log.Println
如果项目里已大量使用顶层函数(如
log.Fatal
),别试图后期重定向——要么重构为传参式
*log.Logger
,要么一开始就设好
注意:
log.SetOutput
只影响全局 logger,不影响你用
log.New
创建的独立实例
用多个 log.New 实例模拟日志级别最稳妥
标准库
没有
Info
/
Error
方法,但硬套一个“封装结构体”反而增加维护成本。直接为每个级别建一个
*log.Logger
实例,简单、清晰、无隐藏副作用。
示例:
InfoLog := log.New(os.Stdout, "[INFO] ", log.LstdFlags|log.Lshortfile)
,
ErrorLog := log.New(os.Stderr, "[ERROR] ", log.LstdFlags|log.Lshortfile)
错误日志写
os.Stderr
是惯例,运维工具(如 systemd、supervisor)会自动区分 stderr/stdout 流
不要给所有级别共用同一个文件句柄再靠前缀分流——看似省事,实则丢失错误隔离能力(比如磁盘满时 error 日志可能写不进去,info 还在狂刷)
文件日志必须配轮转,否则迟早填满磁盘
标准库连最基础的按大小切割都不支持,长期运行的服务不加轮转,
app.log
几天就能涨到几十 GB。别信“先上线再加”的说法——上线当天就该配好。
go语言参考手册 中文CHM版
Go 是一个开源的编程语言,它能让构造简单、可靠且高效的软件变得容易。本文给大家带来Go参考手册,需要的可以来下载! Go是从2007年末由Robert Griesemer, Rob Pike, Ken Thompson主持开发,后来还加入了Ian Lance Taylor, Russ Cox等人,并最终于2009年11月开源,在2012年早些时候发布了Go 1稳定版本。现在Go的开发已经是完全开放的,并且拥有一个活跃的社区。 Go 语言特色 简洁、快速、安全 并行、有趣、开源 内存管理、v数组安全、编译
下载
立即学习
“
go语言免费学习笔记(深入)
”;
推荐用
gopkg.in/natefinch/lumberjack.v2
,三行代码搞定:
log.SetOutput(&lumberjack.Logger{Filename: "logs/app.log", MaxSize: 10, MaxBackups: 5})
切忌自己用
time.Now().Format("2006-01-02")
拼文件名做“每日轮转”——没锁、没原子重命名、goroutine 并发时极易冲突或丢日志
如果用
log.SetOutput
指向文件,记得
defer file.Close()
,否则程序异常退出时最后几条日志可能缓存未刷盘
标准库
log
不是不能用,而是它的“简单”有明确边界:不处理轮转、不隔离级别、不防换行遗漏。越早接受这些限制,越少在凌晨三点翻查被挤成一团的日志文件。