Adding Zap Logger to Your Go Project: A Practical Guide⚡ ️
Logging is the backbone of observability in any production-grade system. If you’re building scalable, maintainable Go applications, Uber’s Zap is a high-performance, structured logging library that fits right in.
This article walks you through integrating Zap into your Go project — from setup to idiomatic usage.
🚀 Why Zap?
Zap is designed for speed and structure. Unlike traditional loggers that format strings, Zap encodes logs as structured fields. This makes it ideal for:
- High-throughput services
- JSON-based log pipelines
- Real-time debugging and monitoring
Go-simple project
Building on our previous article, we plan to integrate the Zap logger into our simple Golang project structure in order to provide more efficient and structured logging.
🛠️ Step 1: Install Zap
Add Zap to your project using go get:
go get go.uber.org/zap📦 Step 2: Create a Logger Module
Encapsulate logger setup in a reusable package. For example:
internal/pkg/logger/logger.go
package logger
import (
"go.uber.org/zap"
"go.uber.org/fx"
)
func NewLogger() (*zap.Logger, error) {
cfg := zap.NewProductionConfig()
cfg.OutputPaths = []string{
"stdout"
}
return cfg.Build()
}
var Module = fx.Provide(NewLogger)🧩 Step 3: Wire Logger into Your App
In your main.go, include the logger module:
package main
import (
"go.uber.org/fx"
"your_project/internal/pkg/logger"
)
func main() {
app := fx.New(logger.Module, // other modules...
)
app.Run()
}Replace your_project with your actual module name.
🧪 Step 4: Inject Logger into Services
Use constructor injection to pass the logger:
type ProductService struct {
log *zap.Logger
}
func NewProductService(log *zap.Logger) *ProductService {
log.Info("ProductService initialized")
return &ProductService{
log: log
}
}This ensures structured logging is available throughout your service layer.
🧼 Step 5: Graceful Shutdown
Flush logs on shutdown using FX lifecycle hooks:
func RegisterLoggerLifecycle(lc fx.Lifecycle, log *zap.Logger) {
lc.Append(fx.Hook{
OnStop: func(ctx context.Context) error {
return log.Sync()
},
})
}
var Module = fx.Options(
fx.Provide(NewLogger),
fx.Invoke(RegisterLoggerLifecycle),)🧠 Best Practices
- Use structured fields:
log.Info("User created", zap.String("email", user.Email)) - Avoid global loggers — inject via DI
- Use
zap.SugaredLoggerfor ergonomic logging if needed - Always
Sync()on shutdown to flush buffers
log example
{
"level": "info",
"ts": 1759755003.9162495,
"caller": "service/service.go:34",
"msg": "Product created",
"id": 8
}Git Commit
✅ Conclusion
Integrating Zap into your Go project gives you powerful, structured logging with minimal overhead. Whether you’re building microservices, APIs, or CLI tools, Zap helps you stay observant, performant, and production-ready.
Want help wiring Zap into your own project structure or adding contextual fields like request IDs or user agents? I’d be happy to walk you through it.
