Structured Logging
Fox includes request logging middleware and a small logger package built on top of zerolog. The middleware creates or reuses an x-request-id, stores a request logger on *fox.Context, and writes one request log after the handler finishes.
Basic Usage
Section titled “Basic Usage”r := fox.Default()
r.GET("/user/:id", func(ctx *fox.Context) (*User, error) { log := ctx.Logger id := ctx.Param("id")
log.WithField("user_id", id).Info("fetching user")
user, err := getUserByID(id) if err != nil { log.WithError(err).Error("failed to fetch user") return nil, err }
return user, nil})fox.Default() installs fox.Logger(), fox.NewXResponseTimer(), and fox.Recovery(). If you use fox.New(), add the middleware explicitly:
r := fox.New()r.Use(fox.Logger())TraceID
Section titled “TraceID”Fox uses the x-request-id header as the TraceID. If the request does not include one, the logger middleware generates it and writes it back to the response header.
r.GET("/trace", func(ctx *fox.Context) map[string]string { return map[string]string{ "trace_id": ctx.TraceID(), }})You can also create a logger from any context carrying a TraceID:
log := logger.NewWithContext(ctx.Context)Logger Configuration
Section titled “Logger Configuration”Configure the global logger package before creating request loggers:
import "github.com/fox-gonic/fox/logger"
logger.SetConfig(&logger.Config{ LogLevel: logger.InfoLevel, ConsoleLoggingEnabled: true, EncodeLogsAsJSON: true,})File Logging
Section titled “File Logging”Fox can write to a rotating log file through lumberjack:
logger.SetConfig(&logger.Config{ LogLevel: logger.InfoLevel, ConsoleLoggingEnabled: false, FileLoggingEnabled: true, Filename: "./logs/app.log", MaxSize: 100, // MB MaxBackups: 7, MaxAge: 30, // days EncodeLogsAsJSON: true,})If file logging is enabled and Filename is empty, Fox writes to a temporary file named after the process.
Structured Fields
Section titled “Structured Fields”Use WithField, WithFields, and WithError to add context:
log := ctx.Logger.WithFields(map[string]any{ "user_id": req.UserID, "action": "create_order",})
order, err := createOrder(req)if err != nil { log.WithError(err).Error("order creation failed") return nil, err}
log.WithField("order_id", order.ID).Info("order created")The logger interface also supports formatted messages:
ctx.Logger.Infof("created user %d", user.ID)Request Logger Middleware
Section titled “Request Logger Middleware”fox.Logger() accepts fox.LoggerConfig with SkipPaths:
r.Use(fox.Logger(fox.LoggerConfig{ SkipPaths: []string{"/health", "/metrics"},}))The middleware logs method, path, client IP, response status, latency, and a fixed type field of ENGINE.
Log Levels
Section titled “Log Levels”Fox exposes these levels in github.com/fox-gonic/fox/logger:
logger.DebugLevellogger.InfoLevellogger.WarnLevellogger.ErrorLevellogger.FatalLevellogger.PanicLevel
Best Practices
Section titled “Best Practices”- Use
ctx.Loggerinside Fox handlers so request logs share the request TraceID. - Add structured fields with
WithFieldorWithFields; avoid concatenating values into messages. - Do not log passwords, tokens, secrets, or sensitive personal data.
- Use
logger.SetConfigat application startup before requests are handled. - Use
SkipPathsfor noisy endpoints such as health checks.
Next Steps
Section titled “Next Steps”- Logger Configuration - Complete configuration examples
- Error Handling - Logging and returning errors