@@ -10,11 +10,12 @@ mod field;
1010
1111use crate :: {
1212 filter:: LevelFilter ,
13- subscribe:: { Context , Subscribe } ,
13+ subscribe:: { self , Context , Subscribe } ,
1414 sync:: RwLock ,
1515} ;
1616use directive:: ParseError ;
1717use std:: { cell:: RefCell , collections:: HashMap , env, error:: Error , fmt, str:: FromStr } ;
18+ use thread_local:: ThreadLocal ;
1819use tracing_core:: {
1920 callsite,
2021 collect:: { Collect , Interest } ,
@@ -25,6 +26,16 @@ use tracing_core::{
2526/// A [`Subscriber`] which filters spans and events based on a set of filter
2627/// directives.
2728///
29+ /// `EnvFilter` implements both the [`Subscribe`](#impl-Subscribe<C>) and
30+ /// [`Filter`] traits, so it may be used for both [global filtering][global] and
31+ /// [per-subscriber filtering][psf], respectively. See [the documentation on
32+ /// filtering with `Subscriber`s][filtering] for details.
33+ ///
34+ /// The [`Targets`] type implements a similar form of filtering, but without the
35+ /// ability to dynamically enable events based on the current span context, and
36+ /// without filtering on field values. When these features are not required,
37+ /// [`Targets`] provides a lighter-weight alternative to [`EnvFilter`].
38+ ///
2839/// # Directives
2940///
3041/// A filter consists of one or more comma-separated directives which match on [`Span`]s and [`Event`]s.
@@ -71,7 +82,7 @@ use tracing_core::{
7182/// - A dash in a target will only appear when being specified explicitly:
7283/// `tracing::info!(target: "target-name", ...);`
7384///
74- /// ## Examples
85+ /// ## Example Syntax
7586///
7687/// - `tokio::net=info` will enable all spans or events that:
7788/// - have the `tokio::net` target,
@@ -88,10 +99,54 @@ use tracing_core::{
8899/// - which has a field named `name` with value `bob`,
89100/// - at _any_ level.
90101///
91- /// The [`Targets`] type implements a similar form of filtering, but without the
92- /// ability to dynamically enable events based on the current span context, and
93- /// without filtering on field values. When these features are not required,
94- /// [`Targets`] provides a lighter-weight alternative to [`EnvFilter`].
102+ /// # Examples
103+ ///
104+ /// Parsing an `EnvFilter` from the [default environment
105+ /// variable](EnvFilter::from_default_env) (`RUST_LOG`):
106+ ///
107+ /// ```
108+ /// use tracing_subscriber::{EnvFilter, fmt, prelude::*};
109+ ///
110+ /// tracing_subscriber::registry()
111+ /// .with(fmt::layer())
112+ /// .with(EnvFilter::from_default_env())
113+ /// .init();
114+ /// ```
115+ ///
116+ /// Parsing an `EnvFilter` [from a user-provided environment
117+ /// variable](EnvFilter::from_env):
118+ ///
119+ /// ```
120+ /// use tracing_subscriber::{EnvFilter, fmt, prelude::*};
121+ ///
122+ /// tracing_subscriber::registry()
123+ /// .with(fmt::layer())
124+ /// .with(EnvFilter::from_env("MYAPP_LOG"))
125+ /// .init();
126+ /// ```
127+ ///
128+ /// Using `EnvFilter` as a [per-subscriber filter][psf] to filter only a single
129+ /// [subscriber](crate::subscribe::Subscribe):
130+ ///
131+ /// ```
132+ /// use tracing_subscriber::{EnvFilter, fmt, prelude::*};
133+ ///
134+ /// // Parse an `EnvFilter` configuration from the `RUST_LOG`
135+ /// // environment variable.
136+ /// let filter = EnvFilter::from_default_env();
137+ ///
138+ /// // Apply the filter to this suscriber *only*.
139+ /// let filtered_subscriber = fmt::subscriber().with_filter(filter);
140+ ///
141+ /// // Some other subscriber, whose output we don't want to filter.
142+ /// let unfiltered_subscriber = // ...
143+ /// # fmt::subscriber();
144+ ///
145+ /// tracing_subscriber::registry()
146+ /// .with(filtered_subscriber)
147+ /// .with(unfiltered_subscriber)
148+ /// .init();
149+ /// ```
95150///
96151/// [`Subscriber`]: Subscribe
97152/// [`env_logger`]: https://docs.rs/env_logger/0.7.1/env_logger/#enabling-logging
@@ -101,6 +156,10 @@ use tracing_core::{
101156/// [`level`]: tracing_core::Level
102157/// [`Metadata`]: tracing_core::Metadata
103158/// [`Targets`]: crate::filter::Targets
159+ /// [`Filter`]: #impl-Filter<C>
160+ /// [global]: crate::subscribe#global-filtering
161+ /// [psf]: crate::subscribe#per-subscriber-filtering
162+ /// [filtering]: crate::subscribe#filtering-with-subscribers
104163#[ cfg_attr( docsrs, doc( cfg( all( feature = "env-filter" , feature = "std" ) ) ) ) ]
105164#[ derive( Debug ) ]
106165pub struct EnvFilter {
@@ -109,10 +168,7 @@ pub struct EnvFilter {
109168 has_dynamics : bool ,
110169 by_id : RwLock < HashMap < span:: Id , directive:: SpanMatcher > > ,
111170 by_cs : RwLock < HashMap < callsite:: Identifier , directive:: CallsiteMatcher > > ,
112- }
113-
114- thread_local ! {
115- static SCOPE : RefCell <Vec <LevelFilter >> = RefCell :: new( Vec :: new( ) ) ;
171+ scope : ThreadLocal < RefCell < Vec < LevelFilter > > > ,
116172}
117173
118174type FieldMap < T > = HashMap < Field , T > ;
@@ -347,6 +403,10 @@ impl EnvFilter {
347403 has_dynamics,
348404 by_id : RwLock :: new ( HashMap :: new ( ) ) ,
349405 by_cs : RwLock :: new ( HashMap :: new ( ) ) ,
406+ // TODO(eliza): maybe worth allocating capacity for `num_cpus`
407+ // threads or something (assuming we're running in Tokio)? or
408+ // `num_cpus * 2` or something?
409+ scope : ThreadLocal :: new ( ) ,
350410 }
351411 }
352412
@@ -362,9 +422,7 @@ impl EnvFilter {
362422 Interest :: never ( )
363423 }
364424 }
365- }
366425
367- impl < C : Collect > Subscribe < C > for EnvFilter {
368426 fn register_callsite ( & self , metadata : & ' static Metadata < ' static > ) -> Interest {
369427 if self . has_dynamics && metadata. is_span ( ) {
370428 // If this metadata describes a span, first, check if there is a
@@ -385,20 +443,7 @@ impl<C: Collect> Subscribe<C> for EnvFilter {
385443 }
386444 }
387445
388- fn max_level_hint ( & self ) -> Option < LevelFilter > {
389- if self . dynamics . has_value_filters ( ) {
390- // If we perform any filtering on span field *values*, we will
391- // enable *all* spans, because their field values are not known
392- // until recording.
393- return Some ( LevelFilter :: TRACE ) ;
394- }
395- std:: cmp:: max (
396- self . statics . max_level . into ( ) ,
397- self . dynamics . max_level . into ( ) ,
398- )
399- }
400-
401- fn enabled ( & self , metadata : & Metadata < ' _ > , _: Context < ' _ , C > ) -> bool {
446+ fn enabled ( & self , metadata : & Metadata < ' _ > ) -> bool {
402447 let level = metadata. level ( ) ;
403448
404449 // is it possible for a dynamic filter directive to enable this event?
@@ -418,14 +463,15 @@ impl<C: Collect> Subscribe<C> for EnvFilter {
418463 }
419464 }
420465
421- let enabled_by_scope = SCOPE . with ( |scope| {
422- for filter in scope. borrow ( ) . iter ( ) {
466+ let enabled_by_scope = {
467+ let scope = self . scope . get_or_default ( ) . borrow ( ) ;
468+ for filter in & * scope {
423469 if filter >= level {
424470 return true ;
425471 }
426472 }
427473 false
428- } ) ;
474+ } ;
429475 if enabled_by_scope {
430476 return true ;
431477 }
@@ -441,36 +487,43 @@ impl<C: Collect> Subscribe<C> for EnvFilter {
441487 false
442488 }
443489
444- fn on_new_span ( & self , attrs : & span:: Attributes < ' _ > , id : & span:: Id , _: Context < ' _ , C > ) {
490+ fn max_level_hint ( & self ) -> Option < LevelFilter > {
491+ if self . dynamics . has_value_filters ( ) {
492+ // If we perform any filtering on span field *values*, we will
493+ // enable *all* spans, because their field values are not known
494+ // until recording.
495+ return Some ( LevelFilter :: TRACE ) ;
496+ }
497+ std:: cmp:: max (
498+ self . statics . max_level . into ( ) ,
499+ self . dynamics . max_level . into ( ) ,
500+ )
501+ }
502+
503+ fn on_new_span ( & self , attrs : & span:: Attributes < ' _ > , id : & span:: Id ) {
445504 let by_cs = try_lock ! ( self . by_cs. read( ) ) ;
446505 if let Some ( cs) = by_cs. get ( & attrs. metadata ( ) . callsite ( ) ) {
447506 let span = cs. to_span_match ( attrs) ;
448507 try_lock ! ( self . by_id. write( ) ) . insert ( id. clone ( ) , span) ;
449508 }
450509 }
451510
452- fn on_record ( & self , id : & span:: Id , values : & span:: Record < ' _ > , _: Context < ' _ , C > ) {
453- if let Some ( span) = try_lock ! ( self . by_id. read( ) ) . get ( id) {
454- span. record_update ( values) ;
455- }
456- }
457-
458- fn on_enter ( & self , id : & span:: Id , _: Context < ' _ , C > ) {
511+ fn on_enter ( & self , id : & span:: Id ) {
459512 // XXX: This is where _we_ could push IDs to the stack instead, and use
460513 // that to allow changing the filter while a span is already entered.
461514 // But that might be much less efficient...
462515 if let Some ( span) = try_lock ! ( self . by_id. read( ) ) . get ( id) {
463- SCOPE . with ( | scope| scope . borrow_mut ( ) . push ( span. level ( ) ) ) ;
516+ self . scope . get_or_default ( ) . borrow_mut ( ) . push ( span. level ( ) ) ;
464517 }
465518 }
466519
467- fn on_exit ( & self , id : & span:: Id , _ : Context < ' _ , C > ) {
520+ fn on_exit ( & self , id : & span:: Id ) {
468521 if self . cares_about_span ( id) {
469- SCOPE . with ( | scope| scope . borrow_mut ( ) . pop ( ) ) ;
522+ self . scope . get_or_default ( ) . borrow_mut ( ) . pop ( ) ;
470523 }
471524 }
472525
473- fn on_close ( & self , id : span:: Id , _ : Context < ' _ , C > ) {
526+ fn on_close ( & self , id : span:: Id ) {
474527 // If we don't need to acquire a write lock, avoid doing so.
475528 if !self . cares_about_span ( & id) {
476529 return ;
@@ -481,6 +534,90 @@ impl<C: Collect> Subscribe<C> for EnvFilter {
481534 }
482535}
483536
537+ impl < C : Collect > Subscribe < C > for EnvFilter {
538+ #[ inline]
539+ fn register_callsite ( & self , metadata : & ' static Metadata < ' static > ) -> Interest {
540+ EnvFilter :: register_callsite ( self , metadata)
541+ }
542+
543+ #[ inline]
544+ fn max_level_hint ( & self ) -> Option < LevelFilter > {
545+ EnvFilter :: max_level_hint ( self )
546+ }
547+
548+ #[ inline]
549+ fn enabled ( & self , metadata : & Metadata < ' _ > , _: Context < ' _ , C > ) -> bool {
550+ self . enabled ( metadata)
551+ }
552+
553+ #[ inline]
554+ fn on_new_span ( & self , attrs : & span:: Attributes < ' _ > , id : & span:: Id , _: Context < ' _ , C > ) {
555+ self . on_new_span ( attrs, id)
556+ }
557+
558+ fn on_record ( & self , id : & span:: Id , values : & span:: Record < ' _ > , _: Context < ' _ , C > ) {
559+ if let Some ( span) = try_lock ! ( self . by_id. read( ) ) . get ( id) {
560+ span. record_update ( values) ;
561+ }
562+ }
563+
564+ #[ inline]
565+ fn on_enter ( & self , id : & span:: Id , _: Context < ' _ , C > ) {
566+ self . on_enter ( id) ;
567+ }
568+
569+ #[ inline]
570+ fn on_exit ( & self , id : & span:: Id , _: Context < ' _ , C > ) {
571+ self . on_exit ( id) ;
572+ }
573+
574+ #[ inline]
575+ fn on_close ( & self , id : span:: Id , _: Context < ' _ , C > ) {
576+ self . on_close ( id) ;
577+ }
578+ }
579+
580+ feature ! {
581+ #![ all( feature = "registry" , feature = "std" ) ]
582+
583+ impl <C > subscribe:: Filter <C > for EnvFilter {
584+ #[ inline]
585+ fn enabled( & self , meta: & Metadata <' _>, _: & Context <' _, C >) -> bool {
586+ self . enabled( meta)
587+ }
588+
589+ #[ inline]
590+ fn callsite_enabled( & self , meta: & ' static Metadata <' static >) -> Interest {
591+ self . register_callsite( meta)
592+ }
593+
594+ #[ inline]
595+ fn max_level_hint( & self ) -> Option <LevelFilter > {
596+ EnvFilter :: max_level_hint( self )
597+ }
598+
599+ #[ inline]
600+ fn on_new_span( & self , attrs: & span:: Attributes <' _>, id: & span:: Id , _: Context <' _, C >) {
601+ self . on_new_span( attrs, id)
602+ }
603+
604+ #[ inline]
605+ fn on_enter( & self , id: & span:: Id , _: Context <' _, C >) {
606+ self . on_enter( id) ;
607+ }
608+
609+ #[ inline]
610+ fn on_exit( & self , id: & span:: Id , _: Context <' _, C >) {
611+ self . on_exit( id) ;
612+ }
613+
614+ #[ inline]
615+ fn on_close( & self , id: span:: Id , _: Context <' _, C >) {
616+ self . on_close( id) ;
617+ }
618+ }
619+ }
620+
484621impl FromStr for EnvFilter {
485622 type Err = directive:: ParseError ;
486623
0 commit comments