uber-go/zap
Blazing fast, structured, leveled logging in Go.
Healthy across the board
Permissive license, no critical CVEs, actively maintained — safe to depend on.
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓Last commit 2w ago
- ✓33+ active contributors
- ✓Distributed ownership (top contributor 26% of recent commits)
Show 3 more →Show less
- ✓MIT licensed
- ✓CI configured
- ✓Tests present
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Healthy" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/uber-go/zap)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/uber-go/zap on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: uber-go/zap
Generated by RepoPilot · 2026-05-09 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/uber-go/zap shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
GO — Healthy across the board
- Last commit 2w ago
- 33+ active contributors
- Distributed ownership (top contributor 26% of recent commits)
- MIT licensed
- CI configured
- Tests present
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live uber-go/zap
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/uber-go/zap.
What it runs against: a local clone of uber-go/zap — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in uber-go/zap | Confirms the artifact applies here, not a fork |
| 2 | License is still MIT | Catches relicense before you depend on it |
| 3 | Default branch master exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 41 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of uber-go/zap. If you don't
# have one yet, run these first:
#
# git clone https://github.com/uber-go/zap.git
# cd zap
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of uber-go/zap and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "uber-go/zap(\\.git)?\\b" \\
&& ok "origin remote is uber-go/zap" \\
|| miss "origin remote is not uber-go/zap (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
&& ok "license is MIT" \\
|| miss "license drift — was MIT at generation time"
# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
&& ok "default branch master exists" \\
|| miss "default branch master no longer exists"
# 4. Critical files exist
test -f "logger.go" \\
&& ok "logger.go" \\
|| miss "missing critical file: logger.go"
test -f "field.go" \\
&& ok "field.go" \\
|| miss "missing critical file: field.go"
test -f "config.go" \\
&& ok "config.go" \\
|| miss "missing critical file: config.go"
test -f "encoder.go" \\
&& ok "encoder.go" \\
|| miss "missing critical file: encoder.go"
test -f "sugar.go" \\
&& ok "sugar.go" \\
|| miss "missing critical file: sugar.go"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 41 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~11d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/uber-go/zap"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
Zap is a blazing-fast, zero-allocation JSON logging library for Go that provides both a strongly-typed Logger (for hot-path performance) and a SugaredLogger (for convenience). It's 4-10x faster than competing Go logging packages by using a reflection-free encoder and pre-allocating buffers, making it production-grade for performance-critical applications. Monolithic package structure: core logger primitives in root (logger.go, sugar.go, config.go, encoder.go), a performant buffer pool in buffer/ (buffer.go, pool.go), comprehensive field encoders in array.go, and an experimental extension in exp/zapfield/. Benchmarks live in benchmarks/ comparing against zerolog, logrus, slog, and others.
👥Who it's for
Go backend engineers and infrastructure teams building high-throughput services (APIs, microservices, distributed systems) where logging overhead directly impacts application performance, plus framework maintainers integrating structured logging into Go libraries.
🌱Maturity & risk
Production-ready and actively maintained. The codebase shows strong engineering discipline with comprehensive test coverage (array_test.go, config_test.go, encoder_test.go, etc.), CI/CD via GitHub Actions (.github/workflows/go.yml), linting via golangci-yml, and code coverage tracking (.codecov.yml). Zap is widely adopted at Uber and across the Go ecosystem.
Very low risk. Uber maintains this as a core dependency with strict Go version support (only 2 most recent minor versions per README), minimal external dependencies, and a detailed CHANGELOG.md for tracking breaking changes. No single-maintainer risk visible given corporate backing. The only minor consideration is tight Go version constraints may require frequent updates for users on bleeding-edge Go.
Active areas of work
Active development tracked in CHANGELOG.md with recent additions (slog_test.go benchmarks suggest slog integration work). Dependabot configured (.github/dependabot.yml) for dependency updates. The exp/ experimental package indicates exploration of new field handling patterns.
🚀Get running
git clone https://github.com/uber-go/zap.git && cd zap && go mod download && go test ./...
Daily commands:
No server to run—this is a library. Run tests with make test (inferred from Makefile presence) or go test ./.... Run benchmarks with cd benchmarks && go test -bench=. ./....
🗺️Map of the codebase
logger.go— Core Logger type and its methods; the primary API that all users interact with for structured logging.field.go— Field type and factory functions (zap.String, zap.Int, etc.); defines how structured data is encoded into logs.config.go— Configuration struct and NewProduction/NewDevelopment constructors; entry point for logger initialization.encoder.go— Encoder interface and core encoding abstractions; bridge between logs and output formats (JSON, console).sugar.go— SugaredLogger wrapper providing printf-style and loosely-typed APIs; frequently used convenience layer.buffer/buffer.go— Buffer pool management for zero-allocation logging; critical performance optimization used throughout.internal/stacktrace/stack.go— Stack trace capture and formatting utilities; used for error logging and debugging output.
🛠️How to make changes
Add a new Field type (e.g., Custom object marshaler)
- Define your custom field factory function in field.go following the pattern of existing ones (e.g., String, Int). Use reflect or custom marshaling logic. (
field.go) - Register your field in the Field type's switch statement in the encoder if it requires special handling beyond generic ObjectMarshaler. (
encoder.go) - Add tests in field_test.go verifying correct JSON/console output and zero-allocation behavior. (
field_test.go)
Create a custom Encoder (e.g., Protocol Buffers format)
- Implement the Encoder interface defined in encoder.go: AddArray, AddObject, AddString, etc. methods. (
encoder.go) - Register your encoder factory in config.go or as a standalone NewCustomEncoder() constructor function. (
config.go) - Implement BuildEncoder or a similar factory to instantiate your encoder from EncoderConfig. (
config.go) - Add encoder tests ensuring output format correctness and benchmarking against JSON encoder. (
encoder_test.go)
Add a new Sink destination (e.g., send logs to a remote service)
- Implement the Sink interface (Close, Write, Sync methods) in sink.go or a new file. (
sink.go) - Register your sink in the sink registry via RegisterSink() so NewProduction/NewDevelopment can use URIs like 'myservice://endpoint'. (
sink.go) - Add tests in sink_test.go to verify write and sync semantics, error handling, and connection pooling if applicable. (
sink_test.go)
Extend SugaredLogger with domain-specific convenience methods
- Create a wrapper struct embedding *SugaredLogger in your application or a new file, then add custom methods that call sugar.Infow or sugar.Errorw with application-specific field names. (
sugar.go) - Optionally, define helper functions in field.go or a separate domain_fields.go to encapsulate repeated field tuples (e.g., RequestID, UserID). (
field.go) - Document the domain-specific logger interface in your application README; consider examples similar to example_test.go. (
example_test.go)
🔧Why these technologies
- sync.Pool for buffer management — Eliminates allocations on the hot path by reusing buffers across log calls; critical for sub-microsecond latency.
- Structured logging (field-based, not printf) — Enables type-safe encoding and machine-parseable output (JSON); allows efficient filtering and analysis of logs.
- Interface-based encoders and sinks — Allows user-defined output formats (JSON, Logfmt, Protobuf) and destinations (file, syslog, network) without core changes.
- SugaredLogger wrapper layer — Provides printf-style convenience for developers who prioritize ease-of-use over microsecond performance; ~4–10x faster than competitors.
- Go 1.21 slog integration (experimental) — Bridges zap to the standard library; allows users to adopt slog while using zap's performance and flexibility.
⚖️Trade-offs already made
-
Structured logging only (no printf-only mode in core Logger)
- Why: Enforces type safety and enables efficient machine parsing; discourages runtime-costly string formatting.
- Consequence: Users who want printf-style logging must use SugaredLogger (slightly slower); core Logger requires more verbose syntax.
-
Zero-allocation encoder design with buffer pooling
- Why: Achieves sub-microsecond latency per log call; critical for high-throughput services.
- Consequence: More complex encoder interface; users implementing custom encoders must understand buffer lifecycle and pooling semantics.
-
Single-threaded logger instance (no internal locking on Logger, relies on atomic field swaps)
- Why: Minimizes contention; each goroutine can hold its own logger or use global with atomic updates.
- Consequence: Users must not mutate Config on a live Logger; recommended pattern is to create Logger once and reuse.
-
Separate sink interface from encoder
- Why: Allows flexible composition: any encoder can write to any sink (file, network, etc.).
- Consequence: Adds one layer of indirection; users must understand both Encoder and Sink contracts to add custom behavior.
🚫Non-goals (don't propose these)
- Does not provide log aggregation, parsing, or querying; assumes external tools (e.g., ELK, Datadog, Grafana Loki) consume logs.
- Does not handle context.Context propagation or tracing integration directly; delegates to caller or separate packages (e.g., go.opentelemetry.io).
- Does not offer built-in sampling or rate-limiting; users must implement via Level or custom Encoder logic.
- Not a real-time, low-latency message queue; focused on single-application structured logging, not distributed messaging.
- Does not provide GUI or dashboard; purely library-based; visualization is external.
🪤Traps & gotchas
Clock synchronization: clock_test.go exists but clock.go is not listed—verify how wall-clock time is injected in tests. The export of struct fields in logger.go/sugar.go follows Go's public API convention strictly; adding new unexported fields requires care with json marshaling. Buffer pool in buffer/pool.go reuses slices; verify cap() not len() when resetting. No explicit shutdown hook beyond Sync() on logger—buffered writes must be flushed before process exit.
🏗️Architecture
💡Concepts to learn
- Zero-allocation logging — Zap's core value proposition is pre-allocated buffers and reuse via sync.Pool to avoid GC pressure; understanding buffer/pool.go is essential to why zap is fast
- Reflection-free field serialization — array.go contains hand-coded type switches instead of reflect.ValueOf; this eliminates the performance cost of introspection used by other loggers
- Encoder interface pattern — encoder.go defines a pluggable output format abstraction (JSON, MessagePack, Protocol Buffers); allows zap to support multiple serialization backends
- Structured logging (key-value pairs) — Zap enforces typed key-value fields (zap.String, zap.Int) over printf-style formats, enabling log aggregation and searching by field
- Leveled logging — logger.go provides multiple severity levels (Debug, Info, Warn, Error, Fatal); essential for runtime filtering and alert routing
- sync.Pool resource pooling — buffer/pool.go uses sync.Pool to reuse encoder buffers across goroutines; critical pattern in Go for reducing GC load in high-concurrency systems
- Sugaring pattern (API convenience wrapper) — sugar.go wraps the high-performance Logger with loosely-typed APIs (Infof, Infow); illustrates the performance-vs-convenience trade-off
🔗Related repos
sirupsen/logrus— Competing structured logging library for Go; slower but more feature-rich, often the alternative developers evaluate against zaprs/zerolog— Alternative high-performance Go logger (shown in benchmarks as 42% faster than zap on 10 fields); different zero-allocation approach using interface{} poolinguber-go/config— Companion Uber library for application configuration; often used with zap for log-level management and initializationgrpc/grpc-go— Common integration point—grpc-go provides middleware to bridge its logging to zap for unified request/response tracing
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add comprehensive benchmarks for zapslog handler with concurrent write scenarios
The exp/zapslog directory contains a handler implementation for Go's slog standard library, but benchmarks/slog_test.go only compares zap against slog. There are no benchmarks specifically testing the zapslog handler's performance under concurrent load, different buffer sizes, and various encoder configurations. This would help ensure the handler maintains zap's performance promises when used with slog APIs.
- [ ] Extend benchmarks/slog_test.go to add benchmarks specifically for exp/zapslog/handler.go
- [ ] Add concurrent write benchmarks (10, 100, 1000+ goroutines) comparing zapslog handler vs pure zap logger
- [ ] Add benchmarks for different log levels and handler options defined in exp/zapslog/options.go
- [ ] Document results in exp/zapslog/doc.go to guide users on performance characteristics
Add unit tests for HTTP handler edge cases in http_handler_test.go
The http_handler.go file provides an HTTP endpoint for dynamically changing log levels, but http_handler_test.go lacks comprehensive coverage for error conditions, malformed requests, concurrent access, and race conditions. This is a critical feature for production systems where atomic log level changes are needed.
- [ ] Add tests in http_handler_test.go for concurrent level change requests to verify thread-safety
- [ ] Add tests for malformed JSON request bodies and invalid log level strings
- [ ] Add tests for race conditions using Go's race detector (test with -race flag)
- [ ] Add tests for HTTP response status codes under various failure scenarios
- [ ] Update .github/workflows/go.yml to run tests with -race flag if not already present
Add missing test coverage for buffer pool exhaustion and metrics in buffer/pool_test.go
The buffer/pool.go implements a critical performance component for zap's zero-allocation logging, but buffer/pool_test.go appears to lack tests for edge cases like pool exhaustion, metrics tracking, and behavior under high concurrency. Adding these tests would ensure reliability for users with extreme throughput requirements.
- [ ] Add tests in buffer/pool_test.go for pool behavior when all buffers are exhausted and a new buffer is requested
- [ ] Add tests verifying correct buffer reuse patterns and no memory leaks across thousands of Get/Put cycles
- [ ] Add benchmark tests comparing pool performance with and without concurrent access patterns
- [ ] Add tests for the internal pool statistics/metrics if available, or document why they're not exposed
🌿Good first issues
- Add godoc examples for SugaredLogger.Infow and Logger.Info to example_test.go showing context propagation patterns (currently only basic examples exist)
- Create integration test in common_test.go demonstrating usage with context.Context cancellation and deadline propagation
- Expand benchmarks/scenario_bench_test.go with a 'high-cardinality tags' scenario (1000+ unique field values) to stress-test the pool allocation strategy
⭐Top contributors
Click to expand
Top contributors
- @abhinav — 26 commits
- @dependabot[bot] — 12 commits
- @sywhang — 11 commits
- @arukiidou — 7 commits
- @jquirke — 5 commits
📝Recent commits
Click to expand
Recent commits
5b81b37— release v1.28.0 (#1547) (sywhang)0ab0d5a— zapcore: Add PreWriteHook for transforming entries before write (#1534) (sywhang)d278c59— [chore] CI: test on Go 1.26 (#1535) (sywhang)16fb16b— chore(dep): replace archived gopkg.in/yaml.v3 with officially maintained go.yaml.in/yaml/v3 (#1522) (tuunit)7b755a3— release 1.27.1 (#1521) (sywhang)d6b395b— Update lazy logger not to materialize unless it's being written to (#1519) (rabbbit)4b9cea0— ci: Test with Go 1.24, Go 1.25 (#1508) (arukiidou)7c80d7b— Fix race condition in WithLazy implementation (#1426) (#1511) (jquirke)07077a6— Prevent zap.Object from panicing on nils (#1501) (alshopov)a6afd05— Fix lint check name (#1502) (JacobOaks)
🔒Security observations
The uber-go/zap logging library demonstrates a strong security posture with no critical vulnerabilities detected in the static analysis. The codebase appears well-maintained with proper Go module structure. Primary recommendations focus on: (1) updating Go version dependencies to the latest supported versions, (2) implementing comprehensive dependency tracking via SBOM, (3) reviewing HTTP handler security controls, and (4) ensuring test files do not contain sensitive data. The library's purpose as a logging framework means it handles application data but does not appear to store or transmit secrets. Recommend regular security audits and maintaining strict Go version support policies.
- Low · Deprecated Go Version Support —
assets/go.mod. The assets/go.mod file specifies 'go 1.21.5' which is an older Go version. Go 1.21.5 is no longer receiving security updates. According to the README, zap only supports the two most recent minor versions of Go, but the assets module may not be enforcing this consistently. Fix: Update to a supported Go version (1.22+ or later). Ensure all go.mod files across the repository maintain consistent minimum Go versions that align with the project's support policy. - Low · Missing SBOM and Dependency Tracking —
benchmarks/go.mod, benchmarks/go.sum, exp/go.mod, exp/go.sum. While a go.sum file is present, there is no explicit Software Bill of Materials (SBOM) or comprehensive dependency audit configuration visible. The benchmarks directory contains multiple go.mod files that could introduce transitive dependencies. Fix: Implement SBOM generation using tools like cyclonedx-gomod or syft. Add regular dependency audit steps in CI/CD pipeline using 'go list -json -m all' and vulnerability scanners. - Low · HTTP Handler Without Security Headers —
http_handler.go. The file 'http_handler.go' suggests HTTP functionality for logging. Without reviewing the implementation, HTTP handlers in logging libraries should include security headers to prevent misuse. Fix: Review http_handler.go to ensure it implements security best practices: proper Content-Type headers, X-Content-Type-Options: nosniff, X-Frame-Options headers, and input validation for any HTTP parameters. - Low · Test File Security Considerations —
**/*_test.go files throughout the repository. Multiple test files exist (e.g., common_test.go, config_test.go) which may contain test credentials or mock data that could inadvertently expose patterns for injection attacks or misconfiguration. Fix: Conduct a review of test files to ensure no hardcoded credentials, API keys, or sensitive mock data are present. Use test fixtures or environment variables for sensitive test data.
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.