go语言.浅析log.Println和fmt.Println
这是一个很自然的直觉——表面上看确实像(都是控制台打印一行字),但本质上它们不一样,只是底层都绕不开"往一个输出目标写字节"这件事。准确的说法是:它俩有共同的底层机制,但定位和能力差别很大。
相同点(让你觉得"一样"的原因)
- 最终都调用底层的写入操作,把格式化后的字节送到一个
io.Writer; - 终端里直接运行时,肉眼看到的输出位置几乎相同;
- 参数风格类似,都是
Println(x...)。
本质区别
| 维度 | fmt.Println |
log.Println |
|---|---|---|
| 定位 | 格式化输出工具,负责"把数据按格式变成文本" | 日志工具,负责"记录带上下文的运行信息" |
| 默认去向 | 标准输出 os.Stdout | 标准错误 os.Stderr |
| 时间前缀 | 无 | 默认带 日期 时间 |
| 输出目标可否全局切换 | 不能,每次得显式指定 Writer | log.SetOutput() 一改全局生效 |
| 换行 | 自动加 | 自动加 |
| 设计层级 | 底层原语,log 内部格式化也借助 fmt 的能力 |
建立在 fmt 之上的"应用层"设施 |
一个关键事实:log 包内部其实就是在用 fmt 来做格式化。所以不是平级的两个东西,而更像 fmt 是"写字"的基础能力,log 是在它外面包了一层"日志该有的样子"(时间戳、统一出口、logger 实例)。
举个具体例子
fmt.Println("完毕")
// 完毕
log.Println("完毕")
// 2026/10/04 10:00:00 完毕
输出内容本身就不同——log 多了时间戳,这就是"日志"和"普通输出"最直观的分野。
怎么选用(这才是重点)
- 程序的正常结果、要给下游/管道消费的内容 → 用
fmt(走 stdout)。比如 CLI 工具输出查询结果、打印生成的文本。 - 给人看的运行过程、诊断、报错信息 → 用
log(走 stderr)。比如"连接成功""处理到第几个""出错了"。
这样分流的好处在重定向时才真正体现:
go run . >result.txt 2>error.log
程序结果干净地进 result.txt,日志诊断进 error.log,互不污染。如果两者都用 fmt 打到 stdout,就没法这样分离了。、
go run . 2>err.log # 只把 stderr(log 的内容)收进 err.log
go run . >out.log # 只把 stdout(fmt 的内容)收进 out.log
在 VS Code/Trae 这类 IDE 的调试控制台、输出面板里,两个流通常也会被合并展示。
另外想改去向:log.SetOutput
log.SetOutput(os.Stdout) // 改为标准输出
log.SetOutput(f) // 改为某个文件
log.SetOutput(io.MultiWriter(f, os.Stderr)) // 文件和控制台同时写
一句话总结
底层"写字节"的机制相同,但 fmt 是通用格式化输出,log 是带时间戳、走 stderr、可统一配置出口的专用日志设施——后者是前者之上的一层封装。 把它们当成"同一个东西"在小程序里不会出问题,但养成"结果用 fmt、日志用 log"的习惯,对以后写服务很重要。
浙公网安备 33010602011771号