or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

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

integrations.mddocs/

Integration Packages

Zap provides several integration packages for connecting with other systems and frameworks: gRPC logging, IO wrapping, and experimental features including slog support.

Package: zapgrpc

Import: import "go.uber.org/zap/zapgrpc"

Adapter for using zap with gRPC's logging interface (grpclog.LoggerV2).

Creating gRPC Logger

func NewLogger(l *zap.Logger, options ...Option) *Logger

Create a grpclog.LoggerV2-compatible logger backed by a zap logger.

Parameters:

  • l: Zap logger to wrap
  • options: Configuration options

Logger Type

type Logger struct {
    // unexported fields
}

Implements grpclog.LoggerV2 and grpclog.Logger interfaces.

Logger Methods (grpclog.LoggerV2)

func (l *Logger) Info(args ...interface{})
func (l *Logger) Infoln(args ...interface{})
func (l *Logger) Infof(format string, args ...interface{})

func (l *Logger) Warning(args ...interface{})
func (l *Logger) Warningln(args ...interface{})
func (l *Logger) Warningf(format string, args ...interface{})

func (l *Logger) Error(args ...interface{})
func (l *Logger) Errorln(args ...interface{})
func (l *Logger) Errorf(format string, args ...interface{})

func (l *Logger) Fatal(args ...interface{})
func (l *Logger) Fatalln(args ...interface{})
func (l *Logger) Fatalf(format string, args ...interface{})

func (l *Logger) V(level int) bool

Logger Methods (grpclog.Logger)

func (l *Logger) Print(args ...interface{})
func (l *Logger) Printf(format string, args ...interface{})
func (l *Logger) Println(args ...interface{})

Options

type Option interface {
    // unexported method
}

func WithDebug() Option
  • WithDebug: Use debug level for Print/Printf/Println methods (default is info level)

Usage Examples

Basic gRPC Integration

import (
    "google.golang.org/grpc"
    "google.golang.org/grpc/grpclog"
    "go.uber.org/zap"
    "go.uber.org/zap/zapgrpc"
)

func main() {
    zapLogger, _ := zap.NewProduction()
    defer zapLogger.Sync()

    // Replace gRPC's global logger
    grpcLogger := zapgrpc.NewLogger(zapLogger)
    grpclog.SetLoggerV2(grpcLogger)

    // gRPC will now log through zap
    conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())
    // ...
}

With Debug Level for Print Methods

zapLogger, _ := zap.NewProduction()
grpcLogger := zapgrpc.NewLogger(zapLogger, zapgrpc.WithDebug())
grpclog.SetLoggerV2(grpcLogger)

// Print methods will log at debug level instead of info

With Scoped Logger

zapLogger, _ := zap.NewProduction()
grpcLogger := zapgrpc.NewLogger(
    zapLogger.Named("grpc").With(zap.String("component", "grpc-client")),
)
grpclog.SetLoggerV2(grpcLogger)

// All gRPC logs will be tagged with logger name and component field

Package: zapio

Import: import "go.uber.org/zap/zapio"

Provides an io.Writer implementation that logs each write as a separate log entry.

Writer Type

type Writer struct {
    Log   *zap.Logger
    Level zapcore.Level
}

Writer that splits output on newlines and logs each line at the specified level.

Fields:

  • Log: Zap logger to write entries to
  • Level: Log level for entries (e.g., zapcore.InfoLevel)

Writer Methods

func (w *Writer) Write(p []byte) (n int, err error)
func (w *Writer) Sync() error
func (w *Writer) Close() error
  • Write: Implements io.Writer. Splits on '\n' and logs each line.
  • Sync: Flush any buffered logs
  • Close: Close writer and flush logs

Usage Examples

Redirecting Command Output to Logger

import (
    "os/exec"
    "go.uber.org/zap"
    "go.uber.org/zap/zapio"
    "go.uber.org/zap/zapcore"
)

