Zerolog: The Ultra-Fast, Zero-Allocation JSON Logger for Go with OpenTelemetry Integration

In today's high-performance Go applications, logging solutions that minimize overhead while maximizing utility are essential. Among the top contenders in the Go logging ecosystem, Zerolog continues to stand out in 2025 as a premier choice for developers seeking both speed and developer experience. With the rise of OpenTelemetry as the industry standard for observability, Zerolog's integration capabilities make it even more valuable. This introduction explores what makes Zerolog exceptional and how it works with OpenTelemetry for comprehensive observability.

What is Zerolog?

Zerolog is a lightweight, zero-allocation JSON logging library designed specifically for Go. Created by Olivier Poitrey, Zerolog was built from the ground up with performance as its primary goal, while still providing an elegant, chainable API that makes logging a joy rather than a chore.

The library's name reflects its core philosophy: "zero" allocations when logging, resulting in minimal impact on your application's performance. Since its introduction, Zerolog has maintained this commitment to performance while expanding its feature set to meet modern logging requirements.

Key Features That Set Zerolog Apart

1. JSON-First Architecture

Unlike many logging libraries that treat structured logging as an afterthought, Zerolog embraces a JSON-first approach. This design decision makes it particularly well-suited for cloud-native applications where log aggregation and analysis tools expect structured data formats.

2. Zero-Allocation Design

Zerolog's most distinctive feature is its ability to log with zero heap allocations in common use cases. This design significantly reduces garbage collection pressure, resulting in more predictable performance—especially important in latency-sensitive applications.

3. Intuitive, Chainable API

The library provides a fluent, chainable API that improves code readability while maintaining performance:

go
12345
logger.Info().
Str("service", "api").
Int("status", 200).
Dur("latency", duration).
Msg("request processed")

This approach makes code more readable while ensuring type safety for logged fields.

4. Context-Aware Logging

Zerolog excels at adding context to logs, making it easier to trace requests through complex systems:

go
12456
logger := zerolog.New(os.Stdout).With().Timestamp().Logger()
ctx := logger.WithContext(context)
// Later in the code
log := zerolog.Ctx(ctx)
log.Info().Msg("Using context logger")

5. Conditional Logging

To further optimize performance, Zerolog provides elegant ways to perform conditional logging:

go
12345
logger.Debug().
Enabled() &&
logger.Debug().
Str("expensive_field", computeExpensiveValue()).
Msg("debug message")

Zerolog with OpenTelemetry: Unified Observability

In 2025's observability landscape, OpenTelemetry has become the industry standard for collecting telemetry data across distributed systems. Integrating Zerolog with OpenTelemetry provides a comprehensive observability solution that combines high-performance logging with distributed tracing and metrics.

Seamless Trace Context Propagation

One of the most powerful features when combining Zerolog with OpenTelemetry is the ability to correlate logs with traces:

go
1234567891011121314161718192021222324
// Extract trace information from OpenTelemetry context
func loggerWithTraceInfo(ctx context.Context, logger zerolog.Logger) zerolog.Logger {
span := trace.SpanFromContext(ctx)
if span.SpanContext().IsValid() {
traceID := span.SpanContext().TraceID().String()
spanID := span.SpanContext().SpanID().String()
return logger.With().
Str("trace_id", traceID).
Str("span_id", spanID).
Logger()
}
return logger
}
// Usage
func handleRequest(ctx context.Context) {
// Get contextual logger with trace info
log := loggerWithTraceInfo(ctx, zerolog.Ctx(ctx).Logger())
log.Info().Msg("Processing request")
// The log will include trace_id and span_id automatically
}

Creating an OpenTelemetry Log Exporter

Zerolog can be configured to send logs to OpenTelemetry collectors using a custom writer:

