必须用os.openfile配os.o_append|os.o_wronly追加写,缺一不可;需检查每次调用错误、加锁防并发、用fmt.fprintln自动换行。

用 os.OpenFile 以 os.O_APPEND | os.O_WRONLY 模式打开文件
Go 中不能直接用 os.WriteFile 追加,它总是覆盖。必须用 os.OpenFile 显式控制打开模式。os.O_APPEND 是关键——它让每次 Write 自动跳到文件末尾,无需手动 Seek;os.O_WRONLY 表示只写,避免读写冲突。
常见错误是漏掉 os.O_WRONLY(比如只传 os.O_APPEND),这会导致打开失败并报错 invalid argument;或者误用 os.O_CREATE 却没配 0644 权限参数,导致文件创建失败。
实操建议:
- 务必同时传入
os.O_APPEND和os.O_WRONLY,缺一不可 - 如果文件可能不存在,加上
os.O_CREATE,并提供权限参数(如0644) - 别用
os.O_RDWR——追加写不需要读能力,且某些系统下会干扰O_APPEND行为
写入前检查 error,别忽略返回值
os.OpenFile 和后续的 Write 都可能失败:路径不存在、磁盘满、权限不足、文件被占用等。忽略 error 是线上日志丢失的最常见原因。
典型现象:程序看似正常运行,但日志文件始终为空或内容不全,查半天才发现 f, _ := os.OpenFile(...) 里错误被吞了。
实操建议:
- 每次调用
os.OpenFile后立刻检查err != nil -
f.Write或fmt.Fprintln(f, ...)后也要检查err——写入中途失败不会自动重试 - 记得
defer f.Close(),但关闭也可能出错(如磁盘写满后 flush 失败),生产环境建议也检查
用 fmt.Fprintln 而不是 .WriteString 处理多行日志
追加场景多数是写日志,每条记录需换行。用 .WriteString 容易忘记加 \n,导致所有内容挤在一行;而 fmt.Fprintln 自动追加换行符,语义清晰、不易出错。
性能上差异极小(一次系统调用),但可读性和维护性提升明显。注意 fmt.Fprintln 返回的是写入字节数和 error,仍需检查后者。
示例片段:
f, err := os.OpenFile("app.log", os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
if err != nil {
log.Fatal(err)
}
defer f.Close()
_, err = fmt.Fprintln(f, "user login: alice", time.Now().Format(time.RFC3339))
if err != nil {
log.Printf("failed to write log: %v", err)
}
并发写入时必须加锁,os.File 本身不保证线程安全
多个 goroutine 直接共用同一个 *os.File 并发调用 Write,会出现内容交错、丢失甚至 panic。因为 O_APPEND 的原子性仅针对单次系统调用,而 Go 的 Write 可能拆分成多次 write(2),中间可能被其他 goroutine 插入。
这不是“大概率出问题”,而是“一定会出问题”,尤其在高频率写入时。
实操建议:
- 全局日志写入器应封装成带
sync.Mutex或sync.RWMutex的结构体 - 避免在 HTTP handler 等高频入口直接打开/关闭文件——开销大且易触发 fd 耗尽
- 更稳妥的做法是用
log.SetOutput配合自定义io.Writer,内部统一串行化写入
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











