You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

接入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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 02:32:33