func runCommand(logger *zap.Logger) error {
    writer := &zapio.Writer{
        Log:   logger.Named("command"),
        Level: zapcore.InfoLevel,
    }
    defer writer.Close()

    cmd := exec.Command("ls", "-la")
    cmd.Stdout = writer
    cmd.Stderr = writer

    return cmd.Run()
    // Each line of output is logged as separate Info entry
}

Logging HTTP Response Bodies

func logResponse(logger *zap.Logger, resp *http.Response) error {
    writer := &zapio.Writer{
        Log:   logger.With(zap.String("url", resp.Request.URL.String())),
        Level: zapcore.DebugLevel,
    }
    defer writer.Close()

    _, err := io.Copy(writer, resp.Body)
    return err
    // Response body lines logged at debug level
}

Capturing Standard Library Log Output

import (
    "log"
    "go.uber.org/zap"
    "go.uber.org/zap/zapio"
    "go.uber.org/zap/zapcore"
)

func redirectStdLib(logger *zap.Logger) {
    writer := &zapio.Writer{
        Log:   logger.Named("stdlib"),
        Level: zapcore.InfoLevel,
    }

    log.SetOutput(writer)
    log.SetFlags(0) // zap will add timestamps

    log.Println("This goes through zap")
}

Package: exp/zapfield

Import: import "go.uber.org/zap/exp/zapfield"

Requires Go 1.18+

Experimental package providing generic field constructors using Go generics.

Generic String Field

func Str[K ~string, V ~string](k K, v V) zap.Field

Generic string field constructor. Accepts any types based on string.

Generic String Slice Field

func Strs[K ~string, V ~[]S, S ~string](k K, v V) zap.Field

Generic string slice field constructor.

Usage Examples

import (
    "go.uber.org/zap"
    "go.uber.org/zap/exp/zapfield"
)

type UserID string
type Username string

func logUser(logger *zap.Logger, id UserID, name Username) {
    logger.Info("user action",
        zapfield.Str("user_id", id),      // Works with UserID type
        zapfield.Str("username", name),   // Works with Username type
    )
}

type Tag string

func logTags(logger *zap.Logger, tags []Tag) {
    logger.Info("tagged content",
        zapfield.Strs("tags", tags),  // Works with []Tag
    )
}

Package: exp/zapslog

Import: import "go.uber.org/zap/exp/zapslog"

Requires Go 1.21+

Experimental package providing a slog.Handler implementation backed by zap, enabling integration with Go's structured logging package.

Creating slog Handler

func NewHandler(core zapcore.Core, opts ...HandlerOption) *Handler

Create a slog.Handler from a zapcore.Core.

Parameters:

  • core: Zapcore.Core to write log entries through
  • opts: Configuration options

Handler Type

type Handler struct {
    // unexported fields
}

Implements slog.Handler interface.

Handler Methods

func (h *Handler) Enabled(ctx context.Context, level slog.Level) bool
func (h *Handler) Handle(ctx context.Context, record slog.Record) error
func (h *Handler) WithAttrs(attrs []slog.Attr) slog.Handler
func (h *Handler) WithGroup(name string) slog.Handler

Standard slog.Handler interface implementation.

Handler Options

type HandlerOption interface {
    // unexported method
}

func WithCaller(enabled bool) HandlerOption
func WithCallerSkip(skip int) HandlerOption
func WithStacktrace(level slog.Level) HandlerOption
  • WithCaller: Enable/disable caller annotation
  • WithCallerSkip: Skip frames when determining caller
  • WithStacktrace: Add stack traces at or above specified level

Usage Examples

Basic slog Integration

import (
    "log/slog"
    "go.uber.org/zap"
    "go.uber.org/zap/exp/zapslog"
    "go.uber.org/zap/zapcore"
)

func setupSlog() {
    // Create zap core
    zapLogger, _ := zap.NewProduction()
    core := zapLogger.Core()

    // Create slog handler from zap core
    handler := zapslog.NewHandler(core)

    // Create slog logger
    logger := slog.New(handler)

    // Use slog API, logs through zap
    logger.Info("application started",
        slog.String("version", "1.0.0"),
        slog.Int("port", 8080),
    )
}

