不能直接用 log.new 创建多级日志器,因其返回的 *log.logger 无级别区分和动态开关能力;需自定义结构体 logger 封装 level、out、prefix 并实现 info/warn/error 等方法。

为什么不能直接用 log.New 创建多级日志器?
Go 标准库的 log.New 返回的是一个扁平的 *log.Logger,它本身不区分 INFO、WARN、ERROR 级别,也不支持按级别动态开关输出。你调用一次 log.New,得到的只是一个带前缀和输出目标的记录器,所有日志都走同一通道——想实现“只输出 ERROR 及以上”或“INFO 日志写文件、ERROR 同时发告警”,光靠它做不到。
自定义工厂函数该怎么设计才实用?
核心是把日志级别、输出目标、格式化逻辑封装进闭包,返回一个带方法的结构体或函数类型。推荐用结构体 + 方法方式,更易扩展:
type Logger struct {
level int
out io.Writer
prefix string
}
func (l *Logger) Info(v ...interface{}) {
if l.level
<p>工厂函数就负责构造这个结构体实例:</p>
<pre class="brush:php;toolbar:false;">func NewLogger(level int, out io.Writer, prefix string) *Logger {
return &Logger{level: level, out: out, prefix: prefix}
}
-
level用常量控制(如const LevelInfo = 1; const LevelError = 3),避免字符串比较开销 - 不要在工厂里做
os.Open或网络连接——这些应由调用方传入已打开的io.Writer,否则工厂职责过重且难测试 - 如果需要时间戳或 caller 信息,用
log.SetFlags配合底层log.Logger复用,而不是自己拼接字符串
如何让不同级别日志输出到不同目标?
常见需求:INFO 写文件,ERROR 同时写文件+推 Slack。这不是靠“一个 Logger 多个方法”能解决的,得靠组合:
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
用 io.MultiWriter 把多个 io.Writer 聚合成一个,再传给对应级别的 Logger;或者更灵活地,在工厂函数里返回一个接口:
type LevelLogger interface {
Info(...interface{})
Warn(...interface{})
Error(...interface{})
}
func NewMultiTargetLogger(file *os.File, alertWriter io.Writer) LevelLogger {
return &multiLogger{
infoLog: log.New(file, "[INFO] ", log.LstdFlags),
errorLog: log.New(io.MultiWriter(file, alertWriter), "[ERROR] ", log.LstdFlags),
}
}
- 别试图让单个
*log.Logger动态切换 writer——它不支持运行时替换SetOutput后还能并发安全 - 如果要用 JSON 格式,别改
log.Logger的输出逻辑,而是用log.New包一层序列化 writer,比如json.NewEncoder(w)封装成io.Writer - 注意
io.MultiWriter是非缓冲的,若某个 writer 写得慢(比如网络),会拖慢全部日志输出
为什么生产环境别自己造轮子?
标准库 log 缺少结构化、上下文、采样、Hook 等能力,而 zap、zerolog 这类库已把工厂模式做到极致:
logger := zap.NewProductionConfig().Build() // 自动区分 level、JSON 输出、自动加 traceID
infoLogger := logger.With(zap.String("component", "http"))
自己写的工厂函数在小项目够用,但一旦要支持:
- 日志采样(每 100 条 INFO 只记 1 条)
- 字段动态注入(request ID、user ID)
- 异步写入避免阻塞主流程
- 滚动切片、压缩归档
就得重写大量逻辑。真正该花时间的地方,是定义好日志字段规范、分级策略、采集路径,而不是反复调试 io.MultiWriter 的 panic 场景。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










