本文介绍如何在 Go 中使用 logrus 等日志库实现按级别分流输出:INFO 及以上仅输出到 stdout,DEBUG 及以上同时写入带时间戳的文件,解决单 logger 无法差异化路由日志的痛点。
本文介绍如何在 go 中使用 logrus 等日志库实现**按级别分流输出**:info 及以上仅输出到 stdout,debug 及以上同时写入带时间戳的文件,解决单 logger 无法差异化路由日志的痛点。
在 Go 日志实践中,io.MultiWriter 虽能将日志镜像复制到多个 Writer(如文件 + stdout),但它不支持按日志级别分流——即无法让 Debug() 写入文件而 Info() 仅打印到终端。因此,仅用 log.SetOutput(io.MultiWriter(...)) 无法满足题目中「stdout 仅 INFO+、文件记录 DEBUG+」的核心需求。
要真正实现级别感知的多目标输出,推荐使用成熟的结构化日志库 —— Logrus(或其现代替代品 Zap),通过自定义 Hook 实现精准路由:
package mainimport ( "io" "log" "os" "time" "github.com/sirupsen/logrus")// LevelFileHook 将指定级别及以上的日志写入文件type LevelFileHook struct { writer io.Writer levels []logrus.Level}func (h *LevelFileHook) Fire(entry *logrus.Entry) error { if !contains(h.levels, entry.Level) { return nil } line, err := entry.String() if err != nil { return err } _, err = h.writer.Write([]byte(line)) return err}func (h *LevelFileHook) Levels() []logrus.Level { return h.levels}func contains(levels []logrus.Level, level logrus.Level) bool { for _, l := range levels { if l == level { return true } } return false}func main() { // 创建带时间戳的文件 Writer file, err := os.OpenFile("app.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) if err != nil { log.Fatal(err) } defer file.Close() // 初始化 Logrus logger := logrus.New() logger.SetFormatter(&logrus.TextFormatter{ FullTimestamp: true, TimestampFormat: time.RFC3339, }) // Hook 1:仅 DEBUG 及以上写入文件 logger.AddHook(&LevelFileHook{ writer: file, levels: []logrus.Level{ logrus.DebugLevel, logrus.InfoLevel, logrus.WarnLevel, logrus.ErrorLevel, logrus.FatalLevel, logrus.PanicLevel, }, }) // Hook 2:仅 INFO 及以上输出到 stdout(可进一步过滤为 INFO+) logger.AddHook(&LevelFileHook{ writer: os.Stdout, levels: []logrus.Level{ logrus.InfoLevel, logrus.WarnLevel, logrus.ErrorLevel, logrus.FatalLevel, logrus.PanicLevel, }, }) // 使用示例 logger.Debug("Entering some func") // → 仅写入 app.log logger.Info("Result was: Successful") // → 同时输出到 stdout 和 app.log}
Go 原生 log 包功能有限,无法原生支持分级多输出;Logrus 通过 Hook 扩展机制提供了优雅、可组合的解决方案。只需为不同输出目标注册对应级别的 Hook,即可精准控制每条日志的流向,兼顾可读性、可维护性与生产可用性。