Zap provides multiple ways to create and configure loggers: preset constructors for quick setup, declarative configuration with the Config struct, and programmatic options for fine-grained control.
func NewProduction(options ...Option) (*Logger, error)
func NewDevelopment(options ...Option) (*Logger, error)
func NewExample(options ...Option) *Logger
func NewNop() *Loggerfunc New(core zapcore.Core, options ...Option) *LoggerCreate a logger from a custom zapcore.Core with optional configuration options.
func Must(logger *Logger, err error) *LoggerPanic if error is non-nil, otherwise return the logger. Useful for initialization:
logger := zap.Must(zap.NewProduction())type Config struct {
Level AtomicLevel
Development bool
DisableCaller bool
DisableStacktrace bool
Sampling *SamplingConfig
Encoding string
EncoderConfig zapcore.EncoderConfig
OutputPaths []string
ErrorOutputPaths []string
InitialFields map[string]interface{}
}Fields:
func (cfg Config) Build(opts ...Option) (*Logger, error)Build a logger from the configuration, optionally applying additional options.
func NewProductionConfig() Config
func NewDevelopmentConfig() ConfigGet pre-configured Config structs for production or development environments.
config := zap.NewProductionConfig()
config.OutputPaths = []string{"stdout", "/var/log/app.log"}
config.InitialFields = map[string]interface{}{
"app": "myservice",
"version": "1.0.0",
}
config.Level.SetLevel(zapcore.DebugLevel)
logger, err := config.Build()
if err != nil {
panic(err)
}
defer logger.Sync()Config can be serialized/deserialized as JSON:
configJSON := []byte(`{
"level": "info",
"encoding": "json",
"outputPaths": ["stdout"],
"errorOutputPaths": ["stderr"],
"initialFields": {"service": "myapp"},
"encoderConfig": {
"messageKey": "message",
"levelKey": "level",
"levelEncoder": "lowercase"
}
}`)
var config zap.Config
if err := json.Unmarshal(configJSON, &config); err != nil {
panic(err)
}
logger, err := config.Build()type SamplingConfig struct {
Initial int
Thereafter int
Hook func(zapcore.Entry, zapcore.SamplingDecision)
}Control log sampling to reduce volume:
config := zap.NewProductionConfig()
config.Sampling = &zap.SamplingConfig{
Initial: 100, // Log first 100 entries per second
Thereafter: 100, // Then log every 100th entry
}
logger, _ := config.Build()Options provide programmatic control over logger behavior. They can be passed to preset constructors, the New function, or WithOptions.
func WrapCore(f func(zapcore.Core) zapcore.Core) OptionWrap or replace the logger's core. Useful for adding tee-ing, sampling, or custom core implementations.
Example:
logger := zap.NewExample(zap.WrapCore(func(core zapcore.Core) zapcore.Core {
// Add a second output
fileCore := zapcore.NewCore(
zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()),
zapcore.AddSync(file),
zapcore.InfoLevel,
)
return zapcore.NewTee(core, fileCore)
}))func Hooks(hooks ...func(zapcore.Entry) error) OptionRegister hooks that are called after each log entry is written. Useful for integrating with external systems.
Example:
logger := zap.NewProduction(zap.Hooks(func(entry zapcore.Entry) error {
if entry.Level >= zapcore.ErrorLevel {
sendToAlertingSystem(entry)
}
return nil
}))func Fields(fs ...Field) OptionAdd initial context fields to the logger.
Example:
logger := zap.NewProduction(zap.Fields(
zap.String("service", "api"),
zap.String("version", "1.0.0"),
))func ErrorOutput(w zapcore.WriteSyncer) OptionSet where internal logger errors are written (e.g., configuration errors, sync failures).
func Development() OptionEnable development mode: DPanic logs panic, stack traces more liberal.
func AddCaller() Option
func WithCaller(enabled bool) Option
func AddCallerSkip(skip int) Optionskip frames when determining callerExample:
// Wrapper function that logs; skip 1 frame to get actual caller
func logError(logger *zap.Logger, msg string, err error) {
logger.WithOptions(zap.AddCallerSkip(1)).Error(msg, zap.Error(err))
}func AddStacktrace(lvl zapcore.LevelEnabler) OptionAutomatically add stack traces for logs at or above the specified level.
Example:
logger := zap.NewProduction(zap.AddStacktrace(zapcore.ErrorLevel))func IncreaseLevel(lvl zapcore.LevelEnabler) OptionSet a minimum level floor. Useful for temporarily increasing log level.
Example:
// Ensure at least WarnLevel, even if core allows Debug
logger := zap.NewDevelopment(zap.IncreaseLevel(zapcore.WarnLevel))func WithPanicHook(hook zapcore.CheckWriteHook) Option
func WithFatalHook(hook zapcore.CheckWriteHook) OptionCustomize behavior when panic or fatal logs are written.
Example:
logger := zap.NewProduction(
zap.WithFatalHook(zapcore.WriteThenGoexit), // Exit without calling os.Exit
)func OnFatal(action zapcore.CheckWriteAction) OptionDeprecated: Use WithFatalHook instead. This option sets the action to take after fatal logs.
func WithClock(clock zapcore.Clock) OptionProvide a custom clock for timestamps. Useful for testing.
type AtomicLevel struct {
// unexported fields
}AtomicLevel provides a thread-safe, dynamically changeable log level. It also implements http.Handler for runtime level changes via HTTP.
func NewAtomicLevel() AtomicLevel
func NewAtomicLevelAt(l zapcore.Level) AtomicLevel
func ParseAtomicLevel(text string) (AtomicLevel, error)func (lvl AtomicLevel) Level() zapcore.Level
func (lvl AtomicLevel) SetLevel(l zapcore.Level)
func (lvl AtomicLevel) Enabled(l zapcore.Level) bool
func (lvl AtomicLevel) String() string
func (lvl AtomicLevel) MarshalText() ([]byte, error)
func (lvl *AtomicLevel) UnmarshalText(text []byte) error
func (lvl AtomicLevel) ServeHTTP(w http.ResponseWriter, r *http.Request)config := zap.NewProductionConfig()
logger, _ := config.Build()
// Change level at runtime
config.Level.SetLevel(zapcore.DebugLevel)
// Check current level
if config.Level.Level() == zapcore.DebugLevel {
logger.Debug("debug mode enabled")
}config := zap.NewProductionConfig()
logger, _ := config.Build()
// Expose level control via HTTP
http.Handle("/log/level", config.Level)
http.ListenAndServe(":8080", nil)
// Change level via HTTP:
// GET http://localhost:8080/log/level -> {"level":"info"}
// PUT http://localhost:8080/log/level?level=debug
// curl -X PUT http://localhost:8080/log/level -d level=debugtype EncoderConfig = zapcore.EncoderConfigEncoderConfig customizes how log entries are formatted.
func NewProductionEncoderConfig() zapcore.EncoderConfig
func NewDevelopmentEncoderConfig() zapcore.EncoderConfigtype EncoderConfig struct {
MessageKey string
LevelKey string
TimeKey string
NameKey string
CallerKey string
FunctionKey string
StacktraceKey string
SkipLineEnding bool
LineEnding string
EncodeLevel LevelEncoder
EncodeTime TimeEncoder
EncodeDuration DurationEncoder
EncodeCaller CallerEncoder
EncodeName NameEncoder
ConsoleSeparator string
NewReflectedEncoder func(io.Writer) ReflectedEncoder
}encoderConfig := zapcore.EncoderConfig{
TimeKey: "ts",
LevelKey: "level",
NameKey: "logger",
CallerKey: "caller",
FunctionKey: zapcore.OmitKey,
MessageKey: "msg",
StacktraceKey: "stacktrace",
LineEnding: zapcore.DefaultLineEnding,
EncodeLevel: zapcore.LowercaseLevelEncoder,
EncodeTime: zapcore.ISO8601TimeEncoder,
EncodeDuration: zapcore.SecondsDurationEncoder,
EncodeCaller: zapcore.ShortCallerEncoder,
}
config := zap.Config{
Level: zap.NewAtomicLevelAt(zapcore.InfoLevel),
Encoding: "json",
EncoderConfig: encoderConfig,
OutputPaths: []string{"stdout"},
}
logger, _ := config.Build()func RegisterEncoder(name string, constructor func(zapcore.EncoderConfig) (zapcore.Encoder, error)) errorRegister a custom encoder that can be referenced by name in Config.
Example:
zap.RegisterEncoder("custom", func(config zapcore.EncoderConfig) (zapcore.Encoder, error) {
return &myCustomEncoder{config: config}, nil
})
config := zap.NewProductionConfig()
config.Encoding = "custom"
logger, _ := config.Build()func Open(paths ...string) (zapcore.WriteSyncer, func(), error)Open multiple output paths and combine them into a single WriteSyncer. Returns a cleanup function to close opened files.
Supported URL schemes:
RegisterSinkfunc RegisterSink(scheme string, factory func(*url.URL) (Sink, error)) error
type Sink interface {
zapcore.WriteSyncer
Close() error
}Register a custom sink factory for URLs with a specific scheme.
Example:
zap.RegisterSink("kafka", func(u *url.URL) (zap.Sink, error) {
return &kafkaSink{broker: u.Host, topic: u.Path}, nil
})
config := zap.NewProductionConfig()
config.OutputPaths = []string{"kafka://broker:9092/logs"}
logger, _ := config.Build()func CombineWriteSyncers(writers ...zapcore.WriteSyncer) zapcore.WriteSyncerUtility function to combine multiple WriteSyncers into a single, locked WriteSyncer. If no inputs are supplied, returns a no-op WriteSyncer.
Example:
file1, _ := os.Create("/var/log/app1.log")
file2, _ := os.Create("/var/log/app2.log")
// Write to both files
syncer := zap.CombineWriteSyncers(
zapcore.AddSync(file1),
zapcore.AddSync(file2),
)
core := zapcore.NewCore(
zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()),
syncer,
zapcore.InfoLevel,
)
logger := zap.New(core)package main
import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
func main() {
// Build logger with full customization
config := zap.Config{
Level: zap.NewAtomicLevelAt(zapcore.InfoLevel),
Development: false,
Sampling: &zap.SamplingConfig{
Initial: 100,
Thereafter: 100,
},
Encoding: "json",
EncoderConfig: zapcore.EncoderConfig{
TimeKey: "timestamp",
LevelKey: "severity",
NameKey: "logger",
CallerKey: "caller",
FunctionKey: zapcore.OmitKey,
MessageKey: "message",
StacktraceKey: "stacktrace",
LineEnding: zapcore.DefaultLineEnding,
EncodeLevel: zapcore.CapitalLevelEncoder,
EncodeTime: zapcore.ISO8601TimeEncoder,
EncodeDuration: zapcore.StringDurationEncoder,
EncodeCaller: zapcore.ShortCallerEncoder,
},
OutputPaths: []string{"stdout", "/var/log/myapp.log"},
ErrorOutputPaths: []string{"stderr"},
InitialFields: map[string]interface{}{
"service": "myapp",
"version": "1.2.3",
},
}
logger, err := config.Build(
zap.AddCaller(),
zap.AddStacktrace(zapcore.ErrorLevel),
zap.Hooks(func(entry zapcore.Entry) error {
if entry.Level >= zapcore.ErrorLevel {
// Send to monitoring system
}
return nil
}),
)
if err != nil {
panic(err)
}
defer logger.Sync()
logger.Info("application started")
}func LevelFlag(name string, defaultLevel zapcore.Level, usage string) *zapcore.LevelCreate a flag.Value for configuring log level via command-line flags.
Example:
import (
"flag"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
var logLevel = zap.LevelFlag("log-level", zapcore.InfoLevel, "minimum log level")
func main() {
flag.Parse()
config := zap.NewProductionConfig()
config.Level = zap.NewAtomicLevelAt(*logLevel)
logger, _ := config.Build()
logger.Info("application started", zap.Stringer("level", *logLevel))
}
// Run with: ./app -log-level=debug