
本文介绍使用 io.multiwriter 实现 go 标准日志库(log)同时写入控制台(os.stdout)和文件的简洁方案,避免重复记录或自定义日志器的复杂性。
本文介绍使用 io.multiwriter 实现 go 标准日志库(log)同时写入控制台(os.stdout)和文件的简洁方案,避免重复记录或自定义日志器的复杂性。
在 Go 中,默认的 log 包仅支持单一输出目标。若需将日志同时输出到终端(便于开发调试)和文件(便于长期留存与分析),最轻量、标准且可靠的方式是使用 io.MultiWriter —— 它能将一次写入操作自动分发到多个 io.Writer 实例,行为类似于 Unix 的 tee 命令。
以下是完整可运行的示例代码:
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 和文件
mw := io.MultiWriter(os.Stdout, logFile)
log.SetOutput(mw)
// 测试日志输出(将同时出现在终端和 log.txt 中)
log.Println("【INFO】服务已启动")
log.Printf("【DEBUG】当前配置: %+v", map[string]string{"env": "dev"})
}
✅ 关键要点说明:
- os.O_WRONLY 比 os.O_RDWR 更符合日志写入语义(仅需写入,无需读取);
- 权限建议使用 0644(所有者可读写,组/其他只读),提升安全性;
- 务必调用 defer logFile.Close(),防止文件句柄泄漏;
- MultiWriter 是线程安全的,可安全用于多 goroutine 场景(log 包本身也保证并发安全);
- 若需区分控制台与文件的日志格式(例如终端加颜色、文件带毫秒时间戳),则需切换至 log/slog(Go 1.21+)或第三方库(如 zap),但标准 log 包不支持按 writer 定制格式。
综上,io.MultiWriter 是实现双目标日志输出的零依赖、高性能、标准库原生解法,推荐作为中小型项目或快速原型的日志基础方案。











