Blazing fast structured logging from Uber.
import "go.uber.org/zap"
// Development (human readable)
func NewDevelopmentLogger() *zap.Logger {
logger, _ := zap.NewDevelopment()
return logger
}
// Production (JSON, optimized)
func NewProductionLogger() *zap.Logger {
logger, _ := zap.NewProduction()
return logger
}
// With options
func NewLogger() *zap.Logger {
cfg := zap.NewProductionConfig()
cfg.Level = zap.NewAtomicLevelAt(zap.InfoLevel)
cfg.OutputPaths = []string{"stdout", "/var/log/app.log"}
cfg.EncoderConfig.TimeKey = "timestamp"
cfg.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
logger, _ := cfg.Build()
return logger
}
// Logger - faster, type-safe
logger := zap.NewProduction()
logger.Info("User logged in",
zap.String("user_id", "123"),
zap.Int("attempt", 1),
zap.Duration("latency", time.Millisecond*50),
)
// SugaredLogger - convenient, slower
sugar := logger.Sugar()
sugar.Infow("User logged in",
"user_id", "123",
"attempt", 1,
"latency", time.Millisecond*50,
)
sugar.Infof("User %s logged in", userID)
logger.Info("Request completed",
zap.String("method", "GET"),
zap.String("path", "/users"),
zap.Int("status", 200),
zap.Duration("latency", latency),
zap.String("client_ip", clientIP),
zap.Any("headers", headers), // Any type
zap.Error(err), // Error field
zap.Stack("stacktrace"), // Stack trace
)
// Namespaced fields
logger.Info("Request",
zap.Namespace("http"),
zap.String("method", "GET"),
zap.Int("status", 200),
)
// Output: {"http": {"method": "GET", "status": 200}}
logger.Debug("Debug message") // -1
logger.Info("Info message") // 0
logger.Warn("Warning message") // 1
logger.Error("Error message") // 2
logger.DPanic("DPanic message") // 3 (panics in development)
logger.Panic("Panic message") // 4 (always panics)
logger.Fatal("Fatal message") // 5 (calls os.Exit(1))
// Check level before expensive operations
if logger.Core().Enabled(zap.DebugLevel) {
logger.Debug("Expensive", zap.Any("data", computeExpensiveData()))
}
// Add fields to all subsequent logs
requestLogger := logger.With(
zap.String("request_id", requestID),
zap.String("user_id", userID),
)
requestLogger.Info("Processing request")
requestLogger.Info("Request completed")
// Both logs include request_id and user_id
// Named logger
dbLogger := logger.Named("database")
dbLogger.Info("Query executed")
// Output: {"logger": "database", "msg": "Query executed"}
import (
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
func GinLogger(logger *zap.Logger) gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now()
path := c.Request.URL.Path
c.Next()
logger.Info("Request",
zap.String("method", c.Request.Method),
zap.String("path", path),
zap.Int("status", c.Writer.Status()),
zap.Duration("latency", time.Since(start)),
zap.String("client_ip", c.ClientIP()),
)
}
}
r := gin.New()
r.Use(GinLogger(logger))
cfg := zap.NewProductionConfig()
cfg.EncoderConfig = zapcore.EncoderConfig{
TimeKey: "ts",
LevelKey: "level",
NameKey: "logger",
CallerKey: "caller",
MessageKey: "msg",
StacktraceKey: "stacktrace",
LineEnding: zapcore.DefaultLineEnding,
EncodeLevel: zapcore.LowercaseLevelEncoder,
EncodeTime: zapcore.ISO8601TimeEncoder,
EncodeDuration: zapcore.MillisDurationEncoder,
EncodeCaller: zapcore.ShortCallerEncoder,
}
// Sample logs to reduce volume in production
cfg := zap.NewProductionConfig()
cfg.Sampling = &zap.SamplingConfig{
Initial: 100, // First 100 per second logged
Thereafter: 100, // Then every 100th message
}
// Replace global logger
logger := zap.NewProduction()
zap.ReplaceGlobals(logger)
// Use global
zap.L().Info("Using global logger")
zap.S().Infow("Using global sugar")
// Always sync before exit
defer logger.Sync()
zap.NewProduction() for JSON outputlogger.Sync() before exitzap.String() etc. over SugaredLoggerWith()Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer