logger

package
v1.3.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 6, 2026 License: Apache-2.0 Imports: 11 Imported by: 0

Documentation

Overview

Package logger provides HTTP request logging middleware for celeris.

The middleware logs each request with configurable fields including method, path, status, latency, and bytes written. It supports both standard slog handlers and the zero-alloc FastHandler.

Basic usage with the default slog output:

server.Use(logger.New())

Using the zero-alloc FastHandler with color output:

mw := logger.New(logger.Config{
    Output: slog.New(logger.NewFastHandler(os.Stderr, &logger.FastHandlerOptions{
        Color: true,
    })),
})
server.Use(mw)

Body Capture

Set Config.CaptureRequestBody and/or Config.CaptureResponseBody to log request and response bodies. Bodies are truncated to Config.MaxCaptureBytes (default 4096).

Sensitive Header Redaction

Config.SensitiveHeaders lists header names whose values are redacted. When nil, DefaultSensitiveHeaders is used. Set to an empty slice to disable all redaction.

Request ID Integration

The middleware reads the request ID from the context store (key "request_id") first, falling back to the x-request-id header.

Predefined Configurations

CLFConfig returns a Config for Common Log Format style output. JSONConfig returns a Config for structured JSON output. Both return a Config value that can be further customized:

cfg := logger.CLFConfig()
cfg.SkipPaths = []string{"/health"}
server.Use(logger.New(cfg))

Design Rationale

All output is structured through Go's log/slog package. No template strings are provided. The Config.Fields callback supplies arbitrary extensibility. FastHandler formats directly into pooled byte buffers, avoiding fmt.Sprintf and time.Format entirely.

Query Parameter Security

Security: LogQueryParams logs raw query strings which may contain sensitive values (OAuth tokens, API keys, session identifiers). Consider using SkipPaths for sensitive endpoints or implementing a custom Fields function that redacts sensitive query parameters.

Skipping

Set Config.Skip to bypass dynamically, or Config.SkipPaths for exact-match path exclusions.

Middleware Order

Register after requestid for request ID inclusion in logs.

Reverse Proxy Integration

When running behind a reverse proxy, install the proxy middleware via Server.Pre() so that Logger sees the real client IP:

server.Pre(proxy.New(proxy.Config{
    TrustedProxies: []string{"10.0.0.0/8"},
}))
server.Use(logger.New()) // now logs the real client IP

Without proxy middleware, Logger records the reverse proxy's IP address, not the end user's.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func DefaultSensitiveFormFields

func DefaultSensitiveFormFields() []string

DefaultSensitiveFormFields returns a copy of the default sensitive form field names. Unlike DefaultSensitiveHeaders, this list is NOT applied automatically — Config.SensitiveFormFields nil means no redaction. Pass this list explicitly when enabling Config.LogFormValues to get reasonable defaults:

logger.New(logger.Config{
    LogFormValues:       true,
    SensitiveFormFields: logger.DefaultSensitiveFormFields(),
})

func DefaultSensitiveHeaders

func DefaultSensitiveHeaders() []string

DefaultSensitiveHeaders returns a copy of the default sensitive header names redacted when Config.SensitiveHeaders is nil. Set SensitiveHeaders to an empty slice ([]string{}) to disable all redaction.

func New

func New(config ...Config) celeris.HandlerFunc

New creates a logger middleware with the given config.

Example
package main

import (
	"github.com/goceleris/celeris/middleware/logger"
)

func main() {
	// Zero-config: logs to slog.Default() with INFO/WARN/ERROR levels.
	// DefaultSensitiveHeaders are redacted automatically.
	_ = logger.New()
}
Example (CustomFields)
package main

import (
	"io"
	"log/slog"
	"time"

	"github.com/goceleris/celeris"

	"github.com/goceleris/celeris/middleware/logger"
)

func main() {
	// Add custom fields to every log entry.
	log := slog.New(slog.NewJSONHandler(io.Discard, nil))
	_ = logger.New(logger.Config{
		Output:    log,
		SkipPaths: []string{"/health", "/ready"},
		Fields: func(c *celeris.Context, latency time.Duration) []slog.Attr {
			return []slog.Attr{
				slog.String("trace_id", c.Header("x-trace-id")),
				slog.Bool("slow", latency > time.Second),
			}
		},
	})
}
Example (DisableRedaction)
package main

import (
	"io"
	"log/slog"

	"github.com/goceleris/celeris/middleware/logger"
)

func main() {
	// Pass an empty slice to disable all header redaction.
	log := slog.New(slog.NewJSONHandler(io.Discard, nil))
	_ = logger.New(logger.Config{
		Output:           log,
		SensitiveHeaders: []string{},
	})
}
Example (FastHandler)
package main

