---
id: logger
---

# Logger

Logger middleware for [Fiber](https://github.com/gofiber/fiber) that logs HTTP requests and responses.

## Signatures

```go
func New(config ...Config) fiber.Handler
func RegisterTag(tag string, fn LogFunc) error
func MustRegisterTag(tag string, fn LogFunc)
```

## Examples

Import the package:

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

:::tip
Registration order matters: only routes added after the logger are logged, so register it early.
:::

Once your Fiber app is initialized, use the middleware like this:

```go
// Initialize default config
app.Use(logger.New())

// Or extend your config for customization
// Log remote IP and port
app.Use(logger.New(logger.Config{
    Format: "[${ip}]:${port} ${status} - ${method} ${path}\n",
}))

// Logging Request ID
app.Use(requestid.New()) // Ensure requestid middleware is used before the logger
app.Use(logger.New(logger.Config{
    // requestid.New() registers ${requestid} automatically.
    Format: "${pid} ${requestid} ${status} - ${method} ${path}\n",
}))

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

// Custom File Writer
accessLog, err := os.OpenFile("./access.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
    log.Fatalf("error opening access.log file: %v", err)
}
defer accessLog.Close()
app.Use(logger.New(logger.Config{
    Stream: accessLog,
}))

// Add Custom Tags
app.Use(logger.New(logger.Config{
    CustomTags: map[string]logger.LogFunc{
        "custom_tag": func(output logger.Buffer, c fiber.Ctx, data *logger.Data, extraParam string) (int, error) {
            return output.WriteString("it is a custom tag")
        },
    },
}))

// Callback after log is written
app.Use(logger.New(logger.Config{
    TimeFormat: time.RFC3339Nano,
    TimeZone:   "Asia/Shanghai",
    Done: func(c fiber.Ctx, logString []byte) {
        if c.Response().StatusCode() != fiber.StatusOK {
            reporter.SendToSlack(logString)
        }
    },
}))

// Disable colors when outputting to default format
app.Use(logger.New(logger.Config{
    DisableColors: true,
}))

// Force the use of colors
app.Use(logger.New(logger.Config{
    ForceColors: true,
}))

// Use predefined formats
app.Use(logger.New(logger.Config{
    Format: logger.CommonFormat,
}))

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

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

app.Use(logger.New(logger.Config{
    Format: logger.ECSFormat,
}))
```

### Auto-Registered Tags

Some Fiber middleware registers logger middleware tags automatically. Register the producing middleware before `logger.New()` and then use the tag in `Format`.

```go
app.Use(requestid.New())
app.Use(logger.New(logger.Config{
    Format: "${requestid} ${status} ${method} ${path}\n",
}))
```

The logger middleware resolves tags in this order:

1. Built-in logger tags, such as `${method}`, `${path}`, and `${status}`.
2. Globally registered tags from Fiber middleware or `logger.RegisterTag`.
3. `Config.CustomTags`, which override tags with the same name for that logger instance.

The following tags are registered by Fiber middleware when the middleware is initialized:

| Tag | Registered by | Value |
| :-- | :------------ | :---- |
| `${requestid}` / `${request-id}` | `requestid.New()` | Request ID stored by the requestid middleware. |
| `${username}` | `basicauth.New()` | Authenticated username stored by the basicauth middleware. |
| `${api-key}` | `keyauth.New()` | Redacted API key stored by the keyauth middleware. |
| `${csrf-token}` | `csrf.New()` | Redacted marker when the csrf middleware stores a token. |
| `${session-id}` | `session.New()` or `session.NewWithStore()` | Redacted session ID stored by the session middleware. |

:::note
Auto-registered tags are access-log tags for `middleware/logger`. The same names are also registered for application logs in the `log` package via `logger.RegisterContextTag` — see [api/log#context-tags](../api/log.md#context-tags) for details on `log.WithContext` enrichment.
:::

### Register Tags from Custom Middleware

Third-party middleware can expose logger tags with `logger.RegisterTag` or `logger.MustRegisterTag`. Use `sync.Once` so the tag is registered once even when the middleware is initialized multiple times.

```go
package tenantmw

import (
    "sync"

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

type tenantContextKey struct{}

var tenantKey tenantContextKey

var registerLoggerTagsOnce sync.Once

func New() fiber.Handler {
    registerLoggerTagsOnce.Do(func() {
        logger.MustRegisterTag("tenant", func(output logger.Buffer, c fiber.Ctx, _ *logger.Data, _ string) (int, error) {
            tenant, _ := fiber.ValueFromContext[string](c, tenantKey)
            return output.WriteString(tenant)
        })
    })

    return func(c fiber.Ctx) error {
        fiber.StoreInContext(c, tenantKey, "acme")
        return c.Next()
    }
}
```

Use the registered tag in the logger format after installing the middleware:

```go
app.Use(tenantmw.New())
app.Use(logger.New(logger.Config{
    Format: "${tenant} ${status} ${method} ${path}\n",
}))
```

Use `Config.CustomTags` when one logger instance needs a local override without changing the global tag registration:

```go
app.Use(logger.New(logger.Config{
    Format: "${tenant} ${status} ${method} ${path}\n",
    CustomTags: map[string]logger.LogFunc{
        "tenant": func(output logger.Buffer, c fiber.Ctx, _ *logger.Data, _ string) (int, error) {
            return output.WriteString("override")
        },
    },
}))
```

### Use Logger Middleware with Other Loggers

To combine the logger middleware with loggers like Zerolog, Zap, or Logrus, use the `LoggerToWriter` helper to adapt them to an `io.Writer`.

```go
package main

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

func main() {
    // Create a new Fiber instance
    app := fiber.New()

    // Create a new zap logger which is compatible with Fiber AllLogger interface
    zap := fiberzap.NewLogger(fiberzap.LoggerConfig{
        ExtraKeys: []string{"request_id"},
    })

    // Use the logger middleware with the zap logger
    app.Use(logger.New(logger.Config{
        Stream: logger.LoggerToWriter(zap, log.LevelDebug),
    }))

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

    // Start server on http://localhost:3000
    app.Listen(":3000")
}
```

:::tip
Writing to `os.File` is goroutine-safe, but custom streams may require locking to serialize writes.
:::

## Config

| Property      | Type                                              | Description                                                                                                                                   | Default                                                               |
| :------------ | :------------------------------------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------- |
| Next          | `func(fiber.Ctx) bool`                            | Next defines a function to skip this middleware when it returns true.                                                                           | `nil`                                                                 |
| Skip          | `func(fiber.Ctx) bool`                            | Skip is a function to determine if logging is skipped or written to Stream.                                                                   | `nil`                                                                 |
| Done          | `func(fiber.Ctx, []byte)`                         | Done is a function that is called after the log string for a request is written to Stream, and pass the log string as parameter.              | `nil`                                                                 |
| CustomTags    | `map[string]LogFunc`                              | Defines custom tag actions for this logger instance. These tags override built-in and globally registered tags with the same name.             | `nil`                                                                 |
| `Format`   | `string`  | Defines the logging tags. See more in [Predefined Formats](#predefined-formats), or create your own using [Tags](#constants). | `[${time}] ${ip} ${status} - ${latency} ${method} ${path} ${error}\n` (same as `DefaultFormat`) |
| TimeFormat    | `string`                                          | TimeFormat defines the time format for log timestamps.                                                                                        | `15:04:05`                                                            |
| TimeZone      | `string`                                          | TimeZone can be specified, such as "UTC" and "America/New_York" and "Asia/Chongqing", etc                                                     | `"Local"`                                                             |
| TimeInterval  | `time.Duration`                                   | TimeInterval is the delay before the timestamp is updated.                                                                                    | `500 * time.Millisecond`                                              |
| Stream        | `io.Writer`                                       | Stream is a writer where logs are written.                                                                                                    | `os.Stdout`                                                           |
| LoggerFunc    | `func(c fiber.Ctx, data *Data, cfg *Config) error` | Custom logger function for integration with logging libraries (Zerolog, Zap, Logrus, etc). Defaults to Fiber's default logger if not defined. | `see default_logger.go defaultLoggerInstance`                         |
| DisableColors | `bool`                                            | DisableColors defines if the logs output should be colorized.                                                                                 | `false`                                                               |
| ForceColors   | `bool`                                            | ForceColors defines if the logs output should be colorized even when the output is not a terminal.                                             | `false`                                                               |

## Default Config

```go
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

Logger provides predefined formats that you can use by name or directly by specifying the format string.

| **Format Constant** | **Format String** | **Description** |
|---------------------|--------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------|
| `DefaultFormat` | `"[${time}] ${ip} ${status} - ${latency} ${method} ${path} ${error}\n"` | Fiber's default logger format. |
| `CommonFormat` | `"${ip} - - [${time}] "${method} ${url} ${protocol}" ${status} ${bytesSent}\n"` | Common Log Format (CLF) used in web server logs. |
| `CombinedFormat` | `"${ip} - - [${time}] "${method} ${url} ${protocol}" ${status} ${bytesSent} "${referer}" "${ua}"\n"` | CLF format plus the `referer` and `user agent` fields. |
| `JSONFormat` | `"{\"time\":\"${time}\",\"ip\":\"${ip}\",\"method\":\"${method}\",\"url\":\"${url}\",\"status\":${status},\"bytesSent\":${bytesSent}}\n"` | JSON format for structured logging. |
| `ECSFormat` | `"{\"@timestamp\":\"${time}\",\"ecs\":{\"version\":\"1.6.0\"},\"client\":{\"ip\":\"${ip}\"},\"http\":{\"request\":{\"method\":\"${method}\",\"url\":\"${url}\",\"protocol\":\"${protocol}\"},\"response\":{\"status_code\":${status},\"body\":{\"bytes\":${bytesSent}}}},\"log\":{\"level\":\"INFO\",\"logger\":\"fiber\"},\"message\":\"${method} ${url} responded with ${status}\"}\n"` | Elastic Common Schema (ECS) format for structured logging. |

:::tip
`${bytesSent}` returns the value of the `Content-Length` response header. If the header is missing or the response is streaming (e.g., chunked encoding), the value will be `-1`. Fiber does not calculate the actual response body size for performance reasons.
:::

## Constants

```go
// Logger variables
const (
    TagPid               = "pid"
    TagTime              = "time"
    TagReferer           = "referer"
    TagProtocol          = "protocol"
    TagPort              = "port"
    TagIP                = "ip"
    TagIPs               = "ips"
    TagHost              = "host"
    TagMethod            = "method"
    TagPath              = "path"
    TagURL               = "url"
    TagUA                = "ua"
    TagLatency           = "latency"
    TagStatus            = "status"         // response status
    TagResBody           = "resBody"        // response body
    TagReqHeaders        = "reqHeaders"
    TagQueryStringParams = "queryParams"    // request query parameters
    TagBody              = "body"           // request body
    TagBytesSent         = "bytesSent"
    TagBytesReceived     = "bytesReceived"
    TagRoute             = "route"
    TagError             = "error"
    TagReqHeader         = "reqHeader:"     // request header
    TagRespHeader        = "respHeader:"    // response header
    TagQuery             = "query:"         // request query
    TagForm              = "form:"          // request form
    TagCookie            = "cookie:"        // request cookie
    TagLocals            = "locals:"
    // colors
    TagBlack             = "black"
    TagRed               = "red"
    TagGreen             = "green"
    TagYellow            = "yellow"
    TagBlue              = "blue"
    TagMagenta           = "magenta"
    TagCyan              = "cyan"
    TagWhite             = "white"
    TagReset             = "reset"
)
```
