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:

bash
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

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:

go
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:

go
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:

go
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.SugaredLogger for ergonomic logging if needed
  • Always Sync() on shutdown to flush buffers

log example

json
{
  "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.