blob: d7ad6176983f08fd87b62c685d48df54546ca0a5 [file] [log] [blame]
use super::*;
use tracing_core::subscriber::NoSubscriber;
#[derive(Debug)]
pub(crate) struct NopLayer;
impl<S: Subscriber> Layer<S> for NopLayer {}
#[allow(dead_code)]
struct NopLayer2;
impl<S: Subscriber> Layer<S> for NopLayer2 {}
/// A layer that holds a string.
///
/// Used to test that pointers returned by downcasting are actually valid.
struct StringLayer(&'static str);
impl<S: Subscriber> Layer<S> for StringLayer {}
struct StringLayer2(&'static str);
impl<S: Subscriber> Layer<S> for StringLayer2 {}
struct StringLayer3(&'static str);
impl<S: Subscriber> Layer<S> for StringLayer3 {}
pub(crate) struct StringSubscriber(&'static str);
impl Subscriber for StringSubscriber {
fn register_callsite(&self, _: &'static Metadata<'static>) -> Interest {
Interest::never()
}
fn enabled(&self, _: &Metadata<'_>) -> bool {
false
}
fn new_span(&self, _: &span::Attributes<'_>) -> span::Id {
span::Id::from_u64(1)
}
fn record(&self, _: &span::Id, _: &span::Record<'_>) {}
fn record_follows_from(&self, _: &span::Id, _: &span::Id) {}
fn event(&self, _: &Event<'_>) {}
fn enter(&self, _: &span::Id) {}
fn exit(&self, _: &span::Id) {}
}
fn assert_subscriber(_s: impl Subscriber) {}
fn assert_layer<S: Subscriber>(_l: &impl Layer<S>) {}
#[test]
fn layer_is_subscriber() {
let s = NopLayer.with_subscriber(NoSubscriber::default());
assert_subscriber(s)
}
#[test]
fn two_layers_are_subscriber() {
let s = NopLayer
.and_then(NopLayer)
.with_subscriber(NoSubscriber::default());
assert_subscriber(s)
}
#[test]
fn three_layers_are_subscriber() {
let s = NopLayer
.and_then(NopLayer)
.and_then(NopLayer)
.with_subscriber(NoSubscriber::default());
assert_subscriber(s)
}
#[test]
fn three_layers_are_layer() {
let layers = NopLayer.and_then(NopLayer).and_then(NopLayer);
assert_layer(&layers);
let _ = layers.with_subscriber(NoSubscriber::default());
}
#[test]
#[cfg(feature = "alloc")]
fn box_layer_is_layer() {
use alloc::boxed::Box;
let l: Box<dyn Layer<NoSubscriber> + Send + Sync> = Box::new(NopLayer);
assert_layer(&l);
l.with_subscriber(NoSubscriber::default());
}
#[test]
fn downcasts_to_subscriber() {
let s = NopLayer
.and_then(NopLayer)
.and_then(NopLayer)
.with_subscriber(StringSubscriber("subscriber"));
let subscriber =
<dyn Subscriber>::downcast_ref::<StringSubscriber>(&s).expect("subscriber should downcast");
assert_eq!(subscriber.0, "subscriber");
}
#[test]
fn downcasts_to_layer() {
let s = StringLayer("layer_1")
.and_then(StringLayer2("layer_2"))
.and_then(StringLayer3("layer_3"))
.with_subscriber(NoSubscriber::default());
let layer = <dyn Subscriber>::downcast_ref::<StringLayer>(&s).expect("layer 1 should downcast");
assert_eq!(layer.0, "layer_1");
let layer =
<dyn Subscriber>::downcast_ref::<StringLayer2>(&s).expect("layer 2 should downcast");
assert_eq!(layer.0, "layer_2");
let layer =
<dyn Subscriber>::downcast_ref::<StringLayer3>(&s).expect("layer 3 should downcast");
assert_eq!(layer.0, "layer_3");
}
#[cfg(all(feature = "registry", feature = "std"))]
mod registry_tests {
use super::*;
use crate::registry::LookupSpan;
#[test]
fn context_event_span() {
use std::sync::{Arc, Mutex};
let last_event_span = Arc::new(Mutex::new(None));
struct RecordingLayer {
last_event_span: Arc<Mutex<Option<&'static str>>>,
}
impl<S> Layer<S> for RecordingLayer
where
S: Subscriber + for<'lookup> LookupSpan<'lookup>,
{
fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) {
let span = ctx.event_span(event);
*self.last_event_span.lock().unwrap() = span.map(|s| s.name());
}
}
tracing::subscriber::with_default(
crate::registry().with(RecordingLayer {
last_event_span: last_event_span.clone(),
}),
|| {
tracing::info!("no span");
assert_eq!(*last_event_span.lock().unwrap(), None);
let parent = tracing::info_span!("explicit");
tracing::info!(parent: &parent, "explicit span");
assert_eq!(*last_event_span.lock().unwrap(), Some("explicit"));
let _guard = tracing::info_span!("contextual").entered();
tracing::info!("contextual span");
assert_eq!(*last_event_span.lock().unwrap(), Some("contextual"));
},
);
}
/// Tests for how max-level hints are calculated when combining layers
/// with and without per-layer filtering.
mod max_level_hints {
use super::*;
use crate::filter::*;
#[test]
fn mixed_with_unfiltered() {
let subscriber = crate::registry()
.with(NopLayer)
.with(NopLayer.with_filter(LevelFilter::INFO));
assert_eq!(subscriber.max_level_hint(), None);
}
#[test]
fn mixed_with_unfiltered_layered() {
let subscriber = crate::registry().with(NopLayer).with(
NopLayer
.with_filter(LevelFilter::INFO)
.and_then(NopLayer.with_filter(LevelFilter::TRACE)),
);
assert_eq!(dbg!(subscriber).max_level_hint(), None);
}
#[test]
fn mixed_interleaved() {
let subscriber = crate::registry()
.with(NopLayer)
.with(NopLayer.with_filter(LevelFilter::INFO))
.with(NopLayer)
.with(NopLayer.with_filter(LevelFilter::INFO));
assert_eq!(dbg!(subscriber).max_level_hint(), None);
}
#[test]
fn mixed_layered() {
let subscriber = crate::registry()
.with(NopLayer.with_filter(LevelFilter::INFO).and_then(NopLayer))
.with(NopLayer.and_then(NopLayer.with_filter(LevelFilter::INFO)));
assert_eq!(dbg!(subscriber).max_level_hint(), None);
}
#[test]
fn plf_only_unhinted() {
let subscriber = crate::registry()
.with(NopLayer.with_filter(LevelFilter::INFO))
.with(NopLayer.with_filter(filter_fn(|_| true)));
assert_eq!(dbg!(subscriber).max_level_hint(), None);
}
#[test]
fn plf_only_unhinted_nested_outer() {
// if a nested tree of per-layer filters has an _outer_ filter with
// no max level hint, it should return `None`.
let subscriber = crate::registry()
.with(
NopLayer
.with_filter(LevelFilter::INFO)
.and_then(NopLayer.with_filter(LevelFilter::WARN)),
)
.with(
NopLayer
.with_filter(filter_fn(|_| true))
.and_then(NopLayer.with_filter(LevelFilter::DEBUG)),
);
assert_eq!(dbg!(subscriber).max_level_hint(), None);
}
#[test]
fn plf_only_unhinted_nested_inner() {
// If a nested tree of per-layer filters has an _inner_ filter with
// no max-level hint, but the _outer_ filter has a max level hint,
// it should pick the outer hint. This is because the outer filter
// will disable the spans/events before they make it to the inner
// filter.
let subscriber = dbg!(crate::registry().with(
NopLayer
.with_filter(filter_fn(|_| true))
.and_then(NopLayer.with_filter(filter_fn(|_| true)))
.with_filter(LevelFilter::INFO),
));
assert_eq!(dbg!(subscriber).max_level_hint(), Some(LevelFilter::INFO));
}
#[test]
fn unhinted_nested_inner() {
let subscriber = dbg!(crate::registry()
.with(NopLayer.and_then(NopLayer).with_filter(LevelFilter::INFO))
.with(
NopLayer
.with_filter(filter_fn(|_| true))
.and_then(NopLayer.with_filter(filter_fn(|_| true)))
.with_filter(LevelFilter::WARN),
));
assert_eq!(dbg!(subscriber).max_level_hint(), Some(LevelFilter::INFO));
}
#[test]
fn unhinted_nested_inner_mixed() {
let subscriber = dbg!(crate::registry()
.with(
NopLayer
.and_then(NopLayer.with_filter(filter_fn(|_| true)))
.with_filter(LevelFilter::INFO)
)
.with(
NopLayer
.with_filter(filter_fn(|_| true))
.and_then(NopLayer.with_filter(filter_fn(|_| true)))
.with_filter(LevelFilter::WARN),
));
assert_eq!(dbg!(subscriber).max_level_hint(), Some(LevelFilter::INFO));
}
#[test]
fn plf_only_picks_max() {
let subscriber = crate::registry()
.with(NopLayer.with_filter(LevelFilter::WARN))
.with(NopLayer.with_filter(LevelFilter::DEBUG));
assert_eq!(dbg!(subscriber).max_level_hint(), Some(LevelFilter::DEBUG));
}
#[test]
fn many_plf_only_picks_max() {
let subscriber = crate::registry()
.with(NopLayer.with_filter(LevelFilter::WARN))
.with(NopLayer.with_filter(LevelFilter::DEBUG))
.with(NopLayer.with_filter(LevelFilter::INFO))
.with(NopLayer.with_filter(LevelFilter::ERROR));
assert_eq!(dbg!(subscriber).max_level_hint(), Some(LevelFilter::DEBUG));
}
#[test]
fn nested_plf_only_picks_max() {
let subscriber = crate::registry()
.with(
NopLayer.with_filter(LevelFilter::INFO).and_then(
NopLayer
.with_filter(LevelFilter::WARN)
.and_then(NopLayer.with_filter(LevelFilter::DEBUG)),
),
)
.with(
NopLayer
.with_filter(LevelFilter::INFO)
.and_then(NopLayer.with_filter(LevelFilter::ERROR)),
);
assert_eq!(dbg!(subscriber).max_level_hint(), Some(LevelFilter::DEBUG));
}
}
}