在Go中使用logrus自定义Hook可实现日志按级别分流:文件Hook接收DEBUG及以上级别,终端Hook仅接收INFO及以上,从而让调试日志仅写入文件,常规日志同时输出到终端和文件,有效解决标准库无法区分级别的路由问题。
本文介绍如何在 Go 中使用 logrus 等日志库实现按级别分流输出:INFO 及以上仅输出到 stdout,DEBUG 及以上同时写入带时间戳的文件,解决单 logger 无法差异化路由日志的痛点。
在 Go 的日志实践过程中,许多开发者会遇到一个典型的场景:希望将 DEBUG 级别的调试信息仅写入文件以便后续排查,而将 INFO 及以上级别的正常运行状态直接输出到终端。Go 标准库中的 io.MultiWriter 只能将日志完整复制到多个输出目标,无法实现按级别区分输出。也就是说,仅靠 log.SetOutput(io.MultiWriter(...)) 无法满足这种分级需求。
那么,如何让日志输出具备“级别感知”能力?答案是采用支持级别感知的结构化日志库,例如 Logrus(或性能更优的 Zap),通过自定义 Hook 实现精准路由。下面详细拆解这套 Go 日志分级输出方案。
长期稳定更新的攒劲资源: >>>点此立即查看<<<
package main
import (
"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
}
该方案的核心思路是:每个 Hook 可以独立声明自己关心的日志级别。Logrus 在产生日志条目时,会遍历所有 Hook,只有满足级别条件的 Hook 才会触发写入操作。上述代码中,文件 Hook 接收 DEBUG 及以上所有级别,终端 Hook 仅接收 INFO 及以上级别。这样一来,DEBUG 日志只进入文件,INFO 日志则同时出现在终端和文件里,实现 Go 日志分级输出的精准控制。
io.MultiWriter 本质上是“全量复制器”,它将所有日志原封不动地发送给每个 Writer,无法区分级别,因此不适合需要分开路径的场景。AddHook 机制天然支持按级别过滤,每个 Hook 独立声明生效级别,这是实现 Go 日志分级路由最直接、最标准的方式。os.File 底层也支持并发写入,无需额外加锁。Go 原生 log 包功能有限,无法原生支持“按级别分路径”的日志输出需求。Logrus 借助 Hook 扩展机制,以优雅的方式提供了可组合的解决方案:为每个输出目标注册对应的级别 Hook,即可精确控制每一条日志的去向。这套 Go 日志分级输出方案兼顾了可读性、可维护性,在生产环境下也经得起考验。
侠游戏发布此文仅为了传递信息,不代表侠游戏网站认同其观点或证实其描述