1 //! A `Layer` that enables or disables spans and events based on a set of 2 //! filtering directives. 3 4 // these are publicly re-exported, but the compiler doesn't realize 5 // that for some reason. 6 #[allow(unreachable_pub)] 7 pub use self::{ 8 directive::{Directive, ParseError}, 9 field::BadName as BadFieldName, 10 }; 11 mod directive; 12 mod field; 13 14 use crate::{ 15 filter::LevelFilter, 16 layer::{Context, Layer}, 17 sync::RwLock, 18 }; 19 use std::{cell::RefCell, collections::HashMap, env, error::Error, fmt, str::FromStr}; 20 use tracing_core::{ 21 callsite, 22 field::Field, 23 span, 24 subscriber::{Interest, Subscriber}, 25 Metadata, 26 }; 27 28 /// A [`Layer`] which filters spans and events based on a set of filter 29 /// directives. 30 /// 31 /// # Directives 32 /// 33 /// A filter consists of one or more directives which match on [`Span`]s and [`Event`]s. 34 /// Each directive may have a corresponding maximum verbosity [`level`] which 35 /// enables (e.g., _selects for_) spans and events that match. Like `log`, 36 /// `tracing` considers less exclusive levels (like `trace` or `info`) to be more 37 /// verbose than more exclusive levels (like `error` or `warn`). 38 /// 39 /// The directive syntax is similar to that of [`env_logger`]'s. At a high level, the syntax for directives 40 /// consists of several parts: 41 /// 42 /// ```text 43 /// target[span{field=value}]=level 44 /// ``` 45 /// 46 /// Each component (`target`, `span`, `field`, `value`, and `level`) will be covered in turn. 47 /// 48 /// - `target` matches the event or span's target. In general, this is the module path and/or crate name. 49 /// Examples of targets `h2`, `tokio::net`, or `tide::server`. For more information on targets, 50 /// please refer to [`Metadata`]'s documentation. 51 /// - `span` matches on the span's name. If a `span` directive is provided alongside a `target`, 52 /// the `span` directive will match on spans _within_ the `target`. 53 /// - `field` matches on [fields] within spans. Field names can also be supplied without a `value` 54 /// and will match on any [`Span`] or [`Event`] that has a field with that name. 55 /// For example: `[span{field=\"value\"}]=debug`, `[{field}]=trace`. 56 /// - `value` matches on the value of a span's field. If a value is a numeric literal or a bool, 57 /// it will match _only_ on that value. Otherwise, this filter acts as a regex on 58 /// the `std::fmt::Debug` output from the value. 59 /// - `level` sets a maximum verbosity level accepted by this directive. 60 /// 61 /// ## Usage Notes 62 /// 63 /// - The portion of the directive which is included within the square brackets is `tracing`-specific. 64 /// - Any portion of the directive can be omitted. 65 /// - The sole exception are the `field` and `value` directives. If a `value` is provided, 66 /// a `field` must _also_ be provided. However, the converse does not hold, as fields can 67 /// be matched without a value. 68 /// - If only a level is provided, it will set the maximum level for all `Span`s and `Event`s 69 /// that are not enabled by other filters. 70 /// - A directive without a level will enable anything that it matches. This is equivalent to `=trace`. 71 /// - When a crate has a dash in its name, the default target for events will be the 72 /// crate's module path as it appears in Rust. This means every dash will be replaced 73 /// with an underscore. 74 /// - A dash in a target will only appear when being specified explicitly: 75 /// `tracing::info!(target: "target-name", ...);` 76 /// 77 /// ## Examples 78 /// 79 /// - `tokio::net=info` will enable all spans or events that: 80 /// - have the `tokio::net` target, 81 /// - at the level `info` or above. 82 /// - `my_crate[span_a]=trace` will enable all spans and events that: 83 /// - are within the `span_a` span or named `span_a` _if_ `span_a` has the target `my_crate`, 84 /// - at the level `trace` or above. 85 /// - `[span_b{name=\"bob\"}]` will enable all spans or event that: 86 /// - have _any_ target, 87 /// - are inside a span named `span_b`, 88 /// - which has a field named `name` with value `bob`, 89 /// - at _any_ level. 90 /// 91 /// [`Layer`]: ../layer/trait.Layer.html 92 /// [`env_logger`]: https://docs.rs/env_logger/0.7.1/env_logger/#enabling-logging 93 /// [`Span`]: https://docs.rs/tracing-core/latest/tracing_core/span/index.html 94 /// [fields]: https://docs.rs/tracing-core/latest/tracing_core/struct.Field.html 95 /// [`Event`]: https://docs.rs/tracing-core/latest/tracing_core/struct.Event.html 96 /// [`level`]: https://docs.rs/tracing-core/latest/tracing_core/struct.Level.html 97 /// [`Metadata`]: https://docs.rs/tracing-core/latest/tracing_core/struct.Metadata.html 98 #[cfg(feature = "env-filter")] 99 #[cfg_attr(docsrs, doc(cfg(feature = "env-filter")))] 100 #[derive(Debug)] 101 pub struct EnvFilter { 102 statics: directive::Statics, 103 dynamics: directive::Dynamics, 104 has_dynamics: bool, 105 by_id: RwLock<HashMap<span::Id, directive::SpanMatcher>>, 106 by_cs: RwLock<HashMap<callsite::Identifier, directive::CallsiteMatcher>>, 107 } 108 109 thread_local! { 110 static SCOPE: RefCell<Vec<LevelFilter>> = RefCell::new(Vec::new()); 111 } 112 113 type FieldMap<T> = HashMap<Field, T>; 114 115 #[cfg(feature = "smallvec")] 116 type FilterVec<T> = smallvec::SmallVec<[T; 8]>; 117 #[cfg(not(feature = "smallvec"))] 118 type FilterVec<T> = Vec<T>; 119 120 /// Indicates that an error occurred while parsing a `EnvFilter` from an 121 /// environment variable. 122 #[derive(Debug)] 123 pub struct FromEnvError { 124 kind: ErrorKind, 125 } 126 127 #[derive(Debug)] 128 enum ErrorKind { 129 Parse(ParseError), 130 Env(env::VarError), 131 } 132 133 impl EnvFilter { 134 /// `RUST_LOG` is the default environment variable used by 135 /// [`EnvFilter::from_default_env`] and [`EnvFilter::try_from_default_env`]. 136 /// 137 /// [`EnvFilter::from_default_env`]: #method.from_default_env 138 /// [`EnvFilter::try_from_default_env`]: #method.try_from_default_env 139 pub const DEFAULT_ENV: &'static str = "RUST_LOG"; 140 141 /// Returns a new `EnvFilter` from the value of the `RUST_LOG` environment 142 /// variable, ignoring any invalid filter directives. from_default_env() -> Self143 pub fn from_default_env() -> Self { 144 Self::from_env(Self::DEFAULT_ENV) 145 } 146 147 /// Returns a new `EnvFilter` from the value of the given environment 148 /// variable, ignoring any invalid filter directives. from_env<A: AsRef<str>>(env: A) -> Self149 pub fn from_env<A: AsRef<str>>(env: A) -> Self { 150 env::var(env.as_ref()).map(Self::new).unwrap_or_default() 151 } 152 153 /// Returns a new `EnvFilter` from the directives in the given string, 154 /// ignoring any that are invalid. new<S: AsRef<str>>(dirs: S) -> Self155 pub fn new<S: AsRef<str>>(dirs: S) -> Self { 156 let directives = dirs.as_ref().split(',').filter_map(|s| match s.parse() { 157 Ok(d) => Some(d), 158 Err(err) => { 159 eprintln!("ignoring `{}`: {}", s, err); 160 None 161 } 162 }); 163 Self::from_directives(directives) 164 } 165 166 /// Returns a new `EnvFilter` from the directives in the given string, 167 /// or an error if any are invalid. try_new<S: AsRef<str>>(dirs: S) -> Result<Self, ParseError>168 pub fn try_new<S: AsRef<str>>(dirs: S) -> Result<Self, ParseError> { 169 let directives = dirs 170 .as_ref() 171 .split(',') 172 .map(|s| s.parse()) 173 .collect::<Result<Vec<_>, _>>()?; 174 Ok(Self::from_directives(directives)) 175 } 176 177 /// Returns a new `EnvFilter` from the value of the `RUST_LOG` environment 178 /// variable, or an error if the environment variable contains any invalid 179 /// filter directives. try_from_default_env() -> Result<Self, FromEnvError>180 pub fn try_from_default_env() -> Result<Self, FromEnvError> { 181 Self::try_from_env(Self::DEFAULT_ENV) 182 } 183 184 /// Returns a new `EnvFilter` from the value of the given environment 185 /// variable, or an error if the environment variable is unset or contains 186 /// any invalid filter directives. try_from_env<A: AsRef<str>>(env: A) -> Result<Self, FromEnvError>187 pub fn try_from_env<A: AsRef<str>>(env: A) -> Result<Self, FromEnvError> { 188 env::var(env.as_ref())?.parse().map_err(Into::into) 189 } 190 191 /// Add a filtering directive to this `EnvFilter`. 192 /// 193 /// The added directive will be used in addition to any previously set 194 /// directives, either added using this method or provided when the filter 195 /// is constructed. 196 /// 197 /// Filters may be created from [`LevelFilter`] or [`Level`], which will 198 /// enable all traces at or below a certain verbosity level, or 199 /// parsed from a string specifying a directive. 200 /// 201 /// If a filter directive is inserted that matches exactly the same spans 202 /// and events as a previous filter, but sets a different level for those 203 /// spans and events, the previous directive is overwritten. 204 /// 205 /// [`LevelFilter`]: ../filter/struct.LevelFilter.html 206 /// [`Level`]: https://docs.rs/tracing-core/latest/tracing_core/struct.Level.html 207 /// 208 /// # Examples 209 /// 210 /// From [`LevelFilter`]: 211 //// 212 /// ```rust 213 /// use tracing_subscriber::filter::{EnvFilter, LevelFilter}; 214 /// let mut filter = EnvFilter::from_default_env() 215 /// .add_directive(LevelFilter::INFO.into()); 216 /// ``` 217 /// 218 /// Or from [`Level`]: 219 /// 220 /// ```rust 221 /// # use tracing_subscriber::filter::{EnvFilter, LevelFilter}; 222 /// # use tracing::Level; 223 /// let mut filter = EnvFilter::from_default_env() 224 /// .add_directive(Level::INFO.into()); 225 /// ``` 226 //// 227 /// Parsed from a string: 228 //// 229 /// ```rust 230 /// use tracing_subscriber::filter::{EnvFilter, Directive}; 231 /// 232 /// # fn try_mk_filter() -> Result<(), Box<dyn ::std::error::Error>> { 233 /// let mut filter = EnvFilter::try_from_default_env()? 234 /// .add_directive("my_crate::module=trace".parse()?) 235 /// .add_directive("my_crate::my_other_module::something=info".parse()?); 236 /// # Ok(()) 237 /// # } 238 /// ``` add_directive(mut self, directive: Directive) -> Self239 pub fn add_directive(mut self, directive: Directive) -> Self { 240 if let Some(stat) = directive.to_static() { 241 self.statics.add(stat) 242 } else { 243 self.has_dynamics = true; 244 self.dynamics.add(directive); 245 } 246 self 247 } 248 from_directives(directives: impl IntoIterator<Item = Directive>) -> Self249 fn from_directives(directives: impl IntoIterator<Item = Directive>) -> Self { 250 use tracing::level_filters::STATIC_MAX_LEVEL; 251 use tracing::Level; 252 253 let directives: Vec<_> = directives.into_iter().collect(); 254 255 let disabled: Vec<_> = directives 256 .iter() 257 .filter(|directive| directive.level > STATIC_MAX_LEVEL) 258 .collect(); 259 260 if !disabled.is_empty() { 261 #[cfg(feature = "ansi_term")] 262 use ansi_term::{Color, Style}; 263 // NOTE: We can't use a configured `MakeWriter` because the EnvFilter 264 // has no knowledge of any underlying subscriber or collector, which 265 // may or may not use a `MakeWriter`. 266 let warn = |msg: &str| { 267 #[cfg(not(feature = "ansi_term"))] 268 let msg = format!("warning: {}", msg); 269 #[cfg(feature = "ansi_term")] 270 let msg = { 271 let bold = Style::new().bold(); 272 let mut warning = Color::Yellow.paint("warning"); 273 warning.style_ref_mut().is_bold = true; 274 format!("{}{} {}", warning, bold.clone().paint(":"), bold.paint(msg)) 275 }; 276 eprintln!("{}", msg); 277 }; 278 let ctx_prefixed = |prefix: &str, msg: &str| { 279 #[cfg(not(feature = "ansi_term"))] 280 let msg = format!("note: {}", msg); 281 #[cfg(feature = "ansi_term")] 282 let msg = { 283 let mut equal = Color::Fixed(21).paint("="); // dark blue 284 equal.style_ref_mut().is_bold = true; 285 format!(" {} {} {}", equal, Style::new().bold().paint(prefix), msg) 286 }; 287 eprintln!("{}", msg); 288 }; 289 let ctx_help = |msg| ctx_prefixed("help:", msg); 290 let ctx_note = |msg| ctx_prefixed("note:", msg); 291 let ctx = |msg: &str| { 292 #[cfg(not(feature = "ansi_term"))] 293 let msg = format!("note: {}", msg); 294 #[cfg(feature = "ansi_term")] 295 let msg = { 296 let mut pipe = Color::Fixed(21).paint("|"); 297 pipe.style_ref_mut().is_bold = true; 298 format!(" {} {}", pipe, msg) 299 }; 300 eprintln!("{}", msg); 301 }; 302 warn("some trace filter directives would enable traces that are disabled statically"); 303 for directive in disabled { 304 let target = if let Some(target) = &directive.target { 305 format!("the `{}` target", target) 306 } else { 307 "all targets".into() 308 }; 309 let level = directive 310 .level 311 .clone() 312 .into_level() 313 .expect("=off would not have enabled any filters"); 314 ctx(&format!( 315 "`{}` would enable the {} level for {}", 316 directive, level, target 317 )); 318 } 319 ctx_note(&format!("the static max level is `{}`", STATIC_MAX_LEVEL)); 320 let help_msg = || { 321 let (feature, filter) = match STATIC_MAX_LEVEL.into_level() { 322 Some(Level::TRACE) => unreachable!( 323 "if the max level is trace, no static filtering features are enabled" 324 ), 325 Some(Level::DEBUG) => ("max_level_debug", Level::TRACE), 326 Some(Level::INFO) => ("max_level_info", Level::DEBUG), 327 Some(Level::WARN) => ("max_level_warn", Level::INFO), 328 Some(Level::ERROR) => ("max_level_error", Level::WARN), 329 None => return ("max_level_off", String::new()), 330 }; 331 (feature, format!("{} ", filter)) 332 }; 333 let (feature, earlier_level) = help_msg(); 334 ctx_help(&format!( 335 "to enable {}logging, remove the `{}` feature", 336 earlier_level, feature 337 )); 338 } 339 340 let (dynamics, mut statics) = Directive::make_tables(directives); 341 let has_dynamics = !dynamics.is_empty(); 342 343 if statics.is_empty() && !has_dynamics { 344 statics.add(directive::StaticDirective::default()); 345 } 346 347 Self { 348 statics, 349 dynamics, 350 has_dynamics, 351 by_id: RwLock::new(HashMap::new()), 352 by_cs: RwLock::new(HashMap::new()), 353 } 354 } 355 cares_about_span(&self, span: &span::Id) -> bool356 fn cares_about_span(&self, span: &span::Id) -> bool { 357 let spans = try_lock!(self.by_id.read(), else return false); 358 spans.contains_key(span) 359 } 360 base_interest(&self) -> Interest361 fn base_interest(&self) -> Interest { 362 if self.has_dynamics { 363 Interest::sometimes() 364 } else { 365 Interest::never() 366 } 367 } 368 } 369 370 impl<S: Subscriber> Layer<S> for EnvFilter { register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest371 fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest { 372 if self.has_dynamics && metadata.is_span() { 373 // If this metadata describes a span, first, check if there is a 374 // dynamic filter that should be constructed for it. If so, it 375 // should always be enabled, since it influences filtering. 376 if let Some(matcher) = self.dynamics.matcher(metadata) { 377 let mut by_cs = try_lock!(self.by_cs.write(), else return self.base_interest()); 378 by_cs.insert(metadata.callsite(), matcher); 379 return Interest::always(); 380 } 381 } 382 383 // Otherwise, check if any of our static filters enable this metadata. 384 if self.statics.enabled(metadata) { 385 Interest::always() 386 } else { 387 self.base_interest() 388 } 389 } 390 max_level_hint(&self) -> Option<LevelFilter>391 fn max_level_hint(&self) -> Option<LevelFilter> { 392 if self.dynamics.has_value_filters() { 393 // If we perform any filtering on span field *values*, we will 394 // enable *all* spans, because their field values are not known 395 // until recording. 396 return Some(LevelFilter::TRACE); 397 } 398 std::cmp::max( 399 self.statics.max_level.clone().into(), 400 self.dynamics.max_level.clone().into(), 401 ) 402 } 403 enabled(&self, metadata: &Metadata<'_>, _: Context<'_, S>) -> bool404 fn enabled(&self, metadata: &Metadata<'_>, _: Context<'_, S>) -> bool { 405 let level = metadata.level(); 406 407 // is it possible for a dynamic filter directive to enable this event? 408 // if not, we can avoid the thread local access + iterating over the 409 // spans in the current scope. 410 if self.has_dynamics && self.dynamics.max_level >= *level { 411 if metadata.is_span() { 412 // If the metadata is a span, see if we care about its callsite. 413 let enabled_by_cs = self 414 .by_cs 415 .read() 416 .ok() 417 .map(|by_cs| by_cs.contains_key(&metadata.callsite())) 418 .unwrap_or(false); 419 if enabled_by_cs { 420 return true; 421 } 422 } 423 424 let enabled_by_scope = SCOPE.with(|scope| { 425 for filter in scope.borrow().iter() { 426 if filter >= level { 427 return true; 428 } 429 } 430 false 431 }); 432 if enabled_by_scope { 433 return true; 434 } 435 } 436 437 // is it possible for a static filter directive to enable this event? 438 if self.statics.max_level >= *level { 439 // Otherwise, fall back to checking if the callsite is 440 // statically enabled. 441 return self.statics.enabled(metadata); 442 } 443 444 false 445 } 446 new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, _: Context<'_, S>)447 fn new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, _: Context<'_, S>) { 448 let by_cs = try_lock!(self.by_cs.read()); 449 if let Some(cs) = by_cs.get(&attrs.metadata().callsite()) { 450 let span = cs.to_span_match(attrs); 451 try_lock!(self.by_id.write()).insert(id.clone(), span); 452 } 453 } 454 on_record(&self, id: &span::Id, values: &span::Record<'_>, _: Context<'_, S>)455 fn on_record(&self, id: &span::Id, values: &span::Record<'_>, _: Context<'_, S>) { 456 if let Some(span) = try_lock!(self.by_id.read()).get(id) { 457 span.record_update(values); 458 } 459 } 460 on_enter(&self, id: &span::Id, _: Context<'_, S>)461 fn on_enter(&self, id: &span::Id, _: Context<'_, S>) { 462 // XXX: This is where _we_ could push IDs to the stack instead, and use 463 // that to allow changing the filter while a span is already entered. 464 // But that might be much less efficient... 465 if let Some(span) = try_lock!(self.by_id.read()).get(id) { 466 SCOPE.with(|scope| scope.borrow_mut().push(span.level())); 467 } 468 } 469 on_exit(&self, id: &span::Id, _: Context<'_, S>)470 fn on_exit(&self, id: &span::Id, _: Context<'_, S>) { 471 if self.cares_about_span(id) { 472 SCOPE.with(|scope| scope.borrow_mut().pop()); 473 } 474 } 475 on_close(&self, id: span::Id, _: Context<'_, S>)476 fn on_close(&self, id: span::Id, _: Context<'_, S>) { 477 // If we don't need to acquire a write lock, avoid doing so. 478 if !self.cares_about_span(&id) { 479 return; 480 } 481 482 let mut spans = try_lock!(self.by_id.write()); 483 spans.remove(&id); 484 } 485 } 486 487 impl FromStr for EnvFilter { 488 type Err = ParseError; 489 from_str(spec: &str) -> Result<Self, Self::Err>490 fn from_str(spec: &str) -> Result<Self, Self::Err> { 491 Self::try_new(spec) 492 } 493 } 494 495 impl<S> From<S> for EnvFilter 496 where 497 S: AsRef<str>, 498 { from(s: S) -> Self499 fn from(s: S) -> Self { 500 Self::new(s) 501 } 502 } 503 504 impl Default for EnvFilter { default() -> Self505 fn default() -> Self { 506 Self::from_directives(std::iter::empty()) 507 } 508 } 509 510 impl fmt::Display for EnvFilter { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result511 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 512 let mut statics = self.statics.iter(); 513 let wrote_statics = if let Some(next) = statics.next() { 514 fmt::Display::fmt(next, f)?; 515 for directive in statics { 516 write!(f, ",{}", directive)?; 517 } 518 true 519 } else { 520 false 521 }; 522 523 let mut dynamics = self.dynamics.iter(); 524 if let Some(next) = dynamics.next() { 525 if wrote_statics { 526 f.write_str(",")?; 527 } 528 fmt::Display::fmt(next, f)?; 529 for directive in dynamics { 530 write!(f, ",{}", directive)?; 531 } 532 } 533 Ok(()) 534 } 535 } 536 537 // ===== impl FromEnvError ===== 538 539 impl From<ParseError> for FromEnvError { from(p: ParseError) -> Self540 fn from(p: ParseError) -> Self { 541 Self { 542 kind: ErrorKind::Parse(p), 543 } 544 } 545 } 546 547 impl From<env::VarError> for FromEnvError { from(v: env::VarError) -> Self548 fn from(v: env::VarError) -> Self { 549 Self { 550 kind: ErrorKind::Env(v), 551 } 552 } 553 } 554 555 impl fmt::Display for FromEnvError { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result556 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 557 match self.kind { 558 ErrorKind::Parse(ref p) => p.fmt(f), 559 ErrorKind::Env(ref e) => e.fmt(f), 560 } 561 } 562 } 563 564 impl Error for FromEnvError { source(&self) -> Option<&(dyn Error + 'static)>565 fn source(&self) -> Option<&(dyn Error + 'static)> { 566 match self.kind { 567 ErrorKind::Parse(ref p) => Some(p), 568 ErrorKind::Env(ref e) => Some(e), 569 } 570 } 571 } 572 573 #[cfg(test)] 574 mod tests { 575 use super::*; 576 use tracing_core::field::FieldSet; 577 use tracing_core::*; 578 579 struct NoSubscriber; 580 impl Subscriber for NoSubscriber { 581 #[inline] register_callsite(&self, _: &'static Metadata<'static>) -> subscriber::Interest582 fn register_callsite(&self, _: &'static Metadata<'static>) -> subscriber::Interest { 583 subscriber::Interest::always() 584 } new_span(&self, _: &span::Attributes<'_>) -> span::Id585 fn new_span(&self, _: &span::Attributes<'_>) -> span::Id { 586 span::Id::from_u64(0xDEAD) 587 } event(&self, _event: &Event<'_>)588 fn event(&self, _event: &Event<'_>) {} record(&self, _span: &span::Id, _values: &span::Record<'_>)589 fn record(&self, _span: &span::Id, _values: &span::Record<'_>) {} record_follows_from(&self, _span: &span::Id, _follows: &span::Id)590 fn record_follows_from(&self, _span: &span::Id, _follows: &span::Id) {} 591 592 #[inline] enabled(&self, _metadata: &Metadata<'_>) -> bool593 fn enabled(&self, _metadata: &Metadata<'_>) -> bool { 594 true 595 } enter(&self, _span: &span::Id)596 fn enter(&self, _span: &span::Id) {} exit(&self, _span: &span::Id)597 fn exit(&self, _span: &span::Id) {} 598 } 599 600 struct Cs; 601 impl Callsite for Cs { set_interest(&self, _interest: Interest)602 fn set_interest(&self, _interest: Interest) {} metadata(&self) -> &Metadata<'_>603 fn metadata(&self) -> &Metadata<'_> { 604 unimplemented!() 605 } 606 } 607 608 #[test] callsite_enabled_no_span_directive()609 fn callsite_enabled_no_span_directive() { 610 let filter = EnvFilter::new("app=debug").with_subscriber(NoSubscriber); 611 static META: &Metadata<'static> = &Metadata::new( 612 "mySpan", 613 "app", 614 Level::TRACE, 615 None, 616 None, 617 None, 618 FieldSet::new(&[], identify_callsite!(&Cs)), 619 Kind::SPAN, 620 ); 621 622 let interest = filter.register_callsite(META); 623 assert!(interest.is_never()); 624 } 625 626 #[test] callsite_off()627 fn callsite_off() { 628 let filter = EnvFilter::new("app=off").with_subscriber(NoSubscriber); 629 static META: &Metadata<'static> = &Metadata::new( 630 "mySpan", 631 "app", 632 Level::ERROR, 633 None, 634 None, 635 None, 636 FieldSet::new(&[], identify_callsite!(&Cs)), 637 Kind::SPAN, 638 ); 639 640 let interest = filter.register_callsite(&META); 641 assert!(interest.is_never()); 642 } 643 644 #[test] callsite_enabled_includes_span_directive()645 fn callsite_enabled_includes_span_directive() { 646 let filter = EnvFilter::new("app[mySpan]=debug").with_subscriber(NoSubscriber); 647 static META: &Metadata<'static> = &Metadata::new( 648 "mySpan", 649 "app", 650 Level::TRACE, 651 None, 652 None, 653 None, 654 FieldSet::new(&[], identify_callsite!(&Cs)), 655 Kind::SPAN, 656 ); 657 658 let interest = filter.register_callsite(&META); 659 assert!(interest.is_always()); 660 } 661 662 #[test] callsite_enabled_includes_span_directive_field()663 fn callsite_enabled_includes_span_directive_field() { 664 let filter = 665 EnvFilter::new("app[mySpan{field=\"value\"}]=debug").with_subscriber(NoSubscriber); 666 static META: &Metadata<'static> = &Metadata::new( 667 "mySpan", 668 "app", 669 Level::TRACE, 670 None, 671 None, 672 None, 673 FieldSet::new(&["field"], identify_callsite!(&Cs)), 674 Kind::SPAN, 675 ); 676 677 let interest = filter.register_callsite(&META); 678 assert!(interest.is_always()); 679 } 680 681 #[test] callsite_enabled_includes_span_directive_multiple_fields()682 fn callsite_enabled_includes_span_directive_multiple_fields() { 683 let filter = EnvFilter::new("app[mySpan{field=\"value\",field2=2}]=debug") 684 .with_subscriber(NoSubscriber); 685 static META: &Metadata<'static> = &Metadata::new( 686 "mySpan", 687 "app", 688 Level::TRACE, 689 None, 690 None, 691 None, 692 FieldSet::new(&["field"], identify_callsite!(&Cs)), 693 Kind::SPAN, 694 ); 695 696 let interest = filter.register_callsite(&META); 697 assert!(interest.is_never()); 698 } 699 700 #[test] roundtrip()701 fn roundtrip() { 702 let f1: EnvFilter = 703 "[span1{foo=1}]=error,[span2{bar=2 baz=false}],crate2[{quux=\"quuux\"}]=debug" 704 .parse() 705 .unwrap(); 706 let f2: EnvFilter = format!("{}", f1).parse().unwrap(); 707 assert_eq!(f1.statics, f2.statics); 708 assert_eq!(f1.dynamics, f2.dynamics); 709 } 710 } 711