With Caller and Stack Traces

func setupSlogWithOptions() {
    zapLogger, _ := zap.NewProduction()

    handler := zapslog.NewHandler(
        zapLogger.Core(),
        zapslog.WithCaller(true),
        zapslog.WithStacktrace(slog.LevelError),
    )

    logger := slog.New(handler)

    logger.Error("error occurred")  // Includes caller and stack trace
}

Grouped Attributes

func slogGroups() {
    zapLogger, _ := zap.NewProduction()
    handler := zapslog.NewHandler(zapLogger.Core())
    logger := slog.New(handler)

    logger.Info("user event",
        slog.Group("user",
            slog.String("id", "123"),
            slog.String("name", "alice"),
        ),
        slog.Group("metadata",
            slog.String("ip", "192.168.1.1"),
            slog.Time("timestamp", time.Now()),
        ),
    )
    // Nested structure logged through zap
}

Migrating from slog to zap

// Library uses slog
func libraryFunction(logger *slog.Logger) {
    logger.Info("library operation",
        slog.String("operation", "process"),
        slog.Duration("duration", 100*time.Millisecond),
    )
}

// Application uses zap
func main() {
    zapLogger, _ := zap.NewProduction()
    defer zapLogger.Sync()

    // Wrap zap for slog-based library
    handler := zapslog.NewHandler(zapLogger.Core())
    slogLogger := slog.New(handler)

    libraryFunction(slogLogger)
    // Library logs go through zap
}

Integration Best Practices

Choose the Right Integration

  • zapgrpc: Use when integrating with gRPC services
  • zapio: Use for capturing output from commands, legacy code, or stdlib log
  • exp/zapfield: Use with custom types based on primitives (requires Go 1.18+)
  • exp/zapslog: Use when integrating with code using slog (requires Go 1.21+)

Performance Considerations

  • zapgrpc adds minimal overhead over direct zap logging
  • zapio buffers until newlines, may affect streaming behavior
  • exp/zapfield generics have zero runtime cost
  • exp/zapslog adds small overhead for slog to zap attribute conversion

Migration Strategies

Gradual gRPC Migration

// Phase 1: Tee to both loggers
oldLogger := grpclog.Component("component")
zapLogger, _ := zap.NewProduction()
grpcLogger := zapgrpc.NewLogger(zapLogger)

// Use grpcLogger but keep oldLogger for comparison

Incremental slog Adoption

// Core application uses zap
zapLogger, _ := zap.NewProduction()

// New modules use slog
handler := zapslog.NewHandler(zapLogger.Core())
slogLogger := slog.New(handler)

// Both log through same zap core
newModule.Run(slogLogger)
legacyModule.Run(zapLogger)

Common Patterns

Multi-Output with zapio

zapLogger, _ := zap.NewProduction()

// Tee stdout and stderr to logger
stdoutWriter := &zapio.Writer{Log: zapLogger.Named("stdout"), Level: zapcore.InfoLevel}
stderrWriter := &zapio.Writer{Log: zapLogger.Named("stderr"), Level: zapcore.ErrorLevel}

cmd := exec.Command("./application")
cmd.Stdout = io.MultiWriter(os.Stdout, stdoutWriter)
cmd.Stderr = io.MultiWriter(os.Stderr, stderrWriter)

cmd.Run()
// Output visible on console AND logged through zap

Context-Aware slog Handler

type contextKey int

const userIDKey contextKey = 0

func enrichedHandler(core zapcore.Core) slog.Handler {
    handler := zapslog.NewHandler(core)

    // Wrap to add context values
    return &contextHandler{
        Handler: handler,
    }
}

type contextHandler struct {
    slog.Handler
}

func (h *contextHandler) Handle(ctx context.Context, r slog.Record) error {
    if userID, ok := ctx.Value(userIDKey).(string); ok {
        r.AddAttrs(slog.String("user_id", userID))
    }
    return h.Handler.Handle(ctx, r)
}