Skip to content

Commit ed7b60a

Browse files
subscriber: impl Filter for EnvFilter (#1983)
## Motivation Filtering by span and field requires using `EnvFilter` rather than `Targets`. Per-layer filtering requires the `Filter` trait, which `EnvFilter` does not implement. ## Solution Implement the `Filter` trait for `EnvFilter`. PR #1973 adds additiional methods to the `Filter` trait, which are necessary for `EnvFilter` to implement dynamic span filtering. Now that those methods are added, we can provide a `Filter` impl for `EnvFilter`. In addition, we changed the globally-scoped `thread_local!` macro to use a `ThreadLocal` struct as a field, so that multiple `EnvFilter`s used as per-layer filters don't share a single span scope. Fixes #1868 Follow-up on #1973 Co-authored-by: Eliza Weisman <[email protected]>
1 parent df9666b commit ed7b60a

File tree

6 files changed

+1029
-229
lines changed

6 files changed

+1029
-229
lines changed

tracing-subscriber/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ rust-version = "1.49.0"
2727
default = ["smallvec", "fmt", "ansi", "tracing-log", "std"]
2828
alloc = []
2929
std = ["alloc", "tracing-core/std"]
30-
env-filter = ["matchers", "regex", "lazy_static", "tracing", "std"]
30+
env-filter = ["matchers", "regex", "lazy_static", "tracing", "std", "thread_local"]
3131
fmt = ["registry", "std"]
3232
ansi = ["fmt", "ansi_term"]
3333
registry = ["sharded-slab", "thread_local", "std"]

tracing-subscriber/src/filter/env/mod.rs

Lines changed: 178 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,12 @@ mod field;
1010

1111
use crate::{
1212
filter::LevelFilter,
13-
layer::{Context, Layer},
13+
layer::{self, Context, Layer},
1414
sync::RwLock,
1515
};
1616
use directive::ParseError;
1717
use std::{cell::RefCell, collections::HashMap, env, error::Error, fmt, str::FromStr};
18+
use thread_local::ThreadLocal;
1819
use tracing_core::{
1920
callsite,
2021
field::Field,
@@ -26,6 +27,16 @@ use tracing_core::{
2627
/// A [`Layer`] which filters spans and events based on a set of filter
2728
/// directives.
2829
///
30+
/// `EnvFilter` implements both the [`Layer`](#impl-Layer<S>) and [`Filter`] traits, so it may
31+
/// be used for both [global filtering][global] and [per-layer filtering][plf],
32+
/// respectively. See [the documentation on filtering with `Layer`s][filtering]
33+
/// for details.
34+
///
35+
/// The [`Targets`] type implements a similar form of filtering, but without the
36+
/// ability to dynamically enable events based on the current span context, and
37+
/// without filtering on field values. When these features are not required,
38+
/// [`Targets`] provides a lighter-weight alternative to [`EnvFilter`].
39+
///
2940
/// # Directives
3041
///
3142
/// A filter consists of one or more comma-separated directives which match on [`Span`]s and [`Event`]s.
@@ -72,7 +83,7 @@ use tracing_core::{
7283
/// - A dash in a target will only appear when being specified explicitly:
7384
/// `tracing::info!(target: "target-name", ...);`
7485
///
75-
/// ## Examples
86+
/// ## Example Syntax
7687
///
7788
/// - `tokio::net=info` will enable all spans or events that:
7889
/// - have the `tokio::net` target,
@@ -89,10 +100,54 @@ use tracing_core::{
89100
/// - which has a field named `name` with value `bob`,
90101
/// - at _any_ level.
91102
///
92-
/// The [`Targets`] type implements a similar form of filtering, but without the
93-
/// ability to dynamically enable events based on the current span context, and
94-
/// without filtering on field values. When these features are not required,
95-
/// [`Targets`] provides a lighter-weight alternative to [`EnvFilter`].
103+
/// # Examples
104+
///
105+
/// Parsing an `EnvFilter` from the [default environment
106+
/// variable](EnvFilter::from_default_env) (`RUST_LOG`):
107+
///
108+
/// ```
109+
/// use tracing_subscriber::{EnvFilter, fmt, prelude::*};
110+
///
111+
/// tracing_subscriber::registry()
112+
/// .with(fmt::layer())
113+
/// .with(EnvFilter::from_default_env())
114+
/// .init();
115+
/// ```
116+
///
117+
/// Parsing an `EnvFilter` [from a user-provided environment
118+
/// variable](EnvFilter::from_env):
119+
///
120+
/// ```
121+
/// use tracing_subscriber::{EnvFilter, fmt, prelude::*};
122+
///
123+
/// tracing_subscriber::registry()
124+
/// .with(fmt::layer())
125+
/// .with(EnvFilter::from_env("MYAPP_LOG"))
126+
/// .init();
127+
/// ```
128+
///
129+
/// Using `EnvFilter` as a [per-layer filter][plf] to filter only a single
130+
/// [`Layer`]:
131+
///
132+
/// ```
133+
/// use tracing_subscriber::{EnvFilter, fmt, prelude::*};
134+
///
135+
/// // Parse an `EnvFilter` configuration from the `RUST_LOG`
136+
/// // environment variable.
137+
/// let filter = EnvFilter::from_default_env();
138+
///
139+
/// // Apply the filter to this layer *only*.
140+
/// let filtered_layer = fmt::layer().with_filter(filter);
141+
///
142+
/// // Some other layer, whose output we don't want to filter.
143+
/// let unfiltered_layer = // ...
144+
/// # fmt::layer();
145+
///
146+
/// tracing_subscriber::registry()
147+
/// .with(filtered_layer)
148+
/// .with(unfiltered_layer)
149+
/// .init();
150+
/// ```
96151
///
97152
/// [`Span`]: tracing_core::span
98153
/// [fields]: tracing_core::Field
@@ -101,6 +156,10 @@ use tracing_core::{
101156
/// [`Metadata`]: tracing_core::Metadata
102157
/// [`Targets`]: crate::filter::Targets
103158
/// [`env_logger`]: https://crates.io/crates/env_logger
159+
/// [`Filter`]: #impl-Filter<S>
160+
/// [global]: crate::layer#global-filtering
161+
/// [plf]: crate::layer#per-layer-filtering
162+
/// [filtering]: crate::layer#filtering-with-layers
104163
#[cfg_attr(docsrs, doc(cfg(all(feature = "env-filter", feature = "std"))))]
105164
#[derive(Debug)]
106165
pub 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

118174
type FieldMap<T> = HashMap<Field, T>;
@@ -350,6 +406,10 @@ impl EnvFilter {
350406
has_dynamics,
351407
by_id: RwLock::new(HashMap::new()),
352408
by_cs: RwLock::new(HashMap::new()),
409+
// TODO(eliza): maybe worth allocating capacity for `num_cpus`
410+
// threads or something (assuming we're running in Tokio)? or
411+
// `num_cpus * 2` or something?
412+
scope: ThreadLocal::new(),
353413
}
354414
}
355415

@@ -365,9 +425,7 @@ impl EnvFilter {
365425
Interest::never()
366426
}
367427
}
368-
}
369428

370-
impl<S: Subscriber> Layer<S> for EnvFilter {
371429
fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest {
372430
if self.has_dynamics && metadata.is_span() {
373431
// If this metadata describes a span, first, check if there is a
@@ -388,20 +446,7 @@ impl<S: Subscriber> Layer<S> for EnvFilter {
388446
}
389447
}
390448

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.into(),
400-
self.dynamics.max_level.into(),
401-
)
402-
}
403-
404-
fn enabled(&self, metadata: &Metadata<'_>, _: Context<'_, S>) -> bool {
449+
fn enabled(&self, metadata: &Metadata<'_>) -> bool {
405450
let level = metadata.level();
406451

407452
// is it possible for a dynamic filter directive to enable this event?
@@ -421,14 +466,15 @@ impl<S: Subscriber> Layer<S> for EnvFilter {
421466
}
422467
}
423468

424-
let enabled_by_scope = SCOPE.with(|scope| {
425-
for filter in scope.borrow().iter() {
469+
let enabled_by_scope = {
470+
let scope = self.scope.get_or_default().borrow();
471+
for filter in &*scope {
426472
if filter >= level {
427473
return true;
428474
}
429475
}
430476
false
431-
});
477+
};
432478
if enabled_by_scope {
433479
return true;
434480
}
@@ -444,36 +490,43 @@ impl<S: Subscriber> Layer<S> for EnvFilter {
444490
false
445491
}
446492

447-
fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, _: Context<'_, S>) {
493+
fn max_level_hint(&self) -> Option<LevelFilter> {
494+
if self.dynamics.has_value_filters() {
495+
// If we perform any filtering on span field *values*, we will
496+
// enable *all* spans, because their field values are not known
497+
// until recording.
498+
return Some(LevelFilter::TRACE);
499+
}
500+
std::cmp::max(
501+
self.statics.max_level.into(),
502+
self.dynamics.max_level.into(),
503+
)
504+
}
505+
506+
fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id) {
448507
let by_cs = try_lock!(self.by_cs.read());
449508
if let Some(cs) = by_cs.get(&attrs.metadata().callsite()) {
450509
let span = cs.to_span_match(attrs);
451510
try_lock!(self.by_id.write()).insert(id.clone(), span);
452511
}
453512
}
454513

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-
461-
fn on_enter(&self, id: &span::Id, _: Context<'_, S>) {
514+
fn on_enter(&self, id: &span::Id) {
462515
// XXX: This is where _we_ could push IDs to the stack instead, and use
463516
// that to allow changing the filter while a span is already entered.
464517
// But that might be much less efficient...
465518
if let Some(span) = try_lock!(self.by_id.read()).get(id) {
466-
SCOPE.with(|scope| scope.borrow_mut().push(span.level()));
519+
self.scope.get_or_default().borrow_mut().push(span.level());
467520
}
468521
}
469522

470-
fn on_exit(&self, id: &span::Id, _: Context<'_, S>) {
523+
fn on_exit(&self, id: &span::Id) {
471524
if self.cares_about_span(id) {
472-
SCOPE.with(|scope| scope.borrow_mut().pop());
525+
self.scope.get_or_default().borrow_mut().pop();
473526
}
474527
}
475528

476-
fn on_close(&self, id: span::Id, _: Context<'_, S>) {
529+
fn on_close(&self, id: span::Id) {
477530
// If we don't need to acquire a write lock, avoid doing so.
478531
if !self.cares_about_span(&id) {
479532
return;
@@ -484,6 +537,90 @@ impl<S: Subscriber> Layer<S> for EnvFilter {
484537
}
485538
}
486539

540+
impl<S: Subscriber> Layer<S> for EnvFilter {
541+
#[inline]
542+
fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest {
543+
EnvFilter::register_callsite(self, metadata)
544+
}
545+
546+
#[inline]
547+
fn max_level_hint(&self) -> Option<LevelFilter> {
548+
EnvFilter::max_level_hint(self)
549+
}
550+
551+
#[inline]
552+
fn enabled(&self, metadata: &Metadata<'_>, _: Context<'_, S>) -> bool {
553+
self.enabled(metadata)
554+
}
555+
556+
#[inline]
557+
fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, _: Context<'_, S>) {
558+
self.on_new_span(attrs, id)
559+
}
560+
561+
fn on_record(&self, id: &span::Id, values: &span::Record<'_>, _: Context<'_, S>) {
562+
if let Some(span) = try_lock!(self.by_id.read()).get(id) {
563+
span.record_update(values);
564+
}
565+
}
566+
567+
#[inline]
568+
fn on_enter(&self, id: &span::Id, _: Context<'_, S>) {
569+
self.on_enter(id);
570+
}
571+
572+
#[inline]
573+
fn on_exit(&self, id: &span::Id, _: Context<'_, S>) {
574+
self.on_exit(id);
575+
}
576+
577+
#[inline]
578+
fn on_close(&self, id: span::Id, _: Context<'_, S>) {
579+
self.on_close(id);
580+
}
581+
}
582+
583+
feature! {
584+
#![all(feature = "registry", feature = "std")]
585+
586+
impl<S> layer::Filter<S> for EnvFilter {
587+
#[inline]
588+
fn enabled(&self, meta: &Metadata<'_>, _: &Context<'_, S>) -> bool {
589+
self.enabled(meta)
590+
}
591+
592+
#[inline]
593+
fn callsite_enabled(&self, meta: &'static Metadata<'static>) -> Interest {
594+
self.register_callsite(meta)
595+
}
596+
597+
#[inline]
598+
fn max_level_hint(&self) -> Option<LevelFilter> {
599+
EnvFilter::max_level_hint(self)
600+
}
601+
602+
#[inline]
603+
fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, _: Context<'_, S>) {
604+
self.on_new_span(attrs, id)
605+
}
606+
607+
#[inline]
608+
fn on_enter(&self, id: &span::Id, _: Context<'_, S>) {
609+
self.on_enter(id);
610+
}
611+
612+
#[inline]
613+
fn on_exit(&self, id: &span::Id, _: Context<'_, S>) {
614+
self.on_exit(id);
615+
}
616+
617+
#[inline]
618+
fn on_close(&self, id: span::Id, _: Context<'_, S>) {
619+
self.on_close(id);
620+
}
621+
}
622+
}
623+
487624
impl FromStr for EnvFilter {
488625
type Err = directive::ParseError;
489626

0 commit comments

Comments
 (0)