or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

configuration.mdfields.mdindex.mdintegrations.mdlogger.mdtesting.mdzapcore.md
tile.json

configuration.mddocs/

Logger Configuration and Creation

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.

Logger Creation Functions

Preset Constructors

func NewProduction(options ...Option) (*Logger, error)
func NewDevelopment(options ...Option) (*Logger, error)
func NewExample(options ...Option) *Logger
func NewNop() *Logger
  • NewProduction: Production-ready logger (JSON encoding, InfoLevel+, stderr)
  • NewDevelopment: Development logger (console encoding, DebugLevel+, stderr, stack traces on warnings)
  • NewExample: Deterministic logger for testable examples (no timestamps)
  • NewNop: No-op logger that discards all logs

Custom Logger Constructor

func New(core zapcore.Core, options ...Option) *Logger

Create a logger from a custom zapcore.Core with optional configuration options.

Must Helper

func Must(logger *Logger, err error) *Logger

Panic if error is non-nil, otherwise return the logger. Useful for initialization:

logger := zap.Must(zap.NewProduction())

Declarative Configuration

Config Type

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:

  • Level: Minimum log level (dynamic, can be changed at runtime)
  • Development: Enable development mode (DPanic panics, stack traces more liberal)
  • DisableCaller: Disable caller annotation (file:line)
  • DisableStacktrace: Disable automatic stack traces
  • Sampling: Log sampling configuration (nil disables sampling)
  • Encoding: Encoder name ("json", "console", or registered custom encoder)
  • EncoderConfig: Encoder configuration options
  • OutputPaths: Output destinations (URLs or file paths, e.g., ["stdout", "/var/log/app.log"])
  • ErrorOutputPaths: Destinations for internal logger errors
  • InitialFields: Fields added to all log entries

Config Methods

func (cfg Config) Build(opts ...Option) (*Logger, error)

Build a logger from the configuration, optionally applying additional options.

Configuration Presets

func NewProductionConfig() Config
func NewDevelopmentConfig() Config

Get pre-configured Config structs for production or development environments.

Usage Examples

Using Config with Customization

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()

JSON Configuration

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()

Sampling Configuration

SamplingConfig Type

type SamplingConfig struct {
    Initial    int
    Thereafter int
    Hook       func(zapcore.Entry, zapcore.SamplingDecision)
}

Control log sampling to reduce volume:

  • Initial: Number of entries to log per second before sampling
  • Thereafter: After Initial, log every Thereafter-th entry
  • Hook: Optional callback for sampling decisions

Usage Example

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

Options provide programmatic control over logger behavior. They can be passed to preset constructors, the New function, or WithOptions.

Core Modification Options

func WrapCore(f func(zapcore.Core) zapcore.Core) Option

Wrap 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)
}))

Hook Options

func Hooks(hooks ...func(zapcore.Entry) error) Option

Register 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
}))

Field Options

func Fields(fs ...Field) Option

Add initial context fields to the logger.

Example:

logger := zap.NewProduction(zap.Fields(
    zap.String("service", "api"),
    zap.String("version", "1.0.0"),
))

Output Options

func ErrorOutput(w zapcore.WriteSyncer) Option

Set where internal logger errors are written (e.g., configuration errors, sync failures).

Development Mode Option

func Development() Option

Enable development mode: DPanic logs panic, stack traces more liberal.

Caller Options

func AddCaller() Option
func WithCaller(enabled bool) Option
func AddCallerSkip(skip int) Option
  • AddCaller: Enable caller annotation (file:line)
  • WithCaller: Explicitly enable or disable caller annotation
  • AddCallerSkip: Skip skip frames when determining caller

Example:

// 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))
}

Stack Trace Options

func AddStacktrace(lvl zapcore.LevelEnabler) Option

Automatically add stack traces for logs at or above the specified level.

Example:

logger := zap.NewProduction(zap.AddStacktrace(zapcore.ErrorLevel))

Level Options

func IncreaseLevel(lvl zapcore.LevelEnabler) Option

Set 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))

Panic and Fatal Hook Options

func WithPanicHook(hook zapcore.CheckWriteHook) Option
func WithFatalHook(hook zapcore.CheckWriteHook) Option

Customize behavior when panic or fatal logs are written.

Example:

logger := zap.NewProduction(
    zap.WithFatalHook(zapcore.WriteThenGoexit),  // Exit without calling os.Exit
)

Deprecated Hook Options

func OnFatal(action zapcore.CheckWriteAction) Option

Deprecated: Use WithFatalHook instead. This option sets the action to take after fatal logs.

Clock Option

func WithClock(clock zapcore.Clock) Option

Provide a custom clock for timestamps. Useful for testing.

AtomicLevel

AtomicLevel Type

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.

AtomicLevel Functions

func NewAtomicLevel() AtomicLevel
func NewAtomicLevelAt(l zapcore.Level) AtomicLevel
func ParseAtomicLevel(text string) (AtomicLevel, error)
  • NewAtomicLevel: Create at InfoLevel
  • NewAtomicLevelAt: Create at specific level
  • ParseAtomicLevel: Parse from string ("debug", "info", etc.)

AtomicLevel Methods

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)

Usage Examples

Runtime Level Changes

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")
}

HTTP Endpoint for Level Changes

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=debug

Encoder Configuration

EncoderConfig Type

type EncoderConfig = zapcore.EncoderConfig

EncoderConfig customizes how log entries are formatted.

Preset Encoder Configs

func NewProductionEncoderConfig() zapcore.EncoderConfig
func NewDevelopmentEncoderConfig() zapcore.EncoderConfig
  • Production: JSON keys, epoch timestamp, lowercase level
  • Development: Human-readable console format, ISO8601 time, colored output

EncoderConfig Fields

type 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
}

Usage Example

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()

Custom Encoders

Registering Custom Encoders

func RegisterEncoder(name string, constructor func(zapcore.EncoderConfig) (zapcore.Encoder, error)) error

Register 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()

Sinks and Output

Opening Output Paths

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:

  • No scheme or "file": Local file paths (e.g., "/var/log/app.log")
  • Special paths: "stdout", "stderr"
  • Custom schemes: Register with RegisterSink

Registering Custom Sinks

func 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()

Combining Write Syncers

func CombineWriteSyncers(writers ...zapcore.WriteSyncer) zapcore.WriteSyncer

Utility 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)

Complete Configuration Example

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")
}

Level Flag for CLI

func LevelFlag(name string, defaultLevel zapcore.Level, usage string) *zapcore.Level

Create 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