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")
}
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,
}))
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",
}))
// 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,
}))
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.
Time format for the ${time} tag. See Go’s time.Format documentation.
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.
Disables colorized output.
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,
}
| Format | Description |
|---|
DefaultFormat | [${time}] ${ip} ${status} - ${latency} ${method} ${path} ${error}\n |
CommonFormat | ${ip} - - [${time}] "${method} ${url} ${protocol}" ${status} ${bytesSent}\n |
CombinedFormat | Common format + "${referer}" "${ua}"\n |
JSONFormat | {time: ${time}, ip: ${ip}, method: ${method}, url: ${url}, status: ${status}, bytesSent: ${bytesSent}}\n |
ECSFormat | Elastic Common Schema format |
| Tag | Description |
|---|
${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 |
| Tag | Color |
|---|
${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",
}))
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