go
1234578910111213141516171819202122
import (
"github.com/rs/zerolog"
"go.opentelemetry.io/otel/exporters/otlp/otlplogs"
"go.opentelemetry.io/otel/sdk/logs"
)
func setupOpenTelemetryLogger() zerolog.Logger {
// Configure OpenTelemetry log exporter
exporter, _ := otlplogs.NewExporter(otlplogs.WithInsecure(),
otlplogs.WithEndpoint("otel-collector:4317"))
// Create log provider
provider := logs.NewProvider(
logs.WithBatcher(exporter),
)
// Create a writer that sends logs to OpenTelemetry
otelWriter := newOTelLogWriter(provider)
// Configure zerolog to use the OpenTelemetry writer
return zerolog.New(otelWriter).With().Timestamp().Logger()
}

Unified Observability Dashboard

When properly integrated, Zerolog and OpenTelemetry provide a complete observability picture:

  1. Logs (Zerolog): Detailed application events with contextual information
  2. Traces (OpenTelemetry): Request flows across services with timing information
  3. Metrics (OpenTelemetry): Aggregated performance data and business metrics

This unified approach allows teams to quickly identify and diagnose issues by correlating information across all three telemetry types.

Performance Benefits While Maintaining Observability

Even with OpenTelemetry integration, Zerolog maintains its performance advantages:

  • Sampling-based logging: Log only a percentage of requests at DEBUG level
  • Conditional logging: Avoid expensive computations for disabled log levels
  • Batched exports: Buffer logs before sending to OpenTelemetry collectors
  • Context propagation: Maintain request context across service boundaries

Getting Started with Zerolog and OpenTelemetry

Here's a complete example showing how to set up Zerolog with OpenTelemetry in a web service:

go
134567891011121315161718192021222324252627282930313233343536373839404142434446474849505152535455
package main
import (
"context"
"net/http"
"os"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/trace"
)
func main() {
// Initialize OpenTelemetry
shutdown := initOpenTelemetry()
defer shutdown()
// Configure zerolog
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
logger := zerolog.New(os.Stdout).With().Timestamp().Logger()
// HTTP handler with OpenTelemetry and Zerolog
http.Handle("/", otelhttp.NewHandler(
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Extract the context with trace information
ctx := r.Context()
// Get a logger with trace context
ctxLogger := loggerWithTraceInfo(ctx, logger)
ctxLogger.Info().
Str("path", r.URL.Path).
Str("method", r.Method).
Msg("Request received")
w.Write([]byte("Hello, World!"))
}), "hello"))
// Start server
logger.Info().Msg("Server starting on :8080")
http.ListenAndServe(":8080", nil)
}
func loggerWithTraceInfo(ctx context.Context, logger zerolog.Logger) zerolog.Logger {
span := trace.SpanFromContext(ctx)
if span.SpanContext().IsValid() {
return logger.With().
Str("trace_id", span.SpanContext().TraceID().String()).
Str("span_id", span.SpanContext().SpanID().String()).
Logger()
}
return logger
}

Analyzing OpenTelemetry Logs in Dash0

Logs can be directly routed into Dash0. Dash0 with OpenTelemetry provides the ability to filter, search, group, and triage within a simple user interface, with full keyboard support. Dash0 also gives full log context by showing trace context, the call and resource that created the log - including details like the Kubernetes Pod, server, and cloud environment.

Log AI also enhanced the logs with more semantical metadata and structure without any manual pattern declaration.

Conclusion: Zerolog + OpenTelemetry for Complete Observability in 2025

As Go applications continue to power critical infrastructure and services in 2025, the combination of Zerolog's efficiency with OpenTelemetry's comprehensive observability framework provides an ideal solution for modern development teams. With its zero-allocation performance, structured JSON output, developer-friendly API, and seamless OpenTelemetry integration, Zerolog remains a top choice for Go developers who refuse to compromise on either performance or observability.

Whether you're building microservices, APIs, or CLI tools, Zerolog with OpenTelemetry offers the right balance of features, performance, and observability capabilities to make debugging and monitoring both efficient and effective. As distributed systems become increasingly complex, having a logging solution that integrates smoothly with the broader observability ecosystem is not just convenient—it's essential for operational excellence.

Last updated: March 28, 2025