接入Logrus后Fiber日志中间件异常,仅记录特定请求问题排查
Fiber日志中间件与Logrus共存时部分请求日志丢失问题
此前使用Fiber日志中间件可正常记录所有请求日志,接入Logrus用于代码日志后,Fiber日志中间件仅能记录/favicon.ico、/metrics这类请求,其余请求(如提交用户名密码的POST请求)完全无日志输出。尝试调整中间件接入顺序、将日志输出到文件、移除其他中间件等操作均未解决问题。
日志示例
[127.0.0.1]:49775 200 - GET /metrics [127.0.0.1]:49775 200 - GET /metrics [127.0.0.1]:49775 200 - GET /favicon.ico [127.0.0.1]:49775 200 - GET /metrics
期望效果
Fiber日志中间件与Logrus协同工作,前者正常输出所有请求日志。
相关代码实现
Fiber日志中间件配置
app.Use(logger.New(logger.Config{ Format: "[${ip}]:${port} ${status} - ${method} ${path}\n", }))
Logrus初始化代码
var Logger *logrus.Logger type CustomFormatter struct { TimestampFormat string } func (f *CustomFormatter) Format(entry *logrus.Entry) ([]byte, error) { // 根据日志级别设置颜色 var levelColor int switch entry.Level { case logrus.DebugLevel, logrus.TraceLevel: levelColor = 36 // 青色 case logrus.WarnLevel: levelColor = 33 // 黄色 case logrus.ErrorLevel, logrus.FatalLevel, logrus.PanicLevel: levelColor = 31 // 红色 default: levelColor = 32 // 绿色 } timestamp := entry.Time.Format(f.TimestampFormat) fileName := filepath.Base(entry.Caller.File) line := entry.Caller.Line level := strings.ToUpper(entry.Level.String()) log := fmt.Sprintf("%s \x1b[%dm[%s]\x1b[0m [%s:%d] %s\n", timestamp, levelColor, level, fileName, line, entry.Message) return []byte(log), nil } type LogEntry struct { Timestamp string `json:"timestamp"` Level string `json:"level"` File string `json:"file"` Line int `json:"line"` Message string `json:"message"` } type JSONFormatter struct{} func (f *JSONFormatter) Format(entry *logrus.Entry) ([]byte, error) { logEntry := LogEntry{ Timestamp: entry.Time.Format(time.RFC3339), Level: entry.Level.String(), File: filepath.Base(entry.Caller.File), Line: entry.Caller.Line, Message: entry.Message, } return json.Marshal(logEntry) } type LoggerHook struct { Writer io.Writer Formatter logrus.Formatter } func (h *LoggerHook) Levels() []logrus.Level { return logrus.AllLevels } func (h *LoggerHook) Fire(entry *logrus.Entry) error { data, err := h.Formatter.Format(entry) if err != nil { return err } _, err = h.Writer.Write(data) if err != nil { return err } _, err = h.Writer.Write([]byte("\n")) // 每条日志后换行 return err } func SetupLogger(lv int, path string) *logrus.Logger { logger := logrus.New() logFile, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) if err != nil { logger.Fatalf("Failed to open log file: %v", err) } // 建议添加defer关闭文件句柄,避免泄漏 defer logFile.Close() consoleFormatter := &CustomFormatter{ TimestampFormat: "02-Jan-2006 15:04:05", } logger.SetOutput(io.MultiWriter(os.Stdout)) logger.SetFormatter(consoleFormatter) logger.SetReportCaller(true) logger.AddHook(&LoggerHook{ Writer: logFile, Formatter: &JSONFormatter{}, }) logger.SetLevel(MapIntToLogLevel(lv)) return logger } func MapIntToLogLevel(level int) logrus.Level { switch level { case 6: return logrus.PanicLevel case 5: return logrus.FatalLevel case 4: return logrus.ErrorLevel case 3: return logrus.WarnLevel case 2: return logrus.InfoLevel case 1: return logrus.DebugLevel case 0: return logrus.TraceLevel default: return logrus.InfoLevel } }
解决方案
1. 明确指定Fiber日志中间件的输出目标
修改Fiber日志中间件配置,显式设置Output为os.Stdout,避免输出被其他日志组件的操作干扰:
app.Use(logger.New(logger.Config{ Format: "[${ip}]:${port} ${status} - ${method} ${path}\n", Output: os.Stdout, // 强制指定输出到标准输出 }))
2. 确保Fiber日志中间件优先注册
将Fiber日志中间件作为第一个注册的中间件,保证所有请求都会经过它:
func main() { app := fiber.New() // 先注册Fiber日志中间件 app.Use(logger.New(logger.Config{ Format: "[${ip}]:${port} ${status} - ${method} ${path}\n", Output: os.Stdout, })) // 再初始化Logrus和其他中间件 Logger = SetupLogger(2, "app.log") // 注册路由 app.Post("/login", handleLogin) app.Get("/metrics", handleMetrics) app.Listen(":3000") }
3. 排查Logrus Hook的干扰
临时注释掉Logrus的Hook注册代码,测试Fiber日志是否恢复正常:
// logger.AddHook(&LoggerHook{ // Writer: logFile, // Formatter: &JSONFormatter{}, // })
如果恢复正常,说明Hook中的文件操作可能存在冲突,可尝试调整Hook的实现,比如使用异步写入避免阻塞。
4. 检查请求处理逻辑
确认所有请求处理函数中未提前调用ctx.Abort()或未执行ctx.Next(),导致请求未到达Fiber日志中间件的后续处理流程。
内容的提问来源于stack exchange,提问作者nzrsh
相关产品推荐
相关产品推荐

