🛈 Note: This is pre-release documentation for the upcoming tracing 0.2.0 ecosystem.

For the release documentation, please see docs.rs, instead.

tracing_subscriber/filter/
directive.rs

1use crate::filter::level::{self, LevelFilter};
2use alloc::{string::String, vec::Vec};
3use core::{cmp::Ordering, fmt, iter::FromIterator, slice, str::FromStr};
4use tracing_core::{Level, Metadata};
5/// Indicates that a string could not be parsed as a filtering directive.
6#[derive(Debug)]
7pub struct ParseError {
8    kind: ParseErrorKind,
9}
10
11/// A directive which will statically enable or disable a given callsite.
12///
13/// Unlike a dynamic directive, this can be cached by the callsite.
14#[derive(Debug, PartialEq, Eq, Clone)]
15pub(crate) struct StaticDirective {
16    pub(in crate::filter) target: Option<String>,
17    pub(in crate::filter) field_names: Vec<String>,
18    pub(in crate::filter) level: LevelFilter,
19}
20
21#[cfg(feature = "smallvec")]
22pub(crate) type FilterVec<T> = smallvec::SmallVec<[T; 8]>;
23#[cfg(not(feature = "smallvec"))]
24pub(crate) type FilterVec<T> = Vec<T>;
25
26#[derive(Debug, PartialEq, Clone)]
27pub(in crate::filter) struct DirectiveSet<T> {
28    directives: FilterVec<T>,
29    pub(in crate::filter) max_level: LevelFilter,
30}
31
32pub(in crate::filter) trait Match {
33    fn cares_about(&self, meta: &Metadata<'_>) -> bool;
34    fn level(&self) -> &LevelFilter;
35}
36
37#[derive(Debug)]
38enum ParseErrorKind {
39    #[cfg(feature = "std")]
40    Field(Box<dyn std::error::Error + Send + Sync>),
41    Level(level::ParseError),
42    Other(Option<&'static str>),
43}
44
45// === impl DirectiveSet ===
46
47impl<T> DirectiveSet<T> {
48    // this is only used by `env-filter`.
49    #[cfg(all(feature = "std", feature = "env-filter"))]
50    pub(crate) fn is_empty(&self) -> bool {
51        self.directives.is_empty()
52    }
53
54    pub(crate) fn iter(&self) -> slice::Iter<'_, T> {
55        self.directives.iter()
56    }
57}
58
59impl<T: Ord> Default for DirectiveSet<T> {
60    fn default() -> Self {
61        Self {
62            directives: FilterVec::new(),
63            max_level: LevelFilter::OFF,
64        }
65    }
66}
67
68impl<T: Match + Ord> DirectiveSet<T> {
69    pub(crate) fn directives(&self) -> impl Iterator<Item = &T> {
70        self.directives.iter()
71    }
72
73    pub(crate) fn directives_for<'a>(
74        &'a self,
75        metadata: &'a Metadata<'a>,
76    ) -> impl Iterator<Item = &'a T> + 'a {
77        self.directives().filter(move |d| d.cares_about(metadata))
78    }
79
80    pub(crate) fn add(&mut self, directive: T) {
81        // does this directive enable a more verbose level than the current
82        // max? if so, update the max level.
83        let level = *directive.level();
84        if level > self.max_level {
85            self.max_level = level;
86        }
87        // insert the directive into the vec of directives, ordered by
88        // specificity (length of target + number of field filters). this
89        // ensures that, when finding a directive to match a span or event, we
90        // search the directive set in most specific first order.
91        match self.directives.binary_search(&directive) {
92            Ok(i) => self.directives[i] = directive,
93            Err(i) => self.directives.insert(i, directive),
94        }
95    }
96
97    #[cfg(test)]
98    pub(in crate::filter) fn into_vec(self) -> FilterVec<T> {
99        self.directives
100    }
101}
102
103impl<T: Match + Ord> FromIterator<T> for DirectiveSet<T> {
104    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
105        let mut this = Self::default();
106        this.extend(iter);
107        this
108    }
109}
110
111impl<T: Match + Ord> Extend<T> for DirectiveSet<T> {
112    fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
113        for directive in iter.into_iter() {
114            self.add(directive);
115        }
116    }
117}
118
119impl<T> IntoIterator for DirectiveSet<T> {
120    type Item = T;
121
122    #[cfg(feature = "smallvec")]
123    type IntoIter = smallvec::IntoIter<[T; 8]>;
124    #[cfg(not(feature = "smallvec"))]
125    type IntoIter = alloc::vec::IntoIter<T>;
126
127    fn into_iter(self) -> Self::IntoIter {
128        self.directives.into_iter()
129    }
130}
131
132// === impl Statics ===
133
134impl DirectiveSet<StaticDirective> {
135    pub(crate) fn enabled(&self, meta: &Metadata<'_>) -> bool {
136        let level = meta.level();
137        match self.directives_for(meta).next() {
138            Some(d) => d.level >= *level,
139            None => false,
140        }
141    }
142
143    /// Same as `enabled` above, but skips `Directive`'s with fields.
144    pub(crate) fn target_enabled(&self, target: &str, level: &Level) -> bool {
145        match self.directives_for_target(target).next() {
146            Some(d) => d.level >= *level,
147            None => false,
148        }
149    }
150
151    pub(crate) fn directives_for_target<'a>(
152        &'a self,
153        target: &'a str,
154    ) -> impl Iterator<Item = &'a StaticDirective> + 'a {
155        self.directives()
156            .filter(move |d| d.cares_about_target(target))
157    }
158}
159
160// === impl StaticDirective ===
161
162impl StaticDirective {
163    pub(in crate::filter) fn new(
164        target: Option<String>,
165        field_names: Vec<String>,
166        level: LevelFilter,
167    ) -> Self {
168        Self {
169            target,
170            field_names,
171            level,
172        }
173    }
174
175    pub(in crate::filter) fn cares_about_target(&self, to_check: &str) -> bool {
176        // Does this directive have a target filter, and does it match the
177        // metadata's target?
178        if let Some(ref target) = self.target {
179            if !to_check.starts_with(&target[..]) {
180                return false;
181            }
182        }
183
184        if !self.field_names.is_empty() {
185            return false;
186        }
187
188        true
189    }
190}
191
192impl Ord for StaticDirective {
193    fn cmp(&self, other: &StaticDirective) -> Ordering {
194        // We attempt to order directives by how "specific" they are. This
195        // ensures that we try the most specific directives first when
196        // attempting to match a piece of metadata.
197
198        // First, we compare based on whether a target is specified, and the
199        // lengths of those targets if both have targets.
200        let ordering = self
201            .target
202            .as_ref()
203            .map(String::len)
204            .cmp(&other.target.as_ref().map(String::len))
205            // Then we compare how many field names are matched by each directive.
206            .then_with(|| self.field_names.len().cmp(&other.field_names.len()))
207            // Finally, we fall back to lexicographical ordering if the directives are
208            // equally specific. Although this is no longer semantically important,
209            // we need to define a total ordering to determine the directive's place
210            // in the BTreeMap.
211            .then_with(|| {
212                self.target
213                    .cmp(&other.target)
214                    .then_with(|| self.field_names[..].cmp(&other.field_names[..]))
215            })
216            .reverse();
217
218        #[cfg(debug_assertions)]
219        {
220            if ordering == Ordering::Equal {
221                debug_assert_eq!(
222                    self.target, other.target,
223                    "invariant violated: Ordering::Equal must imply a.target == b.target"
224                );
225                debug_assert_eq!(
226                    self.field_names, other.field_names,
227                    "invariant violated: Ordering::Equal must imply a.field_names == b.field_names"
228                );
229            }
230        }
231
232        ordering
233    }
234}
235
236impl PartialOrd for StaticDirective {
237    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
238        Some(self.cmp(other))
239    }
240}
241
242impl Match for StaticDirective {
243    fn cares_about(&self, meta: &Metadata<'_>) -> bool {
244        // Does this directive have a target filter, and does it match the
245        // metadata's target?
246        if let Some(ref target) = self.target {
247            if !meta.target().starts_with(&target[..]) {
248                return false;
249            }
250        }
251
252        if meta.is_event() && !self.field_names.is_empty() {
253            let fields = meta.fields();
254            for name in &self.field_names {
255                if fields.field(name).is_none() {
256                    return false;
257                }
258            }
259        }
260
261        true
262    }
263
264    fn level(&self) -> &LevelFilter {
265        &self.level
266    }
267}
268
269impl Default for StaticDirective {
270    fn default() -> Self {
271        StaticDirective {
272            target: None,
273            field_names: Vec::new(),
274            level: LevelFilter::ERROR,
275        }
276    }
277}
278
279impl fmt::Display for StaticDirective {
280    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
281        let mut wrote_any = false;
282        if let Some(ref target) = self.target {
283            fmt::Display::fmt(target, f)?;
284            wrote_any = true;
285        }
286
287        if !self.field_names.is_empty() {
288            f.write_str("[")?;
289
290            let mut fields = self.field_names.iter();
291            if let Some(field) = fields.next() {
292                write!(f, "{{{}", field)?;
293                for field in fields {
294                    write!(f, ",{}", field)?;
295                }
296                f.write_str("}")?;
297            }
298
299            f.write_str("]")?;
300            wrote_any = true;
301        }
302
303        if wrote_any {
304            f.write_str("=")?;
305        }
306
307        fmt::Display::fmt(&self.level, f)
308    }
309}
310
311impl FromStr for StaticDirective {
312    type Err = ParseError;
313
314    fn from_str(s: &str) -> Result<Self, Self::Err> {
315        // This method parses a filtering directive in one of the following
316        // forms:
317        //
318        // * `foo=trace` (TARGET=LEVEL)
319        // * `foo[{bar,baz}]=info` (TARGET[{FIELD,+}]=LEVEL)
320        // * `trace` (bare LEVEL)
321        // * `foo` (bare TARGET)
322        let mut split = s.split('=');
323        let part0 = split
324            .next()
325            .ok_or_else(|| ParseError::msg("string must not be empty"))?;
326
327        // Directive includes an `=`:
328        // * `foo=trace`
329        // * `foo[{bar}]=trace`
330        // * `foo[{bar,baz}]=trace`
331        if let Some(part1) = split.next() {
332            if split.next().is_some() {
333                return Err(ParseError::msg(
334                    "too many '=' in filter directive, expected 0 or 1",
335                ));
336            }
337
338            let mut split = part0.split("[{");
339            let target = split.next().map(String::from);
340            let mut field_names = Vec::new();
341            // Directive includes fields:
342            // * `foo[{bar}]=trace`
343            // * `foo[{bar,baz}]=trace`
344            if let Some(maybe_fields) = split.next() {
345                if split.next().is_some() {
346                    return Err(ParseError::msg(
347                        "too many '[{' in filter directive, expected 0 or 1",
348                    ));
349                }
350
351                let fields = maybe_fields
352                    .strip_suffix("}]")
353                    .ok_or_else(|| ParseError::msg("expected fields list to end with '}]'"))?;
354                field_names.extend(fields.split(',').filter_map(|s| {
355                    if s.is_empty() {
356                        None
357                    } else {
358                        Some(String::from(s))
359                    }
360                }));
361            };
362            let level = part1.parse()?;
363            return Ok(Self {
364                level,
365                field_names,
366                target,
367            });
368        }
369
370        // Okay, the part after the `=` was empty, the directive is either a
371        // bare level or a bare target.
372        // * `foo`
373        // * `info`
374        Ok(match part0.parse::<LevelFilter>() {
375            Ok(level) => Self {
376                level,
377                target: None,
378                field_names: Vec::new(),
379            },
380            Err(_) => Self {
381                target: Some(String::from(part0)),
382                level: LevelFilter::TRACE,
383                field_names: Vec::new(),
384            },
385        })
386    }
387}
388
389// === impl ParseError ===
390
391impl ParseError {
392    #[cfg(all(feature = "std", feature = "env-filter"))]
393    pub(crate) fn new() -> Self {
394        ParseError {
395            kind: ParseErrorKind::Other(None),
396        }
397    }
398
399    pub(crate) fn msg(s: &'static str) -> Self {
400        ParseError {
401            kind: ParseErrorKind::Other(Some(s)),
402        }
403    }
404}
405
406impl fmt::Display for ParseError {
407    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
408        match self.kind {
409            ParseErrorKind::Other(None) => f.pad("invalid filter directive"),
410            ParseErrorKind::Other(Some(msg)) => write!(f, "invalid filter directive: {}", msg),
411            ParseErrorKind::Level(ref l) => l.fmt(f),
412            #[cfg(feature = "std")]
413            ParseErrorKind::Field(ref e) => write!(f, "invalid field filter: {}", e),
414        }
415    }
416}
417
418#[cfg(feature = "std")]
419impl std::error::Error for ParseError {
420    fn description(&self) -> &str {
421        "invalid filter directive"
422    }
423
424    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
425        match self.kind {
426            ParseErrorKind::Other(_) => None,
427            ParseErrorKind::Level(ref l) => Some(l),
428            ParseErrorKind::Field(ref n) => Some(n.as_ref()),
429        }
430    }
431}
432
433#[cfg(feature = "std")]
434impl From<Box<dyn std::error::Error + Send + Sync>> for ParseError {
435    fn from(e: Box<dyn std::error::Error + Send + Sync>) -> Self {
436        Self {
437            kind: ParseErrorKind::Field(e),
438        }
439    }
440}
441
442impl From<level::ParseError> for ParseError {
443    fn from(l: level::ParseError) -> Self {
444        Self {
445            kind: ParseErrorKind::Level(l),
446        }
447    }
448}