| 1 | use super::*; |
| 2 | use tracing_core::subscriber::NoSubscriber; |
| 3 | |
| 4 | #[derive(Debug)] |
| 5 | pub(crate) struct NopLayer; |
| 6 | impl<S: Subscriber> Layer<S> for NopLayer {} |
| 7 | |
| 8 | #[allow (dead_code)] |
| 9 | struct NopLayer2; |
| 10 | impl<S: Subscriber> Layer<S> for NopLayer2 {} |
| 11 | |
| 12 | /// A layer that holds a string. |
| 13 | /// |
| 14 | /// Used to test that pointers returned by downcasting are actually valid. |
| 15 | struct StringLayer(&'static str); |
| 16 | impl<S: Subscriber> Layer<S> for StringLayer {} |
| 17 | struct StringLayer2(&'static str); |
| 18 | impl<S: Subscriber> Layer<S> for StringLayer2 {} |
| 19 | |
| 20 | struct StringLayer3(&'static str); |
| 21 | impl<S: Subscriber> Layer<S> for StringLayer3 {} |
| 22 | |
| 23 | pub(crate) struct StringSubscriber(&'static str); |
| 24 | |
| 25 | impl Subscriber for StringSubscriber { |
| 26 | fn register_callsite(&self, _: &'static Metadata<'static>) -> Interest { |
| 27 | Interest::never() |
| 28 | } |
| 29 | |
| 30 | fn enabled(&self, _: &Metadata<'_>) -> bool { |
| 31 | false |
| 32 | } |
| 33 | |
| 34 | fn new_span(&self, _: &span::Attributes<'_>) -> span::Id { |
| 35 | span::Id::from_u64(1) |
| 36 | } |
| 37 | |
| 38 | fn record(&self, _: &span::Id, _: &span::Record<'_>) {} |
| 39 | fn record_follows_from(&self, _: &span::Id, _: &span::Id) {} |
| 40 | fn event(&self, _: &Event<'_>) {} |
| 41 | fn enter(&self, _: &span::Id) {} |
| 42 | fn exit(&self, _: &span::Id) {} |
| 43 | } |
| 44 | |
| 45 | fn assert_subscriber(_s: impl Subscriber) {} |
| 46 | fn assert_layer<S: Subscriber>(_l: &impl Layer<S>) {} |
| 47 | |
| 48 | #[test] |
| 49 | fn layer_is_subscriber() { |
| 50 | let s = NopLayer.with_subscriber(NoSubscriber::default()); |
| 51 | assert_subscriber(s) |
| 52 | } |
| 53 | |
| 54 | #[test] |
| 55 | fn two_layers_are_subscriber() { |
| 56 | let s = NopLayer |
| 57 | .and_then(NopLayer) |
| 58 | .with_subscriber(NoSubscriber::default()); |
| 59 | assert_subscriber(s) |
| 60 | } |
| 61 | |
| 62 | #[test] |
| 63 | fn three_layers_are_subscriber() { |
| 64 | let s = NopLayer |
| 65 | .and_then(NopLayer) |
| 66 | .and_then(NopLayer) |
| 67 | .with_subscriber(NoSubscriber::default()); |
| 68 | assert_subscriber(s) |
| 69 | } |
| 70 | |
| 71 | #[test] |
| 72 | fn three_layers_are_layer() { |
| 73 | let layers = NopLayer.and_then(NopLayer).and_then(NopLayer); |
| 74 | assert_layer(&layers); |
| 75 | let _ = layers.with_subscriber(NoSubscriber::default()); |
| 76 | } |
| 77 | |
| 78 | #[test] |
| 79 | #[cfg (feature = "alloc" )] |
| 80 | fn box_layer_is_layer() { |
| 81 | use alloc::boxed::Box; |
| 82 | let l: Box<dyn Layer<NoSubscriber> + Send + Sync> = Box::new(NopLayer); |
| 83 | assert_layer(&l); |
| 84 | l.with_subscriber(NoSubscriber::default()); |
| 85 | } |
| 86 | |
| 87 | #[test] |
| 88 | fn downcasts_to_subscriber() { |
| 89 | let s = NopLayer |
| 90 | .and_then(NopLayer) |
| 91 | .and_then(NopLayer) |
| 92 | .with_subscriber(StringSubscriber("subscriber" )); |
| 93 | let subscriber = |
| 94 | <dyn Subscriber>::downcast_ref::<StringSubscriber>(&s).expect("subscriber should downcast" ); |
| 95 | assert_eq!(subscriber.0, "subscriber" ); |
| 96 | } |
| 97 | |
| 98 | #[test] |
| 99 | fn downcasts_to_layer() { |
| 100 | let s = StringLayer("layer_1" ) |
| 101 | .and_then(StringLayer2("layer_2" )) |
| 102 | .and_then(StringLayer3("layer_3" )) |
| 103 | .with_subscriber(NoSubscriber::default()); |
| 104 | let layer = <dyn Subscriber>::downcast_ref::<StringLayer>(&s).expect("layer 1 should downcast" ); |
| 105 | assert_eq!(layer.0, "layer_1" ); |
| 106 | let layer = |
| 107 | <dyn Subscriber>::downcast_ref::<StringLayer2>(&s).expect("layer 2 should downcast" ); |
| 108 | assert_eq!(layer.0, "layer_2" ); |
| 109 | let layer = |
| 110 | <dyn Subscriber>::downcast_ref::<StringLayer3>(&s).expect("layer 3 should downcast" ); |
| 111 | assert_eq!(layer.0, "layer_3" ); |
| 112 | } |
| 113 | |
| 114 | #[cfg (all(feature = "registry" , feature = "std" ))] |
| 115 | mod registry_tests { |
| 116 | use super::*; |
| 117 | use crate::registry::LookupSpan; |
| 118 | |
| 119 | #[test] |
| 120 | fn context_event_span() { |
| 121 | use std::sync::{Arc, Mutex}; |
| 122 | let last_event_span = Arc::new(Mutex::new(None)); |
| 123 | |
| 124 | struct RecordingLayer { |
| 125 | last_event_span: Arc<Mutex<Option<&'static str>>>, |
| 126 | } |
| 127 | |
| 128 | impl<S> Layer<S> for RecordingLayer |
| 129 | where |
| 130 | S: Subscriber + for<'lookup> LookupSpan<'lookup>, |
| 131 | { |
| 132 | fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) { |
| 133 | let span = ctx.event_span(event); |
| 134 | *self.last_event_span.lock().unwrap() = span.map(|s| s.name()); |
| 135 | } |
| 136 | } |
| 137 | |
| 138 | tracing::subscriber::with_default( |
| 139 | crate::registry().with(RecordingLayer { |
| 140 | last_event_span: last_event_span.clone(), |
| 141 | }), |
| 142 | || { |
| 143 | tracing::info!("no span" ); |
| 144 | assert_eq!(*last_event_span.lock().unwrap(), None); |
| 145 | |
| 146 | let parent = tracing::info_span!("explicit" ); |
| 147 | tracing::info!(parent: &parent, "explicit span" ); |
| 148 | assert_eq!(*last_event_span.lock().unwrap(), Some("explicit" )); |
| 149 | |
| 150 | let _guard = tracing::info_span!("contextual" ).entered(); |
| 151 | tracing::info!("contextual span" ); |
| 152 | assert_eq!(*last_event_span.lock().unwrap(), Some("contextual" )); |
| 153 | }, |
| 154 | ); |
| 155 | } |
| 156 | |
| 157 | /// Tests for how max-level hints are calculated when combining layers |
| 158 | /// with and without per-layer filtering. |
| 159 | mod max_level_hints { |
| 160 | |
| 161 | use super::*; |
| 162 | use crate::filter::*; |
| 163 | |
| 164 | #[test] |
| 165 | fn mixed_with_unfiltered() { |
| 166 | let subscriber = crate::registry() |
| 167 | .with(NopLayer) |
| 168 | .with(NopLayer.with_filter(LevelFilter::INFO)); |
| 169 | assert_eq!(subscriber.max_level_hint(), None); |
| 170 | } |
| 171 | |
| 172 | #[test] |
| 173 | fn mixed_with_unfiltered_layered() { |
| 174 | let subscriber = crate::registry().with(NopLayer).with( |
| 175 | NopLayer |
| 176 | .with_filter(LevelFilter::INFO) |
| 177 | .and_then(NopLayer.with_filter(LevelFilter::TRACE)), |
| 178 | ); |
| 179 | assert_eq!(dbg!(subscriber).max_level_hint(), None); |
| 180 | } |
| 181 | |
| 182 | #[test] |
| 183 | fn mixed_interleaved() { |
| 184 | let subscriber = crate::registry() |
| 185 | .with(NopLayer) |
| 186 | .with(NopLayer.with_filter(LevelFilter::INFO)) |
| 187 | .with(NopLayer) |
| 188 | .with(NopLayer.with_filter(LevelFilter::INFO)); |
| 189 | assert_eq!(dbg!(subscriber).max_level_hint(), None); |
| 190 | } |
| 191 | |
| 192 | #[test] |
| 193 | fn mixed_layered() { |
| 194 | let subscriber = crate::registry() |
| 195 | .with(NopLayer.with_filter(LevelFilter::INFO).and_then(NopLayer)) |
| 196 | .with(NopLayer.and_then(NopLayer.with_filter(LevelFilter::INFO))); |
| 197 | assert_eq!(dbg!(subscriber).max_level_hint(), None); |
| 198 | } |
| 199 | |
| 200 | #[test] |
| 201 | fn plf_only_unhinted() { |
| 202 | let subscriber = crate::registry() |
| 203 | .with(NopLayer.with_filter(LevelFilter::INFO)) |
| 204 | .with(NopLayer.with_filter(filter_fn(|_| true))); |
| 205 | assert_eq!(dbg!(subscriber).max_level_hint(), None); |
| 206 | } |
| 207 | |
| 208 | #[test] |
| 209 | fn plf_only_unhinted_nested_outer() { |
| 210 | // if a nested tree of per-layer filters has an _outer_ filter with |
| 211 | // no max level hint, it should return `None`. |
| 212 | let subscriber = crate::registry() |
| 213 | .with( |
| 214 | NopLayer |
| 215 | .with_filter(LevelFilter::INFO) |
| 216 | .and_then(NopLayer.with_filter(LevelFilter::WARN)), |
| 217 | ) |
| 218 | .with( |
| 219 | NopLayer |
| 220 | .with_filter(filter_fn(|_| true)) |
| 221 | .and_then(NopLayer.with_filter(LevelFilter::DEBUG)), |
| 222 | ); |
| 223 | assert_eq!(dbg!(subscriber).max_level_hint(), None); |
| 224 | } |
| 225 | |
| 226 | #[test] |
| 227 | fn plf_only_unhinted_nested_inner() { |
| 228 | // If a nested tree of per-layer filters has an _inner_ filter with |
| 229 | // no max-level hint, but the _outer_ filter has a max level hint, |
| 230 | // it should pick the outer hint. This is because the outer filter |
| 231 | // will disable the spans/events before they make it to the inner |
| 232 | // filter. |
| 233 | let subscriber = dbg!(crate::registry().with( |
| 234 | NopLayer |
| 235 | .with_filter(filter_fn(|_| true)) |
| 236 | .and_then(NopLayer.with_filter(filter_fn(|_| true))) |
| 237 | .with_filter(LevelFilter::INFO), |
| 238 | )); |
| 239 | assert_eq!(dbg!(subscriber).max_level_hint(), Some(LevelFilter::INFO)); |
| 240 | } |
| 241 | |
| 242 | #[test] |
| 243 | fn unhinted_nested_inner() { |
| 244 | let subscriber = dbg!(crate::registry() |
| 245 | .with(NopLayer.and_then(NopLayer).with_filter(LevelFilter::INFO)) |
| 246 | .with( |
| 247 | NopLayer |
| 248 | .with_filter(filter_fn(|_| true)) |
| 249 | .and_then(NopLayer.with_filter(filter_fn(|_| true))) |
| 250 | .with_filter(LevelFilter::WARN), |
| 251 | )); |
| 252 | assert_eq!(dbg!(subscriber).max_level_hint(), Some(LevelFilter::INFO)); |
| 253 | } |
| 254 | |
| 255 | #[test] |
| 256 | fn unhinted_nested_inner_mixed() { |
| 257 | let subscriber = dbg!(crate::registry() |
| 258 | .with( |
| 259 | NopLayer |
| 260 | .and_then(NopLayer.with_filter(filter_fn(|_| true))) |
| 261 | .with_filter(LevelFilter::INFO) |
| 262 | ) |
| 263 | .with( |
| 264 | NopLayer |
| 265 | .with_filter(filter_fn(|_| true)) |
| 266 | .and_then(NopLayer.with_filter(filter_fn(|_| true))) |
| 267 | .with_filter(LevelFilter::WARN), |
| 268 | )); |
| 269 | assert_eq!(dbg!(subscriber).max_level_hint(), Some(LevelFilter::INFO)); |
| 270 | } |
| 271 | |
| 272 | #[test] |
| 273 | fn plf_only_picks_max() { |
| 274 | let subscriber = crate::registry() |
| 275 | .with(NopLayer.with_filter(LevelFilter::WARN)) |
| 276 | .with(NopLayer.with_filter(LevelFilter::DEBUG)); |
| 277 | assert_eq!(dbg!(subscriber).max_level_hint(), Some(LevelFilter::DEBUG)); |
| 278 | } |
| 279 | |
| 280 | #[test] |
| 281 | fn many_plf_only_picks_max() { |
| 282 | let subscriber = crate::registry() |
| 283 | .with(NopLayer.with_filter(LevelFilter::WARN)) |
| 284 | .with(NopLayer.with_filter(LevelFilter::DEBUG)) |
| 285 | .with(NopLayer.with_filter(LevelFilter::INFO)) |
| 286 | .with(NopLayer.with_filter(LevelFilter::ERROR)); |
| 287 | assert_eq!(dbg!(subscriber).max_level_hint(), Some(LevelFilter::DEBUG)); |
| 288 | } |
| 289 | |
| 290 | #[test] |
| 291 | fn nested_plf_only_picks_max() { |
| 292 | let subscriber = crate::registry() |
| 293 | .with( |
| 294 | NopLayer.with_filter(LevelFilter::INFO).and_then( |
| 295 | NopLayer |
| 296 | .with_filter(LevelFilter::WARN) |
| 297 | .and_then(NopLayer.with_filter(LevelFilter::DEBUG)), |
| 298 | ), |
| 299 | ) |
| 300 | .with( |
| 301 | NopLayer |
| 302 | .with_filter(LevelFilter::INFO) |
| 303 | .and_then(NopLayer.with_filter(LevelFilter::ERROR)), |
| 304 | ); |
| 305 | assert_eq!(dbg!(subscriber).max_level_hint(), Some(LevelFilter::DEBUG)); |
| 306 | } |
| 307 | } |
| 308 | } |
| 309 | |