A Comprehensive Guide to Using Uber's Zap Logging Library in Go
This article introduces Uber's open‑source Zap library for Go, explains its high‑performance, structured, leveled logging features, demonstrates basic and advanced usage with code examples, compares production and development configurations, shows how to use SugaredLogger, customize loggers, apply options, and integrate the library into custom logging packages.
Zap is an open‑source Go logging library from Uber, known for its speed and support for fast, structured, leveled logging.
Features include:
Very fast logging by avoiding interface{} , reflection, and using sync.Pool to reduce heap allocations.
Structured logging.
Seven log levels: Debug , Info , Warn , Error , DPanic (panics in development), Panic , Fatal .
Stack trace output.
Hooks mechanism.
Basic usage :
package main
import (
"time"
"go.uber.org/zap"
)
func main() {
// Production environment
{
logger, _ := zap.NewProduction()
defer logger.Sync()
url := "https://jianghushinian.cn/"
logger.Info("production failed to fetch URL",
zap.String("url", url), // avoid interface{} and reflection
zap.Int("attempt", 3),
zap.Duration("backoff", time.Second),
)
}
// Development environment
{
logger, _ := zap.NewDevelopment()
defer logger.Sync()
url := "https://jianghushinian.cn/"
logger.Debug("development failed to fetch URL",
zap.String("url", url),
zap.Int("attempt", 3),
zap.Duration("backoff", time.Second),
)
}
}Production creates a JSON‑encoded logger, while development creates a console‑encoded logger; the time format differs ( Unix epoch vs ISO8601 ).
Logger method signatures, e.g.:
func (log *Logger) Info(msg string, fields ...Field)The fields argument is of type zapcore.Field , and Zap provides type‑specific helpers such as zap.String , zap.Int , and a generic zap.Any .
Sample output from the above code:
{"level":"info","ts":1679212318.10218,"caller":"zap/main.go:16","msg":"production failed to fetch URL","url":"https://jianghushinian.cn/","attempt":3,"backoff":1}
2023-03-19T15:51:58.102+0800 DEBUG zap/main.go:29 development failed to fetch URL {"url": "https://jianghushinian.cn/", "attempt": 3, "backoff": "1s"}Configuration comparison between production and development:
func NewProduction(options ...Option) (*Logger, error) { return NewProductionConfig().Build(options...) }
func NewProductionConfig() Config {
return Config{
Level: NewAtomicLevelAt(InfoLevel),
Development: false,
Sampling: &SamplingConfig{Initial: 100, Thereafter: 100},
Encoding: "json",
EncoderConfig: NewProductionEncoderConfig(),
OutputPaths: []string{"stderr"},
ErrorOutputPaths: []string{"stderr"},
}
}
func NewDevelopment(options ...Option) (*Logger, error) { return NewDevelopmentConfig().Build(options...) }
func NewDevelopmentConfig() Config {
return Config{
Level: NewAtomicLevelAt(DebugLevel),
Development: true,
Encoding: "console",
EncoderConfig: NewDevelopmentEncoderConfig(),
OutputPaths: []string{"stderr"},
ErrorOutputPaths: []string{"stderr"},
}
}The main differences are the Encoding (JSON vs console) and the EncodeTime function ( EpochTimeEncoder vs ISO8601TimeEncoder ).
SugaredLogger provides a more ergonomic API:
package main
import (
"time"
"go.uber.org/zap"
)
func main() {
logger, _ := zap.NewProduction()
defer logger.Sync()
url := "https://jianghushinian.cn/"
sugar := logger.Sugar()
sugar.Infow("production failed to fetch URL",
"url", url,
"attempt", 3,
"backoff", time.Second,
)
sugar.Info("Info")
sugar.Infof("Infof: %s", url)
sugar.Infoln("Infoln")
}SugaredLogger lets you add key‑value pairs without explicit type helpers, at the cost of a small performance penalty.
Custom logger creation using a tailored zap.Config :
func newCustomLogger() (*zap.Logger, error) {
cfg := zap.Config{
Level: zap.NewAtomicLevelAt(zap.DebugLevel),
Development: false,
Encoding: "json",
EncoderConfig: zapcore.EncoderConfig{
TimeKey: "time",
LevelKey: "level",
NameKey: "logger",
CallerKey: "",
FunctionKey: zapcore.OmitKey,
MessageKey: "message",
LineEnding: zapcore.DefaultLineEnding,
EncodeLevel: zapcore.LowercaseLevelEncoder,
EncodeTime: zapcore.RFC3339TimeEncoder,
EncodeDuration: zapcore.SecondsDurationEncoder,
EncodeCaller: zapcore.ShortCallerEncoder,
},
OutputPaths: []string{"stdout", "test.log"},
ErrorOutputPaths: []string{"error.log"},
}
return cfg.Build()
}
func main() {
logger, _ := newCustomLogger()
defer logger.Sync()
// Add a caller‑skip option to trigger an internal error and write to error.log
logger = logger.WithOptions(zap.AddCallerSkip(100))
logger.Info("Info msg")
logger.Error("Error msg")
}The above code demonstrates how to customize fields, output destinations, and add options such as zap.AddCallerSkip , which can cause Zap to log errors to the configured ErrorOutputPaths .
Available Logger options include:
WrapCore(func(zapcore.Core) zapcore.Core)
Hooks(...func(zapcore.Entry) error)
Fields(...Field)
ErrorOutput(w zapcore.WriteSyncer)
Development()
AddCaller() / WithCaller(bool)
AddCallerSkip(int)
AddStacktrace(lvl zapcore.LevelEnabler)
IncreaseLevel(lvl zapcore.LevelEnabler)
WithFatalHook(zapcore.CheckWriteHook)
WithClock(zapcore.Clock)
Custom wrapper package (based on Zap) provides a Go‑log‑style API and supports multi‑output logging:
package main
import (
"os"
log "github.com/jianghushinian/gokit/log/zap"
)
func main() {
defer log.Sync()
log.Info("failed to fetch URL", log.String("url", "https://jianghushinian.cn/"))
log.Warn("Warn msg", log.Int("attempt", 3))
log.Error("Error msg", log.Duration("backoff", time.Second))
log.SetLevel(log.ErrorLevel)
// Replace default logger
file, _ := os.OpenFile("custom.log", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
logger := log.New(file, log.InfoLevel)
log.ReplaceDefault(logger)
log.Info("Info msg in replace default logger after")
}Multi‑output (tee) example:
package main
import (
"os"
log "github.com/jianghushinian/gokit/log/zap"
)
func main() {
file, _ := os.OpenFile("test-warn.log", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
tees := []log.TeeOption{{Out: os.Stdout, LevelEnablerFunc: func(l log.Level) bool { return l == log.InfoLevel }}, {Out: file, LevelEnablerFunc: func(l log.Level) bool { return l == log.WarnLevel }}}
logger := log.NewTee(tees)
defer logger.Sync()
logger.Info("Info tee msg")
logger.Warn("Warn tee msg")
logger.Error("Error tee msg") // not output
}The article concludes that Zap offers high performance structured logging for Go, and the author’s thin wrapper adds a familiar API while retaining Zap’s speed and flexibility.
Go Programming World
Mobile version of tech blog https://jianghushinian.cn/, covering Golang, Docker, Kubernetes and beyond.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.