tracing-log-sample
A tracing-subscriber layer that rate-limits log output using reservoir sampling.
Instead of dropping logs above a threshold or probabilistically skipping them up-front, this layer collects events into fixed-duration time buckets and keeps a statistically uniform random sample per bucket. When a spike of logs occurs, you still see a representative cross-section rather than just the first N or a biased subset.

Features
- Cascading budgets — define multiple sampling budgets with
EnvFilterpatterns. Events displaced from one budget's reservoir are passed to the next matching budget. - Budget-based — configure a per-second event limit for each budget; the layer automatically scales this to the bucket duration.
- Smeared output — sampled events from the previous bucket are released gradually across the next bucket, avoiding write bursts at rotation boundaries.
- Low overhead — non-matching callsites are rejected at
register_callsitetime, filter results are cached in a bitset, and format buffers are reused via thread-local storage. - Full
fmt::Layerintegration — formatting is delegated totracing_subscriber::fmt::Layer, so compact, pretty, JSON, timestamps, and all other formatting options work out of the box.
Usage
use Duration;
use ;
use SamplingLayer;
let = builder
.without_time
.compact
.bucket_duration
.budget // up to 1000 error events/s
.budget // up to 5000 info events/s
.build;
let subscriber = default.with;
set_global_default.unwrap;
// stats.received(), stats.sampled(), stats.dropped()
How it works
- Each budget has an
EnvFilterand a reservoir sized toceil(limit_per_second * bucket_duration_secs). - On each event, the layer checks which budgets match (via a
u64bitset), delegates to the innerfmt::Layerto format the event once, then feeds the formatted bytes through matching reservoirs in order. - If a reservoir is under capacity the event is stored directly. If full, the incoming event randomly replaces an existing sample with decreasing probability — guaranteeing every event has an equal chance of being kept. Any displaced event cascades to the next matching budget.
- When the time bucket advances, all reservoirs are drained. Rather than writing everything at once, the events are released gradually over the next bucket via adaptive smearing.