Logger
Nimbus uses uber-go/zap for structured, high-performance logging. The logger package provides a pre-configured *zap.SugaredLogger and convenience functions.
Quick start
Import the logger package and call the level functions directly. The global logger is initialised automatically on import.
import "github.com/CodeSyncr/nimbus/logger"
logger.Info("server started", "port", 3333)
logger.Debug("loading config", "path", ".env")
logger.Warn("cache miss", "key", "user:42")
logger.Error("query failed", "error", err)
Log levels
The logger supports four primary levels. Each accepts a message string followed by key-value pairs for structured fields.
logger.Debug(msg, keysAndValues...)— verbose detail for development.logger.Info(msg, keysAndValues...)— general operational events.logger.Warn(msg, keysAndValues...)— potential issues that don't stop execution.logger.Error(msg, keysAndValues...)— errors that need attention.logger.Fatal(msg, keysAndValues...)— logs the message then callsos.Exit(1).
Structured fields
Pass alternating key-value pairs after the message. Zap encodes them as structured fields in the log output.
logger.Info("user login",
"userID", 42,
"email", "alice@example.com",
"ip", "192.168.1.1",
)
// Output: 2026-03-08T12:00:00Z INFO user login {"userID": 42, "email": "alice@example.com", "ip": "192.168.1.1"}
Child loggers with context
logger.With(keysAndValues...) returns a new *zap.SugaredLogger that includes the given fields in every subsequent log line. Useful for adding request-scoped context.
reqLogger := logger.With("requestID", "abc-123", "method", "GET")
reqLogger.Infow("handling request", "path", "/users")
reqLogger.Infow("query complete", "rows", 15)
Logging in middleware
The built-in middleware.Logger() logs every request. You can also add custom logging in your own middleware.
func RequestLogger() router.Middleware {
return func(next router.HandlerFunc) router.HandlerFunc {
return func(c *http.Context) error {
start := time.Now()
err := next(c)
logger.Info("request",
"method", c.Request().Method,
"path", c.Request().URL.Path,
"duration", time.Since(start).String(),
)
return err
}
}
}
Logging in handlers
func createUser(c *http.Context) error {
logger.Info("creating user", "ip", c.Request().RemoteAddr)
// ... create user ...
logger.Info("user created", "userID", user.ID)
return c.JSON(201, user)
}
Custom configuration
Replace the global logger with logger.Set() to customise encoding, level, or output. The default uses console encoding at Info level with ISO 8601 timestamps.
import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"github.com/CodeSyncr/nimbus/logger"
)
cfg := zap.NewProductionConfig()
cfg.Level = zap.NewAtomicLevelAt(zapcore.DebugLevel)
cfg.Encoding = "json"
l, _ := cfg.Build()
logger.Set(l)
Default configuration
Out of the box, the logger is configured with:
- Production config base (sampling, caller info)
- Console encoding for readable terminal output
- Info level (Debug messages hidden by default)
- ISO 8601 timestamps
Request-scoped logger
Use logger.ForRequest(c) to get a logger that automatically includes the request_id (set by the RequestID middleware). This correlates all log lines for a single request.
import "github.com/CodeSyncr/nimbus/logger"
func CreateOrder(c *http.Context) error {
log := logger.ForRequest(c)
log.Info("creating order", "user_id", userID)
// ... create order ...
log.Info("order created", "order_id", order.ID)
// All log lines include {"request_id": "a1b2c3d4..."}
return c.JSON(201, order)
}
Attach a custom scoped logger to the context with logger.WithContext:
// In middleware — enrich logger with user info
func UserLogger() router.Middleware {
return func(next router.HandlerFunc) router.HandlerFunc {
return func(c *http.Context) error {
l := logger.ForRequest(c).With("user_id", getUserID(c))
logger.WithContext(c, l)
return next(c)
}
}
}
Log rotation
For file-based logging in production, use logger.RotatingWriter to automatically rotate log files when they exceed a size threshold and keep a limited number of backups.
import "github.com/CodeSyncr/nimbus/logger"
writer := logger.NewRotatingWriter(logger.RotationConfig{
Path: "storage/logs/app.log",
MaxSizeMB: 100, // Rotate at 100 MB (default)
MaxBackups: 5, // Keep 5 old log files (default)
})
defer writer.Sync()
// Use as an io.Writer for custom zap configuration:
core := zapcore.NewCore(
zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()),
zapcore.AddSync(writer),
zap.InfoLevel,
)
l := zap.New(core).Sugar()
logger.Set(l)
Rotated files are named app-2026-03-16T12-00-00.log. Oldest backups are deleted when MaxBackups is exceeded.