最稳妥的go文件追加方式是用os.openfile配合os.o_append、os.o_wronly和os.o_create标志。必须同时指定这三个标志,否则可能覆盖文件、报错或无法创建;日志场景可选os.o_sync但影响性能;避免使用ioutil.writefile或os.writefile,因其会全量重写;并发写需加sync.mutex或用channel串行化;windows下建议用"\r\n"换行并注意权限。

用 os.OpenFile 配合 os.O_APPEND 最稳妥
Go 没有“直接追加”这种独立函数,必须靠 os.OpenFile 控制打开模式。关键不是写法多,而是模式选错就覆盖或报错。
-
os.O_APPEND必须带上,否则即使文件存在、os.O_WRONLY也默认从头写 - 别漏掉
os.O_CREATE—— 否则文件不存在时会直接返回no such file or directory - 如果只写日志类内容,建议加
os.O_SYNC(但会影响性能),避免断电丢最后几条
示例:
f, err := os.OpenFile("log.txt", os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
if err != nil {
log.Fatal(err)
}
defer f.Close()
f.WriteString("new line\n")
为什么不用 ioutil.WriteFile 或 os.WriteFile
这两个函数本质是“全量重写”,每次调用都会清空原文件再写入全部内容。对日志、临时拼接等场景完全不适用,且并发下极易丢失数据。
在 Go 中使用 google/wire 实现编译时依赖注入——wire.NewSet、wire.Build、wire.Bind(接口→实现)、wire.Struct、wire.Value、wire.Interface
-
os.WriteFile内部调用os.Create,强制 truncate,和追加目标相反 - 哪怕你先读全文件、再拼字符串、再全量写回,也存在竞态:两次调用之间别人可能已写入新内容
- 小文件看似能凑合,但一旦涉及多进程/多 goroutine,立刻出问题
并发写同一个文件末尾要小心 WriteString 不是原子的
多个 goroutine 共享一个 *os.File 并发 WriteString,可能产生乱序或截断——因为底层 write 系统调用虽带 O_APPEND,但 Go 的 WriteString 是先拷贝到 buffer 再 syscall,中间有 gap。
- 真正安全的做法:用
sync.Mutex包一层写操作 - 或者改用
syscall.Write直接调系统调用(绕过 Go buffer),但需自己处理字节切片和错误转换 - 更现实的方案:用 channel 聚合日志,单个 goroutine 顺序写
Windows 下换行符和权限容易被忽略
在 Windows 上,用 \n 追加后打开记事本可能显示为一行——不是 Go 的问题,是记事本只认 \r\n。另外,首次创建文件时若没设权限,后续可能因无写权限失败。
- 跨平台写日志,建议统一用
"\r\n"替代"\n"(尤其目标是人工查看) -
os.OpenFile第四个参数是perm FileMode,首次创建时生效;已有文件则忽略该参数 - 若程序由服务账户运行(如 Windows Service),注意该账户是否对目标目录有写权限
追加本身不难,难的是想清楚:谁在写、是否并发、是否跨平台、失败后要不要重试、日志是否允许丢失。这些决定了你到底该锁、该 channel、还是该切分文件。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










