配置GIN+zerolog:错误日志写入文件,其余日志输出至标准输出
Gin + Zerolog 实现按级别分流日志(Error写文件,其他输出到Stdout)
你的代码问题出在中间件的判断逻辑上:l.GetLevel()获取的是Logger实例的全局级别(你设置的是InfoLevel),而不是当前日志调用的级别(比如调用Error()时的ErrorLevel)。所以这个if条件永远不会成立,所有日志都只会输出到stdout,达不到Error级日志写文件的效果。
下面给出两种可行的解决方案:
方案一:使用Zerolog Hook实现级别分流(推荐)
Hook是Zerolog提供的扩展机制,可以在日志事件触发时自定义处理逻辑,我们可以用它根据级别分流输出。
1. 定义LevelSplitHook
import ( "io" "github.com/rs/zerolog" ) type LevelSplitHook struct { stdoutWriter io.Writer fileWriter io.Writer } // Run 实现zerolog.Hook接口,处理日志事件 func (h *LevelSplitHook) Run(e *zerolog.Event, level zerolog.Level, msg string) { // 根据级别选择输出目标 targetWriter := h.stdoutWriter if level == zerolog.ErrorLevel { targetWriter = h.fileWriter } // 基于目标writer创建新的日志事件,保留原事件的字段 newLogger := zerolog.New(targetWriter).With(). Timestamp(). Caller(). Logger() newEvent := newLogger.Event(level) // 复制原事件的所有字段 e.Struct().MarshalZerologObject(newEvent) // 写入日志消息 newEvent.Msg(msg) // 丢弃原事件,避免重复输出 e.Discard() }
2. 在Gin中间件中配置Hook
func main() { zerolog.TimeFieldFormat = "02-01-2006 15:04:05" zerolog.ErrorStackMarshaler = pkgerrors.MarshalStack zerolog.SetGlobalLevel(zerolog.InfoLevel) // 打开错误日志文件,注意处理文件打开错误 errorFile, err := os.OpenFile("error.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if err != nil { panic(err) } defer errorFile.Close() // 初始化分流Hook splitHook := &LevelSplitHook{ stdoutWriter: os.Stdout, fileWriter: errorFile, } r := gin.Default() r.Use(logger.SetLogger( logger.WithLogger(func(ctx *gin.Context, l zerolog.Logger) zerolog.Logger { // 给logger添加Hook,原输出设置为Discard(由Hook处理实际输出) return l.Hook(splitHook).With().Caller().Logger() }), )) // 你的路由配置... }
3. 路由函数中正常调用日志
func GetTasks(ctx *gin.Context, db *sql.DB, log *zerolog.Logger) { _, err := db.Prepare("SELECT * FROM tasks ORDER BY id ASC") log.Error().Err(errors.New("halo....")).Msg("数据库预处理错误") // 这条日志会写入error.log log.Info().Msg("获取任务列表请求") // 这条日志会输出到stdout }
方案二:自定义Writer实现级别分流
通过自定义io.Writer,解析日志的JSON内容判断级别,再决定输出目标。这种方式更简单,但依赖Zerolog的JSON格式。
1. 定义LevelSplitWriter
import ( "io" "encoding/json" ) type LevelSplitWriter struct { stdout io.Writer file io.Writer } func (w *LevelSplitWriter) Write(p []byte) (n int, err error) { // 解析日志JSON,提取level字段 var logData map[string]interface{} if err := json.Unmarshal(p, &logData); err == nil { if level, ok := logData["level"].(string); ok && level == "error" { return w.file.Write(p) } } // 默认输出到stdout return w.stdout.Write(p) }
2. 在Gin中间件中配置Writer
func main() { zerolog.TimeFieldFormat = "02-01-2006 15:04:05" zerolog.ErrorStackMarshaler = pkgerrors.MarshalStack zerolog.SetGlobalLevel(zerolog.InfoLevel) errorFile, err := os.OpenFile("error.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if err != nil { panic(err) } defer errorFile.Close() splitWriter := &LevelSplitWriter{ stdout: os.Stdout, file: errorFile, } r := gin.Default() r.Use(logger.SetLogger( logger.WithLogger(func(ctx *gin.Context, l zerolog.Logger) zerolog.Logger { return l.Output(splitWriter).With().Caller().Logger() }), )) // 你的路由配置... }
内容的提问来源于stack exchange,提问作者Lutfi Khoirudin
相关产品推荐
相关产品推荐

