Use Zap by configuring a logger with zap.NewProductionConfig() or zap.NewDevelopmentConfig(), then call Sugar() to get a simpler API or stick with the core Logger for maximum performance. Structured logging is automatic; simply pass key-value pairs to methods like Info(), Error(), or Debug(), and Zap will serialize them into JSON or console formats without manual string formatting.
Here is a production-ready setup using the core API for performance-critical paths:
package main
import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
func main() {
// Configure the logger for production (JSON output, sampling, etc.)
config := zap.NewProductionConfig()
config.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
config.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder
logger, err := config.Build(zap.AddCallerSkip(1))
if err != nil {
panic(err)
}
defer logger.Sync()
// Structured log entry with fields
logger.Info("User login successful",
zap.String("user_id", "12345"),
zap.String("ip", "192.168.1.1"),
zap.Duration("duration", 1500000000), // 1.5 seconds
zap.Bool("is_new_user", true),
)
}
For development or quick prototyping where you prefer a fmt.Printf-like syntax, use the Sugar wrapper. It accepts variadic arguments but still outputs structured data:
package main
import (
"go.uber.org/zap"
)
func main() {
// Create a sugared logger for convenience
logger := zap.NewExample().Sugar()
defer logger.Sync()
// Structured logging with a familiar API
logger.Infow("Processing request",
"method", "POST",
"path", "/api/v1/users",
"status", 201,
)
// Error logging with stack trace (if enabled in config)
logger.Errorw("Database connection failed",
"host", "db.internal",
"port", 5432,
"error", "connection refused",
)
}
Key takeaways for effective usage:
- Always use
defer logger.Sync(): This ensures all buffered log entries are flushed to disk or stdout before the program exits, preventing data loss. - Prefer the core API in hot paths: The
Sugarwrapper adds a small allocation overhead. In tight loops or high-throughput services, use the typedlogger.Info()method with explicitzap.Fieldarguments. - Leverage
With()for context: If you have fields common to a specific scope (like a request ID), uselogger.With(zap.String("request_id", "abc"))to create a child logger. This avoids repeating the same fields in every log call within that scope. - Use
zap.Error()for errors: Instead of converting errors to strings manually, pass the error object directly tozap.Error(err). Zap will include the error message and, if configured, the stack trace.