#![cfg(feature = "logging")]
mod stream_a_support;
use stratify::logging::rate_limit::RateLimit;
use stratify::logging::sampling::SampleConfig;
use stratify::logging::Builder;
use stream_a_support::{file_config, lines_written};
use tempfile::TempDir;
use tracing::level_filters::LevelFilter;
use tracing_subscriber::filter::EnvFilter;
const BURST: usize = 200;
fn burst_through(builder: Builder, dir: &TempDir) -> Vec<String> {
let (subscriber, handle) = builder
.file(file_config(dir.path()))
.with_filter(EnvFilter::new("trace"))
.build()
.expect("build failed");
{
let _default = tracing::subscriber::set_default(subscriber);
for i in 0..BURST {
tracing::info!(index = i, "burst event");
}
}
handle.flush();
lines_written(dir.path())
}
#[test]
fn without_gating_every_event_reaches_the_sink() {
let dir = TempDir::new().expect("tempdir");
let written = burst_through(stratify::logging::builder(), &dir);
assert_eq!(written.len(), BURST);
}
#[test]
fn rate_limit_drops_events_beyond_the_configured_budget() {
let dir = TempDir::new().expect("tempdir");
let budget = 5;
let written = burst_through(
stratify::logging::builder().rate_limit(RateLimit::per_minute(budget)),
&dir,
);
assert_eq!(
written.len(),
budget as usize,
"rate_limit(per_minute({budget})) let {} of {BURST} events through",
written.len()
);
}
#[test]
fn sampling_rate_zero_lets_nothing_reach_the_sink() {
let dir = TempDir::new().expect("tempdir");
let sampling = SampleConfig::new(0.0);
let written = burst_through(stratify::logging::builder().sampling(sampling), &dir);
assert!(
written.is_empty(),
"sampling rate 0.0 still wrote {} lines",
written.len()
);
}
#[test]
fn sampling_rate_one_lets_everything_reach_the_sink() {
let dir = TempDir::new().expect("tempdir");
let sampling = SampleConfig::new(1.0);
let written = burst_through(stratify::logging::builder().sampling(sampling), &dir);
assert_eq!(written.len(), BURST);
}
#[test]
fn sampling_min_level_gates_the_sink_through_the_builder() {
let dir = TempDir::new().expect("tempdir");
let sampling = SampleConfig::default().with_min_level(LevelFilter::WARN);
let (subscriber, handle) = stratify::logging::builder()
.sampling(sampling)
.file(file_config(dir.path()))
.with_filter(EnvFilter::new("trace"))
.build()
.expect("build failed");
{
let _default = tracing::subscriber::set_default(subscriber);
tracing::error!("kept error");
tracing::warn!("kept warn");
tracing::info!("dropped info");
tracing::debug!("dropped debug");
tracing::trace!("dropped trace");
}
handle.flush();
let written = lines_written(dir.path());
assert_eq!(written.len(), 2, "written: {written:#?}");
assert!(written[0].contains("kept error"));
assert!(written[1].contains("kept warn"));
}
#[test]
fn sampling_runs_before_rate_limiting() {
let dir = TempDir::new().expect("tempdir");
let sampling = SampleConfig::new(0.0);
let written = burst_through(
stratify::logging::builder()
.sampling(sampling)
.rate_limit(RateLimit::per_minute(5)),
&dir,
);
assert!(written.is_empty(), "written: {written:#?}");
}
#[test]
fn gating_does_not_suppress_spans() {
let dir = TempDir::new().expect("tempdir");
let (subscriber, handle) = stratify::logging::builder()
.rate_limit(RateLimit::per_minute(1))
.file(file_config(dir.path()))
.with_filter(EnvFilter::new("trace"))
.build()
.expect("build failed");
{
let _default = tracing::subscriber::set_default(subscriber);
for _ in 0..10 {
let span = tracing::info_span!("gated_span", kind = "test");
let _entered = span.enter();
tracing::info!("inside span");
}
}
handle.flush();
let written = lines_written(dir.path());
assert_eq!(written.len(), 1, "written: {written:#?}");
assert!(
written[0].contains("gated_span"),
"span context was lost: {}",
written[0]
);
}