Inside uber-go/zap: reflection-free structured logging for Go
Blazing fast, structured, leveled logging in Go.
At a glance
- What is it?
- uber-go/zap is Uber's Go logging library: a reflection-free JSON encoder wrapped in a fast base Logger and a looser SugaredLogger. It is cheap in hot paths and frozen in 1.x, but zerolog posts better raw timings and zap only supports the two newest minor Go versions.
- Who is it for?
- zap is a good fit for services that already run Go and want low-allocation structured logging with a frozen 1.x API and a familiar printf-style option for non-critical call sites. It is the wrong pick if your binding constraint is the raw nanosecond count, since zerolog posts lower times in all three benchmark tables, or if you are stuck on a Go release older than the two newest minor lines, since zap does not support it.
- 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 59 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 November 14, 2026, and from our analysis. They are not legal advice.
Star history
- uber-go/zap24,688 ★ · +35
| Repository | 2026-09-21 | 2026-11-14 |
|---|---|---|
| uber-go/zap | 24.7k | 24.7k |
35 Stars in the last 54 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
SugaredLogger adds printf-style calls while the base Logger stays typed
zap ships a single logging core and two logger types layered on top of it. The base Logger encodes strongly typed Field values and is the faster of the two, allocating far less. SugaredLogger wraps that Logger and adds a printf-style API, and it also accepts structured calls as loosely typed key-value pairs. Both are produced by the same constructor and both are closed with a Sync call that flushes any buffer. Here is the SugaredLogger path, which pairs a structured call with a templated one against the same logger:
logger, _ := zap.NewProduction()
defer logger.Sync() // flushes buffer, if any
sugar := logger.Sugar()
sugar.Infow("failed to fetch URL",
// Structured context as loosely typed key-value pairs.
"url", url,
"attempt", 3,
"backoff", time.Second,
)
sugar.Infof("Failed to fetch URL: %s", url)The same event through the base Logger uses Field constructors instead of bare values:
logger, _ := zap.NewProduction()
defer logger.Sync()
logger.Info("failed to fetch URL",
// Structured context as strongly typed Field values.
zap.String("url", url),
zap.Int("attempt", 3),
zap.Duration("backoff", time.Second),
)Installing the library is one module fetch:
go get -u go.uber.org/zapChoose the Logger when every call site sits in a hot path and you want the compiler to check field types. Choose SugaredLogger when printf-style convenience matters more than the last allocations.
A reflection-free JSON encoder is what keeps the base Logger cheap
The performance story rests on how zap serializes a log line. Reflection-based serialization and string formatting are CPU-intensive and make many small allocations, so logging many interface{} values with encoding/json and fmt.Fprintf slows an application down. zap takes a different route: it includes a reflection-free, zero-allocation JSON encoder, and the base Logger tries to avoid serialization overhead and allocations wherever possible. SugaredLogger is then built on top of that foundation, which is what lets you choose when to count every allocation and when a looser API is fine. The encoder, the field constructors, and the core writers live in separate files: encoder.go, field.go, writer.go, and the zapcore/ package. If your logging sits in a hot path, this encoder design is the reason zap is cheap there.
zerolog posts lower times and fewer allocations in every benchmark table
On raw speed and allocation count, zap does not lead its own benchmark suite. In the table that logs a message with ten fields, zap measures 656 ns/op and 5 allocs/op, while zerolog posts 380 ns/op and 1 alloc/op. In the table for a logger that already carries ten fields of context, zap is 67 ns/op with 0 allocs and zerolog is 35 ns/op with 0 allocs. In the table for a static string with no context, zap is 63 ns/op and zerolog is 32 ns/op. zap's own edge is allocation discipline against the heavier logging libraries: in the ten-field table logrus is 11654 ns/op and 79 allocs/op, and go-kit is 2249 ns/op. The consequence for readers is straightforward. If the only thing you care about is the smallest possible nanosecond count on an uncontended hot path, zerolog posts the better number in all three tables, and zap is the slower choice. The project itself tells you to take these benchmarks with a grain of salt.
zap supports only the two most recent minor Go versions
zap does not support older toolchains. Installing it is a single module fetch:
go get -u go.uber.org/zapBut the project states that zap only supports the two most recent minor versions of Go, and the go.mod file declares go 1.19 as its language floor. That means zap deliberately stops supporting Go releases beyond the two newest minor lines. The consequence for readers is a hard scheduling constraint. If your service or build pipeline is pinned to an older Go, you cannot freely take the latest zap without first moving your toolchain forward. Because zap has promised no breaking changes in the 1.x series and tells semver-aware dependency managers to pin to ^1, staying on the current major line will not save you on an unsupported Go. Plan the Go upgrade alongside the zap upgrade rather than after it.
The 1.x API is frozen, but exp/ is a separate pre-1.0 module
zap marks itself Stable: all APIs are finalized and no breaking changes will be made in the 1.x series, so users of semver-aware dependency management should pin zap to ^1. That stability covers the main module only. The repository also ships a separate module under exp/, and the exp module has its own release line, currently exp/v0.3.0 from October 2024, well behind the main module's v1.28.0 from April 2026. The build system treats these as independent modules: the Makefile lists the module directories as ., ./exp, ./benchmarks, and ./zapgrpc/internal/test, and it runs golangci-lint, tidy, and test steps in each. The consequence for readers is that anything you take from exp/ is pre-1.0 and outside the no-breaking-changes promise. Treat experimental packages as a place where the API can still move, and do not assume the ^1 pin protects them.
README.md is generated from a template and competitor versions are pinned
Two build details affect how you should read the repository. First, the README is generated, not hand-edited: the Makefile updatereadme target deletes README.md and rebuilds it by piping a template through a generator. The source of truth is .readme.tmpl, run through internal/readme. Editing README.md directly will be overwritten. Second, the comparison numbers come from a benchmarking suite whose competitor versions are pinned in a separate go.mod under benchmarks/. The project warns that it may be benchmarking against slightly older versions of other packages, so a number that looks favorable today can drift as those packages ship new releases. The consequence for readers is that the headline table is a snapshot, not a live leaderboard. If you are making a tool choice on these numbers, re-run the suite yourself against current versions rather than treating the checked-in percentages as permanent.
zap is a library you import, not a process you run
zap does not give you a logging daemon, a rotation policy, or a shipping pipeline. It is a Go module you import and construct in process. The package layout reflects that: the root holds logger.go, sugar.go, config.go, level.go, global.go, options.go, and time.go, and the writer and sink plumbing sits in writer.go, sink.go, and the zapcore/ package. There is no command in the module you start as a separate service. Constructors such as zap.NewProduction() return a logger you hold and Sync when you are done. The consequence for readers is that everything around the log line stays your job. Where the bytes go after the encoder writes them, how they are rotated, how long they are retained, and how they reach a collector are decisions you implement on top of zap, not features it ships. If you expected a turnkey logging service, zap is the wrong shape of tool.
Editorial conclusion
zap is a good fit for services that already run Go and want low-allocation structured logging with a frozen 1.x API and a familiar printf-style option for non-critical call sites. It is the wrong pick if your binding constraint is the raw nanosecond count, since zerolog posts lower times in all three benchmark tables, or if you are stuck on a Go release older than the two newest minor lines, since zap does not support it. Before committing, verify your Go version against that two-version support window, and re-run the benchmark suite against current competitor versions because the checked-in numbers pin older releases.
Frequently asked questions
What is the difference between Uber and Uber Go?
uber-go is the GitHub organization for Uber's open source Go libraries. This repository is one of them, published under the module path go.uber.org/zap with API docs hosted at pkg.go.dev, providing a structured, leveled logging package for Go. It is not the ride-hailing product.
What is an Uber Go?
An Uber Go library is a Go package that Uber maintains and publishes under the go.uber.org namespace. uber-go/zap is one: a structured, leveled logging library whose module is go.uber.org/zap and whose documentation lives at pkg.go.dev.
Does Uber Go mean sharing rides?
Not in this context. Here, uber-go refers to Uber's open source Go code, such as this zap logging library. zap is a Go package for structured, leveled output, with no connection to transporting passengers.
Is Uber written in Golang?
This repository is written in Go. zap targets the Go language, declares go 1.19 in its go.mod, and only supports the two most recent minor versions of Go. It ships as a Go module for structured, leveled logging rather than a standalone program.
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)Star history chart for your README
[](https://hysenlabs.com/projects/uber-go-zap)Was this useful?