Skip to content
Draft
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions ctx_interface_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

57 changes: 55 additions & 2 deletions docs/api/log.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@ type ConfigurableLogger[T any] interface {
type AllLogger[T any] interface {
CommonLogger
ConfigurableLogger[T]
WithLogger

// WithContext returns a new logger with the given context.
WithContext(ctx any) CommonLogger
}
```

Expand Down Expand Up @@ -198,7 +200,58 @@ commonLogger := log.WithContext(ctx)
commonLogger.Info("info")
```

Context binding adds request-specific data for easier tracing.
Context binding can render request-specific data for easier tracing. The default context format is `log.DefaultFormat`, which is empty, so `log.WithContext(ctx)` does not add fields until you configure a format.

Use `log.Format` to configure Fiber's built-in default logger. Custom loggers registered with `SetLogger` keep full control over their own `WithContext` behavior and should implement equivalent enrichment themselves when needed.

```go
app.Use(requestid.New())

if err := log.Format(log.RequestIDFormat); err != nil {
log.Fatal(err)
}

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

Middleware that stores request values can register log context tags automatically when the middleware is initialized. For example, after `requestid.New()` has been used, `${requestid}` and `${request-id}` can be used directly in `log.Format` without defining custom tags.

Use `log.WithContext(c)` inside handlers when you want tags to read values stored by Fiber middleware. Passing `c.Context()` only exposes values propagated into the standard request context.

### Context Formats

| Format Constant | Format String | Description |
| :-- | :-- | :-- |
| `DefaultFormat` | `""` | Disables contextual fields. |
| `RequestIDFormat` | `"[${requestid}] "` | Prepends the request ID when the requestid middleware is used. |
| `KeyValueFormat` | `"request-id=${requestid} username=${username} api-key=${api-key} csrf-token=${csrf-token} session-id=${session-id} "` | Prepends common middleware context values as key/value fields. Sensitive values are redacted by the registering middleware. |

### Context Tags

| Tag | Source |
| :-- | :-- |
| `${requestid}` / `${request-id}` | `requestid` middleware |
| `${username}` | `basicauth` middleware |
| `${api-key}` | `keyauth` middleware, redacted |
| `${csrf-token}` | `csrf` middleware, redacted |
| `${session-id}` | `session` middleware, redacted |
| `${value:key}` | Any bound value with `Value(key)` or `UserValue(key)` lookup methods |

### Custom Context Tags

Register custom tags with `log.RegisterContextTag`, then reference them from `log.Format`.

```go
log.MustRegisterContextTag("tenant", func(output log.Buffer, ctx any, _ *log.ContextData, _ string) (int, error) {
tenant, _ := ctx.(fiber.Ctx).Locals("tenant").(string)
return output.WriteString(tenant)
Comment thread
ReneWerner87 marked this conversation as resolved.
Outdated
Comment thread
coderabbitai[bot] marked this conversation as resolved.
})

log.MustFormat("[${tenant}] ")
```

## Logger

Expand Down
97 changes: 89 additions & 8 deletions docs/middleware/logger.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ Logger middleware for [Fiber](https://github.com/gofiber/fiber) that logs HTTP r

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

## Examples
Expand All @@ -20,6 +22,7 @@ Import the package:
import (
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/logger"
"github.com/gofiber/fiber/v3/middleware/requestid"
)
```

Expand All @@ -42,13 +45,7 @@ app.Use(logger.New(logger.Config{
// Logging Request ID
app.Use(requestid.New()) // Ensure requestid middleware is used before the logger
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))
},
},
// For more options, see the Config section
// Use the custom tag ${requestid} as defined above.
// requestid.New() registers ${requestid} automatically.
Format: "${pid} ${requestid} ${status} - ${method} ${path}\n",
}))

Expand Down Expand Up @@ -117,6 +114,90 @@ app.Use(logger.New(logger.Config{
}))
```

### 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}` | `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`. Application logs from the `log` package use their own context tag registry.
:::
Comment thread
ReneWerner87 marked this conversation as resolved.

### 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"
)

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, _ := c.Locals("tenant").(string)
return output.WriteString(tenant)
})
})

return func(c fiber.Ctx) error {
c.Locals("tenant", "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`.
Expand Down Expand Up @@ -166,7 +247,7 @@ Writing to `os.File` is goroutine-safe, but custom streams may require locking t
| 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` | tagFunctions defines the custom tag action. | `map[string]LogFunc` |
| 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. | `map[string]LogFunc` |
| `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"` |
Expand Down
25 changes: 25 additions & 0 deletions docs/whats_new.md
Original file line number Diff line number Diff line change
Expand Up @@ -1587,6 +1587,31 @@ app.Use(logger.New(logger.Config{

Both approaches ensure your logger can access these values while respecting Go's context practices.

The same template/tag mechanism is also available for application logs emitted through `log.WithContext`. This keeps request logging and handler logging consistent without hard-coding middleware-specific values into the `log` package:

```go
app.Use(requestid.New())

if err := log.SetContextTemplate(log.ContextConfig{
Format: "[${requestid}] ",
CustomTags: map[string]log.ContextTagFunc{
"requestid": func(output log.Buffer, ctx any, _ *log.ContextData, _ string) (int, error) {
return output.WriteString(requestid.FromContext(ctx))
},
},
}); err != nil {
log.Fatal(err)
}

app.Get("/", func(c fiber.Ctx) error {
// Pass c so middleware values stored on Fiber's request context can be read.
log.WithContext(c).Info("handling request")
return c.SendString("OK")
})
```
Comment thread
ReneWerner87 marked this conversation as resolved.

`SetContextTemplate` configures Fiber's built-in default logger. Custom loggers registered with `log.SetLogger` keep control over their own `WithContext` behavior.

The `Skip` is a function to determine if logging is skipped or written to `Stream`.

<details>
Expand Down
8 changes: 8 additions & 0 deletions internal/logtemplate/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package logtemplate

import (
"errors"
)

// ErrParameterMissing indicates that a template parameter was referenced but not provided.
var ErrParameterMissing = errors.New("logtemplate: template parameter missing")
Loading
Loading