High-performance, zero-allocation asynchronous logging for Go.
loggerj uses a Pre-Compiled Execution Profile architecture. Rate limits, sampling, and static fields are baked into memory at startup. The hot path consists purely of atomic operations and memory copies — zero mutex locks, zero heap allocations, zero GC pressure.
- Asynchronous Pipeline: Non-blocking channel + dedicated worker goroutine.
- Zero-Allocation Typed Fields:
Int(),Bool(),Dur()without interface boxing. - Lock-Free Rate Limiting & Sampling:
atomic.CompareAndSwap(CAS) with bounded backoff. Applies to the async hot path. - Pre-baked SubProfiles: Static fields formatted once at init, injected via
memcpyat log time. - Sync Mode with Durability Tiers: 4 explicit tiers for audit trails. Direct and FsyncEveryWrite are lock-free (O_APPEND atomic); OSBuffered and FsyncEveryN hold a mutex around the shared
bufio.Writerduring buffer copy AND flush/fsync. - Native log rotation — size-based with backup retention, zero external dependencies. No time-based rotation, no compression. For daily rotation or gzip, use lumberjack via
StartWithWriter(). - slog.Handler Adapter: Routes standard
log/slogcalls through loggerj's zero-alloc pipeline.
package main
import (
"context"
"time"
"github.com/uretgec/loggerj"
)
func main() {
// 1. Initialize Logger
logger := loggerj.NewLogger(loggerj.Config{
JSONOutput: true,
FlushTimeout: 50 * time.Millisecond,
})
// 2. Define SubProfiles (COLD PATH: do this once at startup)
logger.RegisterSub("HTTP",
loggerj.WithRateLimit(1000, time.Second),
loggerj.WithFields("env", "prod", "service", "gateway"),
)
// 3. Start the async worker
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
go logger.Start(ctx)
defer logger.Close()
// 4. Log messages (HOT PATH: zero allocation)
logger.InfoString("HTTP", "request received", "method", "GET", "path", "/api")
// 5. Typed fields for dynamic values (zero alloc)
status := 200
latency := 150 * time.Millisecond
logger.InfoFields("HTTP", []byte("request completed"),
loggerj.Int("status", status),
loggerj.Dur("latency", latency),
loggerj.Bool("cached", true),
)
// 6. slog integration (optional)
// slogger := slog.New(loggerj.NewSlogHandler(logger, "APP"))
// 7. Ensure all logs are written before exit
logger.Flush()
}Intercept logs from Go's standard log package and third-party libraries:
log.SetFlags(0) // Disable std log timestamps; loggerj adds its own
log.SetOutput(logger.AsWriter(loggerj.LevelInfo, "STDLIB"))
log.Println("This message flows through loggerj's async pipeline")Note: The AsWriter adapter is zero-allocation on Go 1.22+. On Go 1.21, it shows 1 alloc/op due to compiler escape analysis limitations across the io.Writer interface boundary. The native InfoString/InfoFields APIs are always zero-alloc.
loggerj is optimized for async throughput and explicit durability guarantees.
- Async Mode: Designed for high-QPS microservices, gateways, and proxies where log volume exceeds 1M logs/s and GC pauses must be avoided.
- Sync Mode: Designed for audit trails and financial logs. Unlike other loggers that implicitly rely on OS page cache,
loggerjexplicitly documents crash-survival guarantees viaDurabilityTier.
For detailed ns/op metrics, hardware benchmarks, and fair apples-to-apples comparisons with zap, zerolog, and slog, see BENCH.md.
For architectural trade-offs, missing features (like nested JSON objects), and migration guides, see COMPARISON.md.
- EXAMPLES.md — Comprehensive examples for all features.
- BENCH.md — Detailed benchmark methodology and results.
- COMPARISON.md — Honest comparison with zap, zerolog, slog, logrus.
- ROADMAP.md — Future vision and development roadmap.
- CHANGELOG.md — Release notes and migration guides.
MIT License