This document covers integration packages for using zap with gRPC and standard library IO operations.
The zapgrpc package provides a logger compatible with gRPC's logging interface.
import "go.uber.org/zap/zapgrpc"The zapgrpc Logger adapts zap's Logger to be compatible with grpclog.LoggerV2.
type Logger struct {
// Has unexported fields
}func NewLogger(l *zap.Logger, options ...Option) *LoggerCreates a new gRPC-compatible logger from a zap Logger.
All methods implement the grpclog.LoggerV2 interface:
// Info-level logging
func (l *Logger) Info(args ...interface{})
func (l *Logger) Infof(format string, args ...interface{})
func (l *Logger) Infoln(args ...interface{})
// Warning-level logging
func (l *Logger) Warning(args ...interface{})
func (l *Logger) Warningf(format string, args ...interface{})
func (l *Logger) Warningln(args ...interface{})
// Error-level logging
func (l *Logger) Error(args ...interface{})
func (l *Logger) Errorf(format string, args ...interface{})
func (l *Logger) Errorln(args ...interface{})
// Fatal-level logging
func (l *Logger) Fatal(args ...interface{})
func (l *Logger) Fatalf(format string, args ...interface{})
func (l *Logger) Fatalln(args ...interface{})
// Verbosity check
func (l *Logger) V(level int) bool// Deprecated: use Info
func (l *Logger) Print(args ...interface{})
// Deprecated: use Infof
func (l *Logger) Printf(format string, args ...interface{})
// Deprecated: use Infoln
func (l *Logger) Println(args ...interface{})type Option interface {
// Has unexported methods
}
// Deprecated: configures Logger to print at zap.DebugLevel
func WithDebug() Optionimport (
"go.uber.org/zap"
"go.uber.org/zap/zapgrpc"
"google.golang.org/grpc/grpclog"
)
// Create zap logger
logger, _ := zap.NewProduction()
// Create gRPC logger from zap
grpcLogger := zapgrpc.NewLogger(logger)
// Set as global gRPC logger
grpclog.SetLoggerV2(grpcLogger)
// Now gRPC will use zap for all its logging// Create zap logger at debug level for verbose gRPC logs
logger, _ := zap.NewDevelopment()
// Create gRPC logger
grpcLogger := zapgrpc.NewLogger(logger.Named("grpc"))
grpclog.SetLoggerV2(grpcLogger)// Create structured logger with context
logger, _ := zap.NewProduction()
contextLogger := logger.With(
zap.String("service", "api"),
zap.String("version", "v1.0"),
)
// gRPC will include context in all logs
grpcLogger := zapgrpc.NewLogger(contextLogger)
grpclog.SetLoggerV2(grpcLogger)// Create different loggers for different gRPC servers
prodLogger, _ := zap.NewProduction()
grpcProdLogger := zapgrpc.NewLogger(prodLogger.Named("grpc.prod"))
adminLogger, _ := zap.NewDevelopment()
grpcAdminLogger := zapgrpc.NewLogger(adminLogger.Named("grpc.admin"))
// Use different loggers for different servers
prodServer := grpc.NewServer(
grpc.UnaryInterceptor(loggingInterceptor(grpcProdLogger)),
)
adminServer := grpc.NewServer(
grpc.UnaryInterceptor(loggingInterceptor(grpcAdminLogger)),
)The zapio package provides tools for using zap with standard library IO operations.
import "go.uber.org/zap/zapio"Writer is an io.Writer that writes to a zap Logger, splitting output on line boundaries.
type Writer struct {
Log *zap.Logger
Level zapcore.Level
// Has unexported fields
}// Write bytes to logger (implements io.Writer)
func (w *Writer) Write(bs []byte) (n int, err error)
// Sync flushes buffered data to logger without waiting for newline
func (w *Writer) Sync() error
// Close flushes buffered data and closes writer
func (w *Writer) Close() errorimport (
"log"
"go.uber.org/zap"
"go.uber.org/zap/zapio"
)
// Create zap logger
zapLogger, _ := zap.NewProduction()
// Create Writer at Info level
writer := &zapio.Writer{
Log: zapLogger,
Level: zapcore.InfoLevel,
}
defer writer.Close()
// Set as output for standard library logger
log.SetOutput(writer)
// Standard library log calls now go through zap
log.Println("This message goes through zap")// Many libraries write to io.Writer
// Capture their output in structured logs
zapLogger, _ := zap.NewProduction()
logWriter := &zapio.Writer{
Log: zapLogger.Named("external.lib"),
Level: zapcore.WarnLevel,
}
defer logWriter.Close()
// Pass to library expecting io.Writer
externalLib.SetOutput(logWriter)
// Library's output appears in zap logszapLogger, _ := zap.NewProduction()
// Create writers at different levels
infoWriter := &zapio.Writer{
Log: zapLogger,
Level: zapcore.InfoLevel,
}
errorWriter := &zapio.Writer{
Log: zapLogger,
Level: zapcore.ErrorLevel,
}
// Use appropriate writer based on context
cmd := exec.Command("some-command")
cmd.Stdout = infoWriter
cmd.Stderr = errorWriter
// Command output logged at appropriate levels
cmd.Run()// Writer buffers until newline or explicit flush
writer := &zapio.Writer{
Log: logger,
Level: zapcore.InfoLevel,
}
// These writes are buffered
writer.Write([]byte("partial "))
writer.Write([]byte("line "))
// Force flush without newline
writer.Sync()
// Or flush on close
writer.Close()import (
"os/exec"
"go.uber.org/zap"
"go.uber.org/zap/zapio"
)
logger, _ := zap.NewProduction()
// Create writers for command output
stdoutWriter := &zapio.Writer{
Log: logger.Named("cmd.stdout"),
Level: zapcore.InfoLevel,
}
defer stdoutWriter.Close()
stderrWriter := &zapio.Writer{
Log: logger.Named("cmd.stderr"),
Level: zapcore.ErrorLevel,
}
defer stderrWriter.Close()
// Run command with logging
cmd := exec.Command("some-command", "arg1", "arg2")
cmd.Stdout = stdoutWriter
cmd.Stderr = stderrWriter
if err := cmd.Run(); err != nil {
logger.Error("command failed", zap.Error(err))
}
// Command output appears in structured logsimport (
"net/http"
"go.uber.org/zap"
"go.uber.org/zap/zapio"
)
logger, _ := zap.NewProduction()
// Log HTTP server errors
errorWriter := &zapio.Writer{
Log: logger.Named("http.server"),
Level: zapcore.ErrorLevel,
}
server := &http.Server{
Addr: ":8080",
ErrorLog: log.New(errorWriter, "", 0),
}
// Server errors logged through zap
server.ListenAndServe()// Writer splits on newlines by default
// For different behavior, wrap in custom writer
type CustomWriter struct {
writer *zapio.Writer
}
func (cw *CustomWriter) Write(p []byte) (n int, err error) {
// Custom processing before writing
processed := processLines(p)
return cw.writer.Write(processed)
}
zapLogger, _ := zap.NewProduction()
baseWriter := &zapio.Writer{
Log: zapLogger,
Level: zapcore.InfoLevel,
}
customWriter := &CustomWriter{writer: baseWriter}
// Use customWriter where io.Writer is neededWith() to add service context to gRPC loggersClose() or defer close on zapio.Writers to flush buffersSync() when you need to ensure partial lines are loggedWrite() can return errors; handle appropriately