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