uber-go/zap: a zero-allocation structured logger for Go, and when it is worth the setup cost
Blazing fast, structured, leveled logging in Go.
At a glance
- What is it?
- Zap is Uber's structured, leveled logging library for Go, built around a reflection-free JSON encoder and two logger APIs. It is fast, stable and MIT licensed, but the README's own benchmark table shows it is not the fastest option in every row.
- Who is it for?
- Adopt zap when you need structured, leveled logs in a Go service and can accept the typed Field API, or when you are migrating off logrus and want a stable 1.x module. Skip it if your logs are a handful of lines per hour, if you want the standard library's slog with no extra dependency, or if raw throughput per operation is the only criterion, since the README's own table shows zerolog ahead on time.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 13 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 29, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What zap solves, and who ends up using it
The problem zap addresses is the cost of turning log statements into machine-readable output. The README frames it directly: for applications that log in the hot path, reflection-based serialization and string formatting are expensive, they are CPU-intensive and they make many small allocations. Using encoding/json and fmt.Fprintf to log lots of interface{} values makes the application slow.
Zap's answer is a reflection-free, zero-allocation JSON encoder, with the base Logger written to avoid serialization overhead and allocations wherever possible. The SugaredLogger is built on top of that foundation, so a team can pick between counting allocations and using a looser, printf-style API.
The audience follows from that. This is a library for Go services that emit structured logs at volume: request handlers, background workers, anything where a log call sits inside a loop or a per-request path. If your program writes a dozen log lines a minute, the performance argument does not apply to you, and the typed API is pure overhead. The README's own framing is that the SugaredLogger is for contexts where performance is nice but not critical, and the Logger is for when performance and type safety are critical.
Two loggers, one core: how the Logger and SugaredLogger differ
Zap ships two entry points over the same core. The Logger takes strongly typed Field values such as zap.String, zap.Int and zap.Duration. The SugaredLogger wraps that Logger and accepts loosely typed key-value pairs plus Infof-style formatting.
The README gives both forms side by side. The structured call is logger.Info("failed to fetch URL", zap.String("url", url), zap.Int("attempt", 3), zap.Duration("backoff", time.Second)). The sugared equivalent is sugar.Infow("failed to fetch URL", "url", url, "attempt", 3, "backoff", time.Second), with sugar.Infof available for printf-style messages.
The trade-off is visible in the README's benchmark tables, which the project describes as its own benchmarking suite. Logging a message and 10 fields, the base Logger is listed at 656 ns/op with 5 allocs/op and the sugared logger at 935 ns/op with 10 allocs/op. With a logger that already carries 10 fields of context, the base Logger is listed at 67 ns/op and 0 allocs/op against 84 ns/op and 1 alloc for the sugared version. So the convenience layer roughly doubles allocations in the first case and adds one allocation in the second. That is the whole argument for the typed API: it is not stylistic, it is the allocation count.
The README also notes that its benchmark comparisons may run against slightly older versions of other packages, and that versions are pinned in benchmarks/go.mod. Treat the table as a directional signal from the maintainers, not as an independent measurement.
Installing zap and writing a first structured log
Installation is a single Go module fetch. The README gives this command:
go get -u go.uber.org/zapThe same section states that zap only supports the two most recent minor versions of Go. The go.mod in the repository declares go 1.19, so check your toolchain against the current Go releases before pinning.
The README's quick start for the typed Logger looks like this:
logger, _ := zap.NewProduction()
defer logger.Sync()
logger.Info("failed to fetch URL",
zap.String("url", url),
zap.Int("attempt", 3),
zap.Duration("backoff", time.Second),
)NewProduction returns a logger configured for production output, and the deferred Sync flushes any buffer. What you should see is a JSON line on stderr carrying the message and the three fields as separate keys, not as a formatted string.
If you want the printf-style API instead, the README's sugared example is:
logger, _ := zap.NewProduction()
defer logger.Sync()
sugar := logger.Sugar()
sugar.Infow("failed to fetch URL",
"url", url,
"attempt", 3,
"backoff", time.Second,
)
sugar.Infof("Failed to fetch URL: %s", url)Note that both examples ignore the error from NewProduction. In real code that error matters, because a configuration problem at startup will otherwise surface as a nil logger panic on the first log call.
Where zap is the wrong tool, and what its benchmarks do not say
The most honest limitation is printed in zap's own README. In the table for logging a message with 10 fields, zerolog is listed at 380 ns/op and 1 alloc/op against zap's 656 ns/op and 5 allocs/op. In the table for a logger with 10 fields of context, zerolog is at 35 ns/op and 0 allocs/op against zap's 67 ns/op and 0 allocs/op. Zap is faster than go-kit, slog, apex/log, log15 and logrus in these tables, and faster than the standard library on a static string, but it is not the fastest entry overall. Anyone choosing zap purely on nanoseconds per operation should read those rows before deciding.
The second limitation is the API surface itself. The base Logger only supports structured logging, so every call site needs typed fields. That is a real cost when you are porting a codebase full of printf calls, and the sugared logger that eases the port is the one that allocates more.
The third is scope. Zap is a logging library, not a log pipeline. There is no built-in shipping, sampling configuration beyond what the package offers, or retention story in the README. It writes where you point it, and the rest is your infrastructure.
Finally, the README's performance claims come from the project's own suite, and the README itself says to take benchmarks with a grain of salt and that comparison versions may be slightly older. There is no third-party measurement in the README, so none should be implied.
zap against the standard library's slog
The natural alternative in modern Go is slog from the standard library, and the README's tables include it. Logging a message and 10 fields, slog is listed at 2481 ns/op with 42 allocs/op and slog with LogAttrs at 2479 ns/op with 40 allocs/op, against zap's 656 ns/op and 5 allocs/op. On a logger with 10 fields of context, slog is at 193 ns/op and 0 allocs/op, LogAttrs at 200 ns/op and 0 allocs/op, against zap's 67 ns/op and 0 allocs/op.
The difference in approach is dependency and ownership. slog ships with the toolchain, so there is nothing to add to go.mod and no version to track; zap is a module you pin, and the README tells semver-aware users to pin zap to ^1. In exchange for that dependency, zap offers a reflection-free encoder and a typed Field API that predates slog's LogAttrs, plus a sugared layer that slog does not have.
If your logging volume is low, slog's numbers are irrelevant and the standard library wins on dependency count alone. If you are already on logrus, the README's tables put logrus at 11654 ns/op and 79 allocs/op for the 10-field case, which is the migration argument zap's own documentation makes implicitly.
Stability, licensing and the cost of staying current
The README's development status section states that all APIs are finalized and that no breaking changes will be made in the 1.x series, with a recommendation to pin zap to ^1. That is an unusually strong compatibility promise for a Go library and it sets the upgrade cost: patch and minor releases inside 1.x should not require code changes.
The release history shows v1.28.0 on 2026-04-28, v1.27.1 on 2025-11-19 and exp/v0.3.0 on 2024-10-22. The exp module is versioned separately at v0, which means it carries no compatibility promise; code that imports from exp/ should be treated as the part most likely to move. Note also that the repository's Makefile treats several directories as independent Go modules (., ./exp, ./benchmarks, ./zapgrpc/internal/test), so a dependency bump is not a single-module operation for the maintainers.
Licensing is MIT per the repository's LICENSE file and the README's closing line. MIT is permissive and imposes no copyleft obligation on your application, but this is a description of the licence identifier and not legal advice; if you redistribute zap or a modified copy, read the licence text and your own counsel's guidance.
On maintenance: the last push to the default branch was on 2026-09-16, and the repository is not archived.
Editorial conclusion
Adopt zap when you need structured, leveled logs in a Go service and can accept the typed Field API, or when you are migrating off logrus and want a stable 1.x module. Skip it if your logs are a handful of lines per hour, if you want the standard library's slog with no extra dependency, or if raw throughput per operation is the only criterion, since the README's own table shows zerolog ahead on time. Verify first that your Go toolchain is one of the two most recent minor versions, since the README states zap only supports those, and check the pinned versions in benchmarks/go.mod before quoting any of the numbers above.
Frequently asked questions
What is uber-go/zap used for?
It is a structured, leveled logging library for Go. The README positions it for applications that log in the hot path, where reflection-based serialization and string formatting are too expensive, and it provides both a typed Logger and a printf-style SugaredLogger.
How do I install uber-go/zap?
The README gives a single command, go get -u go.uber.org/zap, and notes that zap only supports the two most recent minor versions of Go.
What is the difference between the zap Logger and the SugaredLogger?
The Logger takes strongly typed Field values such as zap.String and zap.Int and only supports structured logging. The SugaredLogger wraps it with loosely typed key-value pairs and printf-style methods, and the README's tables list it as allocating more per call than the base Logger.
Is uber-go/zap faster than the standard library and slog?
In the README's own benchmark tables zap is listed as faster than the standard library on a static string and faster than slog in the 10-field cases. The same tables list zerolog ahead of zap on time per operation, so zap is not the fastest entry in every row.
What licence does uber-go/zap use?
The repository's LICENSE file and the README's closing line both state the MIT License.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/uber-go-zap)