Choosing a logging library for your Go service sounds trivial until you're debugging a latency spike at 2 AM and your logs are consuming 15% of the CPU budget. The three dominant options—uber-go/zap, the standard library log/slog, and rs/zerolog—all get the job done, but they make very different trade-offs around performance, API ergonomics, and ecosystem fit. This guide cuts through the noise with working code and concrete production advice.

What structured logging actually means in Go

"Structured logging" means emitting log lines as key-value pairs (typically JSON) instead of free-form strings. The difference matters when your logs flow into a SIEM, a log aggregator like Loki or Datadog, or anything that needs to query on specific fields.

A naive fmt.Printf("user %d login failed", userID) is fast to write but a nightmare to parse at scale. A structured equivalent—logger.Error("login failed", "user_id", userID, "ip", ip)—is machine-readable without regex and plays well with every log aggregation tool built in the last decade.

The three contenders