import (
	"log/slog"
	"os"

	"github.com/goceleris/celeris/middleware/logger"
)

func main() {
	// FastHandler: zero-allocation structured logging with color output.
	log := slog.New(logger.NewFastHandler(os.Stderr, &logger.FastHandlerOptions{
		Color: true,
	}))
	_ = logger.New(logger.Config{Output: log})
}
Example (SensitiveHeaders)
package main

import (
	"io"
	"log/slog"

	"github.com/goceleris/celeris/middleware/logger"
)

func main() {
	// Override the default sensitive headers list.
	log := slog.New(slog.NewJSONHandler(io.Discard, nil))
	_ = logger.New(logger.Config{
		Output: log,
		SensitiveHeaders: []string{
			"Authorization",
			"X-Api-Key",
			"X-Internal-Token",
		},
	})
}

Types

type Config

type Config struct {
	// Skip defines a function to conditionally bypass this middleware.
	// When it returns true for a given request, no log entry is emitted
	// and c.Next() is called directly.
	Skip func(c *celeris.Context) bool

	// Output is the slog.Logger used to emit log records.
	// When nil, slog.Default() is used.
	Output *slog.Logger

	// Level maps an HTTP response status code to a slog.Level, controlling
	// the severity of each log entry. The default maps 5xx to Error,
	// 4xx to Warn, and everything else to Info.
	Level func(status int) slog.Level

	// Fields is an optional callback that returns additional slog.Attr
	// values to include in each log entry. It is called after the
	// downstream handler completes, so latency and response data are
	// available.
	Fields func(c *celeris.Context, latency time.Duration) []slog.Attr

	// Done is an optional callback invoked after the log entry is written.
	// It is always called, even when the log level is disabled, making it
	// suitable for alerting on 5xx responses or collecting metrics outside
	// the log pipeline.
	Done func(c *celeris.Context, latency time.Duration, status int)

	// SkipPaths is a list of request paths to exclude from logging.
	// Matching is exact (no glob or prefix support).
	SkipPaths []string

	// CaptureRequestBody enables logging of the request body, truncated
	// to MaxCaptureBytes. The body is emitted as the "request_body" attr.
	CaptureRequestBody bool

	// CaptureResponseBody enables logging of the response body, truncated
	// to MaxCaptureBytes. The body is emitted as the "response_body" attr
	// and requires the response to be captured via c.CaptureResponse().
	CaptureResponseBody bool

	// MaxCaptureBytes is the maximum number of body bytes to include in
	// the log entry when CaptureRequestBody or CaptureResponseBody is
	// enabled. Default: 4096. This cap prevents OOM on large payloads.
	MaxCaptureBytes int

	// SensitiveHeaders lists header names whose values should be redacted
	// in log output. Matched values are replaced with "[REDACTED]" and
	// header names are compared case-insensitively.
	//
	// When nil, DefaultSensitiveHeaders is used automatically. Set to an
	// empty slice ([]string{}) to disable all header redaction.
	SensitiveHeaders []string

	// TimeZone sets the timezone applied to log timestamps before they
	// are passed to the slog handler. When nil, the local timezone is
	// used.
	TimeZone *time.Location

	// TimeFormat sets a custom Go time layout string (e.g. time.RFC3339)
	// for FastHandler output. When empty, FastHandler uses its built-in
	// zero-alloc RFC3339-millis formatter.
	TimeFormat string

	// LogHost includes the request Host header as the "host" slog attr.
	// Disabled by default to keep log entries compact.
	LogHost bool

	// LogUserAgent includes the User-Agent request header as the
	// "user_agent" slog attr. Disabled by default.
	LogUserAgent bool

	// LogReferer includes the Referer request header as the "referer"
	// slog attr. Disabled by default.
	LogReferer bool

	// LogRoute includes the matched route pattern from c.FullPath() as
	// the "route" slog attr. Omitted when the route is empty (e.g., 404).
	LogRoute bool

	// LogPID includes the process ID (cached at init) as the "pid" slog
	// attr. Useful for distinguishing workers in multi-process deployments.
	LogPID bool

	// LogQueryParams includes the raw query string from the request URL
	// as the "query" slog attr. Omitted when there is no query string.
	LogQueryParams bool

	// LogFormValues includes URL-encoded form field names and values as
	// the "form" slog attr. Only active when the request content-type is
	// application/x-www-form-urlencoded. Use SensitiveFormFields to
	// redact secrets.
	LogFormValues bool

	// LogCookies includes cookie names (not values, for security) as the
	// "cookies" slog attr. Values are intentionally omitted to avoid
	// leaking session tokens into logs.
	LogCookies bool

	// LogBytesIn includes the request Content-Length as the "bytes_in"
	// slog attr (int64). Omitted when the content length is negative or
	// absent.
	LogBytesIn bool

	// LogScheme includes the request scheme from c.Scheme() (e.g.,
	// "https") as the "scheme" slog attr.
	LogScheme bool

	// DisableColors overrides the Color flag on FastHandlerOptions. When
	// true, the FastHandler emits plain text without ANSI escape codes.
	// Useful for log files, CI pipelines, or any non-terminal output.
	DisableColors bool

	// LogResponseHeaders lists specific response header names whose values
	// should be included in the log entry. Names are compared
	// case-insensitively and each matched header is logged as
	// "resp_header.<lowercased-name>".
	LogResponseHeaders []string

	// SensitiveFormFields lists form field names whose values should be
	// redacted when LogFormValues is true. Matched values are replaced
	// with "[REDACTED]" and field names are compared case-insensitively.
	//
	// IMPORTANT: Unlike SensitiveHeaders (where nil uses defaults), nil
	// here means NO redaction at all — every form value is logged verbatim,
	// including passwords and secrets. This asymmetry is intentional
	// because form field names are application-specific.
	//
	// Use [DefaultSensitiveFormFields] for a reasonable starting list:
	//
	//   SensitiveFormFields: logger.DefaultSensitiveFormFields()
	//
	// Semantics:
	//   - nil  → no form field redaction (all values logged as-is).
	//   - []string{} (empty) → no form field redaction (same as nil).
	//   - non-empty → matching fields are replaced with "[REDACTED]".
	SensitiveFormFields []string

	// LogContextKeys lists context store keys whose values should be
	// included in the log entry. For each key, the middleware calls
	// c.Get(key) and, if found, emits the value as a "ctx.<key>" slog
	// attr using fmt.Sprint for non-string types.
	LogContextKeys []string
}

