tracing_subscriber/filter/
directive.rs

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