
本文介绍使用 io.multiwriter 实现 go 标准日志库(log)双输出——既打印到终端又写入文件,无需第三方依赖,简洁高效。
本文介绍使用 io.multiwriter 实现 go 标准日志库(log)双输出——既打印到终端又写入文件,无需第三方依赖,简洁高效。
在 Go 开发中,常需兼顾开发调试(实时查看控制台日志)与生产环境可追溯性(持久化记录到文件)。Go 的标准 log 包默认仅支持单目标输出,但借助 io.MultiWriter,我们能轻松实现“一写多发”——将同一条日志同时写入多个 io.Writer,例如 os.Stdout(控制台)和 *os.File(日志文件)。
✅ 正确实现方式
只需将 log.SetOutput() 的参数替换为 io.MultiWriter 构造的组合写入器:
package main
import (
"io"
"log"
"os"
)
func main() {
// 打开或创建日志文件(追加模式)
logFile, err := os.OpenFile("log.txt", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
log.Fatalf("无法打开日志文件: %v", err)
}
defer logFile.Close()
// 创建 MultiWriter:同时写入 stdout 和 logFile
mw := io.MultiWriter(os.Stdout, logFile)
// 将 log 输出重定向至此多路写入器
log.SetOutput(mw)
// 现在所有 log.* 调用都会同步出现在终端和文件中
log.Println("服务已启动")
log.Printf("用户登录: %s", "alice")
}
? 注意:推荐使用 os.O_WRONLY(只写)而非 os.O_RDWR,因日志写入无需读取权限;文件权限 0644 更符合安全实践(比 0666 更严格)。
⚠️ 注意事项与最佳实践
- 务必调用 defer logFile.Close():避免文件句柄泄漏,尤其在长期运行服务中。
- 错误处理不可省略:os.OpenFile 失败时应妥善处理(如 log.Fatal),而非 panic,以保证日志系统自身健壮性。
- 并发安全:log.Logger 是并发安全的,io.MultiWriter 内部按顺序写入各目标,无需额外加锁。
- 扩展性提示:如需更多输出目标(如网络日志服务、数据库),只需向 MultiWriter 追加对应 io.Writer 实例即可。
通过 io.MultiWriter,你以极简代码实现了专业级日志分发能力——这是 Go “组合优于继承”哲学的典型体现。无需引入复杂日志框架,标准库已为你准备好优雅解法。