Config defines the logger middleware configuration.

func CLFConfig

func CLFConfig() Config

CLFConfig returns a Config pre-configured for Common Log Format (CLF) style output. It enables LogHost, LogUserAgent, and LogReferer, and uses a Fields callback that emits a single "clf" attribute with the traditional combined log format line: host ident user time "method path proto" status bytes "referer" "user-agent".

func JSONConfig

func JSONConfig() Config

JSONConfig returns a Config pre-configured for structured JSON output using slog.JSONHandler writing to os.Stdout. It enables LogHost, LogUserAgent, LogReferer, LogRoute, and LogQueryParams for comprehensive structured logging.

type FastHandler

type FastHandler struct {
	// contains filtered or unexported fields
}

FastHandler is a high-performance slog.Handler that formats log records directly into a pooled byte buffer with zero allocations in steady state. It produces output compatible with slog.TextHandler's format.

Use it as a drop-in replacement for slog.TextHandler when performance matters more than customization:

log := slog.New(logger.NewFastHandler(os.Stderr, nil))
mw := logger.New(logger.Config{Output: log})

func NewFastHandler

func NewFastHandler(w io.Writer, opts *FastHandlerOptions) *FastHandler

NewFastHandler creates a new FastHandler writing to w.

func (*FastHandler) Enabled

func (h *FastHandler) Enabled(_ context.Context, level slog.Level) bool

Enabled reports whether the handler handles records at the given level.

func (*FastHandler) Handle

func (h *FastHandler) Handle(_ context.Context, r slog.Record) error

Handle formats the record and writes it to the output.

func (*FastHandler) HandleDirect

func (h *FastHandler) HandleDirect(ts time.Time, level slog.Level, msg string, attrs []slog.Attr)

HandleDirect formats a log record directly from a pre-built attrs slice, bypassing slog.Record entirely. This avoids the closure escape in Record.Attrs and the copy-before-write, eliminating 4 allocations compared to the standard Handle path.

func (*FastHandler) WithAttrs

func (h *FastHandler) WithAttrs(attrs []slog.Attr) slog.Handler

WithAttrs returns a new handler with the given attributes pre-formatted.

func (*FastHandler) WithGroup

func (h *FastHandler) WithGroup(name string) slog.Handler

WithGroup returns a new handler with the given group name prepended to all subsequent attribute keys.

type FastHandlerOptions

type FastHandlerOptions struct {
	// Level is the minimum log level. Default: slog.LevelInfo.
	Level slog.Level
	// Color enables ANSI color codes in output.
	// Level names are colored: red=ERROR, yellow=WARN, green=INFO, cyan=DEBUG.
	Color bool
	// TimeFormat sets a custom Go time layout (e.g. time.RFC3339).
	// When empty, the built-in RFC3339-millis formatter is used.
	TimeFormat string
}

FastHandlerOptions configures a FastHandler.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL