Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/gofiber/fiber/llms.txt

Use this file to discover all available pages before exploring further.

Logger middleware for Fiber logs HTTP request and response details. It provides flexible formatting, custom tags, and integration with popular logging libraries.

Installation

go get -u github.com/gofiber/fiber/v3
go get -u github.com/gofiber/fiber/v3/middleware/logger

Signatures

func New(config ...Config) fiber.Handler

Usage

Basic Usage

package main

import (
    "github.com/gofiber/fiber/v3"
    "github.com/gofiber/fiber/v3/middleware/logger"
)

func main() {
    app := fiber.New()

    // Default logger
    app.Use(logger.New())

    app.Get("/", func(c fiber.Ctx) error {
        return c.SendString("Hello, World!")
    })

    app.Listen(":3000")
}

Custom Format

app.Use(logger.New(logger.Config{
    Format: "[${ip}]:${port} ${status} - ${method} ${path}\n",
}))

Log to File

accessLog, err := os.OpenFile("./access.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
    log.Fatalf("error opening file: %v", err)
}
defer accessLog.Close()

app.Use(logger.New(logger.Config{
    Stream: accessLog,
}))

Custom Tags

import "github.com/gofiber/fiber/v3/middleware/requestid"

app.Use(requestid.New())
app.Use(logger.New(logger.Config{
    CustomTags: map[string]logger.LogFunc{
        "requestid": func(output logger.Buffer, c fiber.Ctx, data *logger.Data, extraParam string) (int, error) {
            return output.WriteString(requestid.FromContext(c))
        },
    },
    Format: "${pid} ${requestid} ${status} - ${method} ${path}\n",
}))

Predefined Formats

// Common Log Format
app.Use(logger.New(logger.Config{
    Format: logger.CommonFormat,
}))

// Combined Log Format
app.Use(logger.New(logger.Config{
    Format: logger.CombinedFormat,
}))

// JSON Format
app.Use(logger.New(logger.Config{
    Format: logger.JSONFormat,
}))

// Elastic Common Schema (ECS)
app.Use(logger.New(logger.Config{
    Format: logger.ECSFormat,
}))

Time Zone and Format

app.Use(logger.New(logger.Config{
    Format:     "${pid} ${status} - ${method} ${path}\n",
    TimeFormat: "02-Jan-2006",
    TimeZone:   "America/New_York",
}))

Integration with Zap Logger

import (
    "github.com/gofiber/contrib/fiberzap/v2"
    "github.com/gofiber/fiber/v3/log"
    "github.com/gofiber/fiber/v3/middleware/logger"
)

zapLogger := fiberzap.NewLogger(fiberzap.LoggerConfig{
    ExtraKeys: []string{"request_id"},
})

app.Use(logger.New(logger.Config{
    Stream: logger.LoggerToWriter(zapLogger, log.LevelDebug),
}))

Disable Colors

app.Use(logger.New(logger.Config{
    DisableColors: true,
}))

Force Colors

app.Use(logger.New(logger.Config{
    ForceColors: true,
}))

Configuration

Next
func(fiber.Ctx) bool
default:"nil"
Function to skip this middleware when it returns true.
Skip
func(fiber.Ctx) bool
default:"nil"
Function to determine if logging is skipped or written to Stream.
Done
func(fiber.Ctx, []byte)
default:"nil"
Callback function executed after the log string is written to Stream.
CustomTags
map[string]LogFunc
default:"map[string]LogFunc"
Map of custom tag names to functions that generate their values.
Format
string
default:"DefaultFormat"
Logging format string. See Tags for available placeholders.
TimeFormat
string
Time format for the ${time} tag. See Go’s time.Format documentation.
TimeZone
string
Time zone for timestamps (e.g., “UTC”, “America/New_York”, “Asia/Shanghai”).
TimeInterval
time.Duration
default:"500 * time.Millisecond"
Delay before the timestamp is updated.
Stream
io.Writer
default:"os.Stdout"
Output destination for logs.
LoggerFunc
func(c fiber.Ctx, data *Data, cfg *Config) error
default:"defaultLoggerInstance"
Custom logger function for integration with logging libraries.
DisableColors
bool
default:"false"
Disables colorized output.
ForceColors
bool
default:"false"
Forces colorized output even when not outputting to a terminal.

Default Configuration

