slog-async

Asynchronous background-thread drain that keeps slog-rs structured logging off your Rust application's hot path

Library
Cargo
v2.8.0
30stars
Apache License 2.0

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
24/100Needs Attention
Development Activity0
Maintenance0
Community24
Maturity60
Momentum12

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
61/100Good
Architecture70
Code Quality62
Innovation58
Learning Curve55

slog-async is the reference asynchronous drain for the slog-rs structured logging ecosystem in Rust. Because serialization and I/O are typically the slowest parts of logging, slog-async hands log records off to a dedicated worker thread over a bounded channel, so the calling thread only pays the cost of a fast send rather than the full cost of formatting and writing a record.

It is deliberately positioned as a reasonable default implementation rather than a hardcoded part of slog-rs itself: the crate exposes Async and the lower-level AsyncCore so applications can either use it as-is or use its source as a starting point for a custom async logging strategy, with configurable channel size, overflow behavior (drop, drop-and-report, or block), and an optional AsyncGuard to guarantee flushing on drop.

What You Get

  • An Async drain that wraps any slog::Drain and moves its work to a background thread via a bounded crossbeam-channel
  • Configurable overflow handling through OverflowStrategyDrop, DropAndReport (the default, which also logs a count of dropped records), or Block
  • An AsyncGuard returned by build_with_guard() that flushes and joins the worker thread on drop, protecting against message loss from leftover references or panics
  • Lower-level AsyncCore / AsyncCoreBuilder primitives for building custom async drains with the same worker-thread plumbing
  • Optional nested-values and dynamic-keys Cargo features that mirror the corresponding slog crate features

Common Use Cases

  • Wrapping a file- or network-backed slog-rs drain so writing log lines never blocks request-handling threads
  • Keeping high-throughput services responsive under log bursts by capping in-flight records with a bounded channel and a defined overflow policy
  • Guaranteeing logs are flushed before process exit by pairing AsyncGuard with a run()-then-exit() pattern, since std::process::exit skips destructors
  • Prototyping a custom async logging strategy by using AsyncCore as a documented starting point instead of writing worker-thread plumbing from scratch

Under The Hood

Architecture When Async::log() is called, it first flushes any pending “dropped records” counter via push_dropped(), then hands the Record to AsyncCore::log(), which serializes it into an owned AsyncRecord (using ToSendSerializer to convert borrowed key-value data into Send-safe owned values) and pushes it onto a crossbeam-channel created by AsyncCoreBuilder::spawn_thread(). A dedicated worker thread loops on rx.recv(), matching AsyncMsg::Record (replays the record into the wrapped drain via record.log_to(&drain)) or AsyncMsg::Finish (exits the loop), with the whole loop body wrapped in catch_unwind so a panic inside the wrapped drain is caught and reported to stderr instead of killing the worker thread silently.

Tech Stack Pure Rust with no async runtime dependency — concurrency is built directly on std::thread plus crossbeam-channel 0.5 for the bounded queue, thread_local 1 for per-thread sender caching, and take_mut 0.2 for in-place ownership swaps inside the serializer. The crate depends on slog 2.1 and forwards two optional Cargo features, nested-values and dynamic-keys, straight through to the corresponding slog features; minimum supported Rust version is declared as 1.59.0.

Code Quality Test coverage is a single integration test in lib.rs (test::integration_test) that exercises the full pipeline through a hand-written MockDrain backed by an mpsc channel, asserting on the exact formatted output for two log levels — there are no separate unit test files or benchmark suite. Error handling is idiomatic: an AsyncError enum (Full / Fatal) with From impls for the channel and mutex-poisoning error types, and an AsyncResult<T> alias used consistently across the public API. Naming and module layout follow slog-rs ecosystem conventions, and the code contains no unsafe blocks, relying on catch_unwind plus channel semantics for safety at the thread boundary.

API Design The public surface centers on two builder types, AsyncBuilder and the lower-level AsyncCoreBuilder, offering a fluent chan_size() / overflow_strategy() / thread_name() configuration chain before a final .build(), .build_no_guard(), or .build_with_guard() call. Getting started requires minimal code (Async::default(drain) plus slog’s .fuse() adapter), but productive use assumes familiarity with slog-rs’s own Drain trait and Logger construction, so the learning curve leans on understanding the wider slog-rs ecosystem rather than this crate in isolation.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search