RepoPilotOpen in app →

rs/zerolog

Zero Allocation JSON Logger

Healthy

Healthy across the board

weakest axis
Use as dependencyHealthy

Permissive license, no critical CVEs, actively maintained — safe to depend on.

Fork & modifyHealthy

Has a license, tests, and CI — clean foundation to fork and modify.

Learn fromHealthy

Documented and popular — useful reference codebase to read through.

Deploy as-isHealthy

No critical CVEs, sane security posture — runnable as-is.

  • Last commit 2w ago
  • 50+ active contributors
  • Distributed ownership (top contributor 14% of recent commits)
Show all 6 evidence items →
  • 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.

Variant:
RepoPilot: Healthy
[![RepoPilot: Healthy](https://repopilot.app/api/badge/rs/zerolog)](https://repopilot.app/r/rs/zerolog)

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/rs/zerolog on X, Slack, or LinkedIn.

Onboarding doc

Onboarding: rs/zerolog

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:

  1. 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.
  2. 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.
  3. Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/rs/zerolog 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
  • 50+ active contributors
  • Distributed ownership (top contributor 14% 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 rs/zerolog repo on your machine still matches what RepoPilot saw. If any fail, the artifact is stale — regenerate it at repopilot.app/r/rs/zerolog.

What it runs against: a local clone of rs/zerolog — 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 rs/zerolog | 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 ≤ 47 days ago | Catches sudden abandonment since generation |

<details> <summary><b>Run all checks</b> — paste this script from inside your clone of <code>rs/zerolog</code></summary>
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of rs/zerolog. If you don't
# have one yet, run these first:
#
#   git clone https://github.com/rs/zerolog.git
#   cd zerolog
#
# 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 rs/zerolog and re-run."
  exit 2
fi

# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "rs/zerolog(\\.git)?\\b" \\
  && ok "origin remote is rs/zerolog" \\
  || miss "origin remote is not rs/zerolog (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 "log.go" \\
  && ok "log.go" \\
  || miss "missing critical file: log.go"
test -f "event.go" \\
  && ok "event.go" \\
  || miss "missing critical file: event.go"
test -f "encoder_json.go" \\
  && ok "encoder_json.go" \\
  || miss "missing critical file: encoder_json.go"
test -f "context.go" \\
  && ok "context.go" \\
  || miss "missing critical file: context.go"
test -f "internal/json/base.go" \\
  && ok "internal/json/base.go" \\
  || miss "missing critical file: internal/json/base.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 47 ]; then
  ok "last commit was $days_since_last days ago (artifact saw ~17d)"
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/rs/zerolog"
  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).

</details>

TL;DR

zerolog is a high-performance, zero-allocation JSON logger for Go that outputs structured logs directly as JSON (or CBOR) without reflection or intermediate allocations. It pioneered by Uber's zap but optimizes further with a simpler chaining API and lower memory overhead, making it ideal for production services that need both speed and structured logging at scale. Modular monorepo structure: core logging logic in root files (event.go, encoder.go, context.go), specialized encoders as encoder_json.go and encoder_cbor.go, diode/ subpackage for lock-free async logging, hlog/ for http.Handler integration, and cmd/ utilities (prettylog formatter, lint tool). Globals in globals.go handle package-level logger state.

👥Who it's for

Go backend engineers and DevOps teams building microservices, APIs, and infrastructure tools who need fast, structured logging without garbage collection pressure. Specifically appeals to teams running high-throughput services where even kilobytes of per-log allocation matter.

🌱Maturity & risk

Production-ready and actively maintained. The codebase shows comprehensive test coverage (array_test.go, context_test.go, benchmark_test.go), automated CI via GitHub Actions (test.yml), and spans 581KB of Go code with established patterns. The project is widely adopted (see 'who uses zerolog' wiki) and regularly updated.

Very low risk. Single-digit external dependencies (visible in go.mod/go.sum), clean architecture, and extensive test suite. No obvious sign of stale commits. Primary risk is the single-maintainer model (appears to be 'rs'), typical of successful open-source projects. No breaking changes evident in recent activity.

Active areas of work

The repository is actively maintained with focus on Go version compatibility (globals_118.go suggests recent 1.18+ support), encoder flexibility (CBOR alongside JSON), and integration polish (hlog, slog support). CI/CD is healthy via test.yml workflows.

🚀Get running

Clone and verify: git clone https://github.com/rs/zerolog.git && cd rs/zerolog && go test ./... to run the test suite. Then go get -u github.com/rs/zerolog/log in your own project to use it.

Daily commands: This is a library, not a service. To develop locally: go test ./... runs all tests. go build ./cmd/prettylog builds the formatter tool. go bench -bench=. ./... runs benchmarks. See Makefile (197 bytes) for available targets.

🗺️Map of the codebase

  • log.go — Core Logger type and primary API entry point; defines the main logging interface and event chaining mechanism that all contributors must understand.
  • event.go — Event struct that handles the chaining API for building structured log entries; central to understanding zerolog's zero-allocation design.
  • encoder_json.go — JSON encoder implementation that writes structured log data efficiently; critical for understanding how zerolog avoids allocations.
  • context.go — Context helper functions for embedding logger instances in Go contexts; essential for middleware and request-scoped logging patterns.
  • internal/json/base.go — Low-level JSON encoding utilities used throughout the codebase; foundation for all JSON marshaling logic.
  • hook.go — Hook interface for intercepting and modifying log events; key extension point for custom behavior.
  • sampler.go — Sampling implementation for probabilistic log filtering; demonstrates how to compose logging behavior.

🛠️How to make changes

Add a Custom Hook for Event Interception

  1. Define a function matching the Hook interface signature in hook.go: func(e *Event) *Event (hook.go)
  2. Register the hook on the Logger via logger.Hook(myHookFunc) which returns a new Logger (log.go)
  3. The hook will be invoked after each log event before writing, allowing modification or filtering (event.go)

Implement a Custom Output Encoder

  1. Create a new encoder file (e.g., encoder_myformat.go) implementing the Encoder interface with AppendXxx methods (encoder.go)
  2. Export factory function that returns your Encoder instance from a Writer (encoder.go)
  3. Set via logger := zerolog.New(writer).With().Logger() where writer is wrapped with your encoder (log.go)
  4. Add tests in encoder_myformat_test.go validating encoding behavior (benchmark_test.go)

Add a Sampler for Probabilistic Log Filtering

  1. Create a sampler following the Sampler interface in sampler.go (Sample method returns bool) (sampler.go)
  2. Use logger.Sample(mySampler) to wrap your logger with sampling logic (log.go)
  3. For complex per-field sampling, compose multiple samplers or create a custom Hook (hook.go)

Integrate with HTTP Request Logging

  1. Import hlog package and use hlog.NewHandler to wrap your http.Handler (hlog/hlog.go)
  2. Optionally use hlog.RequestIDHandler to extract request IDs from headers (hlog/hlog.go)
  3. Access the logger in handlers via hlog.FromRequest(r) to inject request-scoped fields (context.go)
  4. Write tests using hlog test patterns shown in hlog/hlog_test.go (hlog/hlog_test.go)

🔧Why these technologies

  • JSON output — Structured logging standard for modern observability stacks; easily parsed by log aggregators and analytics tools.
  • CBOR binary encoding — Optional compact binary format for reduced bandwidth and faster parsing when JSON overhead matters.
  • Lock-free ring buffer (diode) — Enables async, high-throughput logging without allocation or locking overhead for concurrent workloads.
  • Fluent chaining API — Provides ergonomic builder pattern while allowing compiler optimizations for zero allocation by building fields in-place.
  • Go context.Context integration — Idiomatic Go practice for request-scoped values; enables transparent logger propagation through middleware stacks.

⚖️Trade-offs already made

  • JSON-only structured output; no log levels in default mode

    • Why: Simplifies API and eliminates decision paralysis; levels can be added as fields if needed.
    • Consequence: Reduced flexibility for filtering at output time; filtering must happen at hook/sampler layer.
  • No reflection; type-specific Append methods instead

    • Why: Enables compile-time verification and zero reflection overhead.
    • Consequence: Larger API surface (Str, Int, Float, Time, etc.) but guaranteed performance and safety.
  • Synchronous default; async buffering optional via diode

    • Why: Keeps core simple and safe; blocking writes ensure logs are flushed. Diode opt-in for throughput.
    • Consequence: Default logging can block on I/O; applications needing >100k logs/sec should use diode buffer.
  • No built-in log rotation or file management

    • Why: Keeps scope focused; Unix philosophy of doing one thing well.
    • Consequence: Users must layer external tools (lumberjack, logrotate) for file management.

🚫Non-goals (don't propose these)

  • Does not provide log levels as a first-class concept (can be added as fields)
  • Does not include built-in file rotation or log management
  • Does not provide sampling or filtering at the output layer (use Sampler or Hook interfaces)
  • Does not support dynamic runtime reconfiguration without creating new loggers
  • Does not aim for zero-copy I/O on very large messages (trades off small allocations for simplicity)

🪤Traps & gotchas

No obvious hidden traps. The codebase is clean: globals.go handles mutex protection, encoder abstraction is clear, and test coverage is comprehensive. One gotcha: pretty printing via ConsoleWriter is deliberately inefficient (noted in README as 'provided but inefficient'); production should use JSON. CBOR vs JSON encoding choice must be set upfront via encoder registration, not per-log.

🏗️Architecture

💡Concepts to learn

  • Zero-allocation logging — The entire premise of zerolog—avoiding heap allocations during log writes to minimize GC pressure. Essential for high-throughput services.
  • Lock-free/wait-free data structures (diode) — The diode/ package uses lock-free queues to decouple log writing from log output, enabling async non-blocking logging without mutexes
  • Fluent builder API — zerolog chains method calls (log.Info().Str(...).Int(...).Msg(...)) to build events; differs from traditional logger.Infof() patterns
  • Binary encoding (CBOR) — Alongside JSON, zerolog supports CBOR (Concise Binary Object Representation) for even faster, smaller log output in resource-constrained environments
  • Context propagation (context.Context) — ctx.go integrates with Go's stdlib context.Context to attach loggers to request contexts, enabling per-request logging across goroutines
  • HTTP middleware pattern (hlog) — hlog/ shows how to wrap net/http handlers to auto-log requests/responses; critical pattern for REST APIs
  • Reflection-free serialization — Unlike many Go loggers, zerolog avoids reflection during field encoding; instead uses explicit Str(), Int(), Bool() methods for type safety and speed
  • uber-go/zap — The predecessor that pioneered zero-allocation structured logging in Go; zerolog is a direct conceptual descendant with simpler API
  • sirupsen/logrus — Alternative Go logging library with more features but higher allocation cost; often compared against zerolog in benchmarks
  • go-kit/kit — Microservices toolkit that integrates with zerolog for structured logging in distributed systems
  • golang/go — As of Go 1.21+, the stdlib added log/slog package; zerolog maintains compatibility layer (slog integration mentioned in README)
  • rs/cors — Companion package by same author for HTTP middleware patterns; demonstrates idiomatic Go middleware design similar to hlog/

🪄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 CBOR encoder tests and benchmarks

The repo has internal/cbor/ with CBOR encoding support, but encoder_cbor.go lacks dedicated test coverage. Currently, there are encoder_json.go and encoder.go, but no encoder_cbor_test.go. This is critical for a zero-allocation logger where performance parity between JSON and CBOR encoders must be verified and maintained.

  • [ ] Create encoder_cbor_test.go with unit tests for CBOR encoding of all field types (strings, numbers, timestamps, errors, arrays)
  • [ ] Add CBOR-specific benchmark tests to benchmark_test.go comparing JSON vs CBOR performance
  • [ ] Test round-trip compatibility: ensure CBOR encoded logs can be decoded by internal/cbor/decoder_test.go patterns
  • [ ] Verify zero-allocation guarantees for CBOR encoder like the JSON encoder

Add integration tests for hlog HTTP middleware with various edge cases

The hlog/ package provides HTTP middleware integration but hlog/hlog_test.go likely lacks coverage for error handling, panic recovery, and concurrent requests. This is a production-critical path that needs robust testing given zerolog's focus on reliability.

  • [ ] Add tests in hlog/hlog_test.go for panics in handlers and ensure proper error logging
  • [ ] Test concurrent requests to verify thread-safety and no allocation leaks under load
  • [ ] Add test cases for missing/malformed headers, custom header injection, and header sanitization
  • [ ] Add benchmark test in hlog/ for HTTP request logging overhead compared to raw zerolog

Create Go 1.20+ specific optimizations with version-gated tests

The repo has go112.go and globals_118.go showing version-specific code paths, but there's no globals_120.go or encoder_120.go. Go 1.20+ introduced range-over-func and other performance improvements that could benefit zero-allocation logging. This aligns with zerolog's core mission.

  • [ ] Create globals_120.go with Go 1.20+ specific optimizations (if applicable) and add build tags in globals.go
  • [ ] Add globals_120_test.go to verify behavior matches older versions while benchmarking improvements
  • [ ] Review encoder_json.go for opportunities to use Go 1.20+ language features for better performance
  • [ ] Update .github/workflows/test.yml to include Go 1.20+ and 1.21 in the test matrix if not present

🌿Good first issues

  • Add comprehensive integration tests for diode/diode.go under diode/diode_test.go (currently sparse) to verify lock-free queue behavior under concurrent load scenarios
  • Write missing tests for error_marshal_test.go edge cases, e.g. circular reference handling, nil pointer dereferencing, and custom error types with stack traces
  • Expand cmd/lint/lint.go documentation and add a GitHub Actions workflow (if missing) that auto-lints all Go files on PR to catch common patterns early

Top contributors

Click to expand

📝Recent commits

Click to expand
  • 116c806 — event: restore Err() logging when ErrorStackMarshaler returns nil (#763) (alliasgher)
  • 1396655 — Bump CI Go matrix minimum from 1.21 to 1.23 (rs)
  • 4b65a2f — Bump actions/cache from 4 to 5 (#741) (dependabot[bot])
  • b835796 — Bump actions/setup-go from 5 to 6 (#742) (dependabot[bot])
  • 134caf8 — Added sanitization of journald keys (#751) (IDisposable)
  • e133b6a — Added variadic StrsV, ObjectsV, and StringersV (#752) (IDisposable)
  • 82017d8 — Bump github.com/coreos/go-systemd/v22 from 22.6.0 to 22.7.0 (#753) (dependabot[bot])
  • 2f5b8a9 — fix: UpdateContext skips Nop and zero-value loggers (#754) (veeceey)
  • d64c9a7 — Add slog.Handler implementation for zerolog (#755) (veeceey)
  • a0d61dc — fix: return dict to Event pool (#749) (IDisposable)

🔒Security observations

The zerolog codebase demonstrates good overall security practices as a JSON logging library with no apparent injection vulnerabilities, hardcoded secrets, or infrastructure misconfigurations. However, the project has a critical dependency management issue: the cmd/lint submodule uses an outdated golang.org/x/tools (v0.1.8 from 2021) and references an unsupported Go version (1.15). These should be updated immediately to address potential security patches. The main codebase itself appears well-structured and secure, with proper separation of concerns and no obvious attack vectors for a logging library.

  • High · Outdated golang.org/x/tools Dependency — cmd/lint/go.mod. The cmd/lint/go.mod file specifies golang.org/x/tools v0.1.8, which is significantly outdated (released in 2021). This dependency may contain known security vulnerabilities and lacks important security patches and bug fixes from newer versions. Fix: Update golang.org/x/tools to the latest stable version (v0.15.0 or newer as of 2024). Run 'go get -u golang.org/x/tools' and test thoroughly.
  • Medium · Missing Go Version Consistency — cmd/lint/go.mod. The cmd/lint/go.mod specifies 'go 1.15', which is extremely outdated (released in 2020 and no longer supported). The main module uses a more recent version based on files like globals_118.go (Go 1.18+), creating version inconsistency across the codebase. Fix: Update 'go 1.15' to match the minimum supported Go version of the main project (likely 1.18+). Ensure consistency across all go.mod files in the monorepo.
  • Low · No Dependency Hash Verification File — cmd/lint/ and other submodules. While go.sum file exists for the main module, explicit verification of transitive dependency integrity across all submodules (cmd/lint, hlog, journald, diode) is not immediately visible in the file structure. Fix: Ensure all submodules have properly populated go.sum files. Run 'go mod tidy' and 'go mod verify' in each module directory to validate dependency integrity.
  • Low · Lint Command Tool Exposure — cmd/lint/lint.go. The cmd/lint directory contains a linting tool that depends on golang.org/x/tools. While linting is generally safe, custom lint tools should be carefully reviewed for command injection or unsafe code analysis patterns. Fix: Review cmd/lint/lint.go for any unsafe use of exec, os/exec, or reflection on untrusted input. Ensure input validation on any user-provided paths or code being analyzed.

LLM-derived; treat as a starting point, not a security audit.


Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.

Healthy signals · rs/zerolog — RepoPilot