var ConfigDefault = Config{
    Next:              nil,
    Skip:              nil,
    Done:              nil,
    Format:            DefaultFormat,
    TimeFormat:        "15:04:05",
    TimeZone:          "Local",
    TimeInterval:      500 * time.Millisecond,
    Stream:            os.Stdout,
    BeforeHandlerFunc: beforeHandlerFunc,
    LoggerFunc:        defaultLoggerInstance,
    enableColors:      true,
}

Predefined Formats

FormatDescription
DefaultFormat[${time}] ${ip} ${status} - ${latency} ${method} ${path} ${error}\n
CommonFormat${ip} - - [${time}] "${method} ${url} ${protocol}" ${status} ${bytesSent}\n
CombinedFormatCommon format + "${referer}" "${ua}"\n
JSONFormat{time: ${time}, ip: ${ip}, method: ${method}, url: ${url}, status: ${status}, bytesSent: ${bytesSent}}\n
ECSFormatElastic Common Schema format

Tags

TagDescription
${pid}Process ID
${time}Timestamp
${referer}Referer header
${protocol}HTTP protocol
${port}Port
${ip}Client IP
${ips}IP addresses from X-Forwarded-For
${host}Host header
${method}HTTP method
${path}Request path
${url}Full URL
${ua}User-Agent
${latency}Request latency
${status}Response status code
${resBody}Response body
${reqHeaders}Request headers
${queryParams}Query parameters
${body}Request body
${bytesSent}Bytes sent
${bytesReceived}Bytes received
${route}Matched route
${error}Error if any
${reqHeader:name}Specific request header
${respHeader:name}Specific response header
${query:name}Specific query parameter
${form:name}Specific form field
${cookie:name}Specific cookie
${locals:name}Specific local variable

Color Tags

TagColor
${black}, ${red}, ${green}, ${yellow}Colors
${blue}, ${magenta}, ${cyan}, ${white}Colors
${reset}Reset color

Best Practices

Register Early

// Register logger BEFORE routes to log all requests
app.Use(logger.New())

// Now register routes
app.Get("/", handler)

Skip Health Checks

app.Use(logger.New(logger.Config{
    Next: func(c fiber.Ctx) bool {
        // Don't log health check endpoints
        return c.Path() == "/health" || c.Path() == "/metrics"
    },
}))

Conditional Logging

app.Use(logger.New(logger.Config{
    Skip: func(c fiber.Ctx) bool {
        // Only log errors
        return c.Response().StatusCode() < 400
    },
}))

Post-Log Actions

app.Use(logger.New(logger.Config{
    Done: func(c fiber.Ctx, logString []byte) {
        // Send errors to external service
        if c.Response().StatusCode() >= 500 {
            sendToSlack(logString)
        }
    },
}))

Common Patterns

Development vs Production

config := logger.Config{}

if os.Getenv("ENV") == "production" {
    config.Format = logger.JSONFormat
    config.DisableColors = true
} else {
    config.Format = logger.DefaultFormat
}

app.Use(logger.New(config))

Separate Access and Error Logs

accessLog, _ := os.OpenFile("./access.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
errorLog, _ := os.OpenFile("./error.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)

app.Use(logger.New(logger.Config{
    Stream: accessLog,
    Skip: func(c fiber.Ctx) bool {
        return c.Response().StatusCode() >= 400
    },
}))

app.Use(logger.New(logger.Config{
    Stream: errorLog,
    Skip: func(c fiber.Ctx) bool {
        return c.Response().StatusCode() < 400
    },
}))

Log Request Body

app.Use(logger.New(logger.Config{
    Format: "${method} ${path} - ${body}\n",
}))

Log Specific Headers

app.Use(logger.New(logger.Config{
    Format: "${reqHeader:X-Request-ID} ${method} ${path} ${status}\n",
}))

Custom Tag Example

app.Use(logger.New(logger.Config{
    CustomTags: map[string]logger.LogFunc{
        "user": func(output logger.Buffer, c fiber.Ctx, data *logger.Data, extraParam string) (int, error) {
            user := c.Locals("user")
            if user != nil {
                return output.WriteString(user.(string))
            }
            return output.WriteString("anonymous")
        },
    },
    Format: "[${user}] ${method} ${path} ${status}\n",
}))

Notes

  • Registration order matters: only routes added after the logger are logged
  • Writing to os.File is goroutine-safe
  • Custom streams may require locking for concurrent writes
  • The TimeInterval reduces timestamp update overhead

Build docs developers (and LLMs) love