tracing_subscriber/filter/
directive.rs

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