🛈 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/env/
field.rs

1use matchers::Pattern;
2use std::{
3    cmp::Ordering,
4    error::Error,
5    fmt::{self, Write},
6    str::FromStr,
7    sync::{
8        atomic::{AtomicBool, Ordering::*},
9        Arc,
10    },
11};
12
13use super::{FieldMap, LevelFilter};
14use tracing_core::field::{Field, Visit};
15
16#[derive(Debug, Eq, PartialEq, Clone)]
17pub(crate) struct Match {
18    pub(crate) name: String, // TODO: allow match patterns for names?
19    pub(crate) value: Option<ValueMatch>,
20}
21
22#[derive(Debug, Eq, PartialEq)]
23pub(crate) struct CallsiteMatch {
24    pub(crate) fields: FieldMap<ValueMatch>,
25    pub(crate) level: LevelFilter,
26}
27
28#[derive(Debug)]
29pub(crate) struct SpanMatch {
30    fields: FieldMap<(ValueMatch, AtomicBool)>,
31    level: LevelFilter,
32    has_matched: AtomicBool,
33}
34
35pub(crate) struct MatchVisitor<'a> {
36    inner: &'a SpanMatch,
37}
38
39#[derive(Debug, Clone)]
40pub(crate) enum ValueMatch {
41    /// Matches a specific `bool` value.
42    Bool(bool),
43    /// Matches a specific `f64` value.
44    F64(f64),
45    /// Matches a specific `u64` value.
46    U64(u64),
47    /// Matches a specific `i64` value.
48    I64(i64),
49    /// Matches any `NaN` `f64` value.
50    NaN,
51    /// Matches any field whose `fmt::Debug` output is equal to a fixed string.
52    Debug(MatchDebug),
53    /// Matches any field whose `fmt::Debug` output matches a regular expression
54    /// pattern.
55    Pat(Box<MatchPattern>),
56}
57
58impl Eq for ValueMatch {}
59
60impl PartialEq for ValueMatch {
61    fn eq(&self, other: &Self) -> bool {
62        use ValueMatch::*;
63        match (self, other) {
64            (Bool(a), Bool(b)) => a.eq(b),
65            (F64(a), F64(b)) => {
66                debug_assert!(!a.is_nan());
67                debug_assert!(!b.is_nan());
68
69                a.eq(b)
70            }
71            (U64(a), U64(b)) => a.eq(b),
72            (I64(a), I64(b)) => a.eq(b),
73            (NaN, NaN) => true,
74            (Pat(a), Pat(b)) => a.eq(b),
75            _ => false,
76        }
77    }
78}
79
80impl Ord for ValueMatch {
81    fn cmp(&self, other: &Self) -> Ordering {
82        use ValueMatch::*;
83        match (self, other) {
84            (Bool(this), Bool(that)) => this.cmp(that),
85            (Bool(_), _) => Ordering::Less,
86
87            (F64(this), F64(that)) => this
88                .partial_cmp(that)
89                .expect("`ValueMatch::F64` may not contain `NaN` values"),
90            (F64(_), Bool(_)) => Ordering::Greater,
91            (F64(_), _) => Ordering::Less,
92
93            (NaN, NaN) => Ordering::Equal,
94            (NaN, Bool(_)) | (NaN, F64(_)) => Ordering::Greater,
95            (NaN, _) => Ordering::Less,
96
97            (U64(this), U64(that)) => this.cmp(that),
98            (U64(_), Bool(_)) | (U64(_), F64(_)) | (U64(_), NaN) => Ordering::Greater,
99            (U64(_), _) => Ordering::Less,
100
101            (I64(this), I64(that)) => this.cmp(that),
102            (I64(_), Bool(_)) | (I64(_), F64(_)) | (I64(_), NaN) | (I64(_), U64(_)) => {
103                Ordering::Greater
104            }
105            (I64(_), _) => Ordering::Less,
106
107            (Pat(this), Pat(that)) => this.cmp(that),
108            (Pat(_), _) => Ordering::Greater,
109
110            (Debug(this), Debug(that)) => this.cmp(that),
111            (Debug(_), _) => Ordering::Greater,
112        }
113    }
114}
115
116impl PartialOrd for ValueMatch {
117    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
118        Some(self.cmp(other))
119    }
120}
121
122/// Matches a field's `fmt::Debug` output against a regular expression pattern.
123///
124/// This is used for matching all non-literal field value filters when regular
125/// expressions are enabled.
126#[derive(Debug, Clone)]
127pub(crate) struct MatchPattern {
128    pub(crate) matcher: Pattern,
129    pattern: Arc<str>,
130}
131
132/// Matches a field's `fmt::Debug` output against a fixed string pattern.
133///
134/// This is used for matching all non-literal field value filters when regular
135/// expressions are disabled.
136#[derive(Debug, Clone)]
137pub(crate) struct MatchDebug {
138    pattern: Arc<str>,
139}
140
141/// Indicates that a field name specified in a filter directive was invalid.
142#[derive(Clone, Debug)]
143#[cfg_attr(docsrs, doc(cfg(feature = "env-filter")))]
144pub struct BadName {
145    name: String,
146}
147
148// === impl Match ===
149
150impl Match {
151    pub(crate) fn has_value(&self) -> bool {
152        self.value.is_some()
153    }
154
155    // TODO: reference count these strings?
156    pub(crate) fn name(&self) -> String {
157        self.name.clone()
158    }
159
160    pub(crate) fn parse(s: &str, regex: bool) -> Result<Self, Box<dyn Error + Send + Sync>> {
161        let mut parts = s.split('=');
162        let name = parts
163            .next()
164            .ok_or_else(|| BadName {
165                name: "".to_string(),
166            })?
167            // TODO: validate field name
168            .to_string();
169        let value = parts
170            .next()
171            .map(|part| match regex {
172                true => ValueMatch::parse_regex(part),
173                false => Ok(ValueMatch::parse_non_regex(part)),
174            })
175            .transpose()?;
176        Ok(Match { name, value })
177    }
178}
179
180impl fmt::Display for Match {
181    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
182        fmt::Display::fmt(&self.name, f)?;
183        if let Some(ref value) = self.value {
184            write!(f, "={}", value)?;
185        }
186        Ok(())
187    }
188}
189
190impl Ord for Match {
191    fn cmp(&self, other: &Self) -> Ordering {
192        // Ordering for `Match` directives is based first on _whether_ a value
193        // is matched or not. This is semantically meaningful --- we would
194        // prefer to check directives that match values first as they are more
195        // specific.
196        let has_value = match (self.value.as_ref(), other.value.as_ref()) {
197            (Some(_), None) => Ordering::Greater,
198            (None, Some(_)) => Ordering::Less,
199            _ => Ordering::Equal,
200        };
201        // If both directives match a value, we fall back to the field names in
202        // length + lexicographic ordering, and if these are equal as well, we
203        // compare the match directives.
204        //
205        // This ordering is no longer semantically meaningful but is necessary
206        // so that the directives can be stored in the `BTreeMap` in a defined
207        // order.
208        has_value
209            .then_with(|| self.name.cmp(&other.name))
210            .then_with(|| self.value.cmp(&other.value))
211    }
212}
213
214impl PartialOrd for Match {
215    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
216        Some(self.cmp(other))
217    }
218}
219
220// === impl ValueMatch ===
221
222fn value_match_f64(v: f64) -> ValueMatch {
223    if v.is_nan() {
224        ValueMatch::NaN
225    } else {
226        ValueMatch::F64(v)
227    }
228}
229
230impl ValueMatch {
231    /// Parse a `ValueMatch` that will match `fmt::Debug` fields using regular
232    /// expressions.
233    ///
234    /// This returns an error if the string didn't contain a valid `bool`,
235    /// `u64`, `i64`, or `f64` literal, and couldn't be parsed as a regular
236    /// expression.
237    #[allow(clippy::result_large_err)]
238    fn parse_regex(s: &str) -> Result<Self, matchers::BuildError> {
239        s.parse::<bool>()
240            .map(ValueMatch::Bool)
241            .or_else(|_| s.parse::<u64>().map(ValueMatch::U64))
242            .or_else(|_| s.parse::<i64>().map(ValueMatch::I64))
243            .or_else(|_| s.parse::<f64>().map(value_match_f64))
244            .or_else(|_| {
245                s.parse::<MatchPattern>()
246                    .map(|p| ValueMatch::Pat(Box::new(p)))
247            })
248    }
249
250    /// Parse a `ValueMatch` that will match `fmt::Debug` against a fixed
251    /// string.
252    ///
253    /// This does *not* return an error, because any string that isn't a valid
254    /// `bool`, `u64`, `i64`, or `f64` literal is treated as expected
255    /// `fmt::Debug` output.
256    fn parse_non_regex(s: &str) -> Self {
257        s.parse::<bool>()
258            .map(ValueMatch::Bool)
259            .or_else(|_| s.parse::<u64>().map(ValueMatch::U64))
260            .or_else(|_| s.parse::<i64>().map(ValueMatch::I64))
261            .or_else(|_| s.parse::<f64>().map(value_match_f64))
262            .unwrap_or_else(|_| ValueMatch::Debug(MatchDebug::new(s)))
263    }
264}
265
266impl fmt::Display for ValueMatch {
267    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
268        match self {
269            ValueMatch::Bool(ref inner) => fmt::Display::fmt(inner, f),
270            ValueMatch::F64(ref inner) => fmt::Display::fmt(inner, f),
271            ValueMatch::NaN => fmt::Display::fmt(&f64::NAN, f),
272            ValueMatch::I64(ref inner) => fmt::Display::fmt(inner, f),
273            ValueMatch::U64(ref inner) => fmt::Display::fmt(inner, f),
274            ValueMatch::Debug(ref inner) => fmt::Display::fmt(inner, f),
275            ValueMatch::Pat(ref inner) => fmt::Display::fmt(inner, f),
276        }
277    }
278}
279
280// === impl MatchPattern ===
281
282impl FromStr for MatchPattern {
283    type Err = matchers::BuildError;
284    fn from_str(s: &str) -> Result<Self, Self::Err> {
285        let matcher = Pattern::new_anchored(s)?;
286        Ok(Self {
287            matcher,
288            pattern: s.to_owned().into(),
289        })
290    }
291}
292
293impl fmt::Display for MatchPattern {
294    #[inline]
295    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
296        fmt::Display::fmt(&*self.pattern, f)
297    }
298}
299
300impl AsRef<str> for MatchPattern {
301    #[inline]
302    fn as_ref(&self) -> &str {
303        self.pattern.as_ref()
304    }
305}
306
307impl MatchPattern {
308    #[inline]
309    fn str_matches(&self, s: &impl AsRef<str>) -> bool {
310        self.matcher.matches(s)
311    }
312
313    #[inline]
314    fn debug_matches(&self, d: &impl fmt::Debug) -> bool {
315        self.matcher.debug_matches(d)
316    }
317
318    pub(super) fn into_debug_match(self) -> MatchDebug {
319        MatchDebug {
320            pattern: self.pattern,
321        }
322    }
323}
324
325impl PartialEq for MatchPattern {
326    #[inline]
327    fn eq(&self, other: &Self) -> bool {
328        self.pattern == other.pattern
329    }
330}
331
332impl Eq for MatchPattern {}
333
334impl PartialOrd for MatchPattern {
335    #[inline]
336    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
337        Some(self.pattern.cmp(&other.pattern))
338    }
339}
340
341impl Ord for MatchPattern {
342    #[inline]
343    fn cmp(&self, other: &Self) -> Ordering {
344        self.pattern.cmp(&other.pattern)
345    }
346}
347
348// === impl MatchDebug ===
349
350impl MatchDebug {
351    fn new(s: &str) -> Self {
352        Self {
353            pattern: s.to_owned().into(),
354        }
355    }
356
357    #[inline]
358    fn debug_matches(&self, d: &impl fmt::Debug) -> bool {
359        // Naively, we would probably match a value's `fmt::Debug` output by
360        // formatting it to a string, and then checking if the string is equal
361        // to the expected pattern. However, this would require allocating every
362        // time we want to match a field value against a `Debug` matcher, which
363        // can be avoided.
364        //
365        // Instead, we implement `fmt::Write` for a type that, rather than
366        // actually _writing_ the strings to something, matches them against the
367        // expected pattern, and returns an error if the pattern does not match.
368        struct Matcher<'a> {
369            pattern: &'a str,
370        }
371
372        impl fmt::Write for Matcher<'_> {
373            fn write_str(&mut self, s: &str) -> fmt::Result {
374                // If the string is longer than the remaining expected string,
375                // we know it won't match, so bail.
376                if s.len() > self.pattern.len() {
377                    return Err(fmt::Error);
378                }
379
380                // If the expected string begins with the string that was
381                // written, we are still potentially a match. Advance the
382                // position in the expected pattern to chop off the matched
383                // output, and continue.
384                if self.pattern.starts_with(s) {
385                    self.pattern = &self.pattern[s.len()..];
386                    return Ok(());
387                }
388
389                // Otherwise, the expected string doesn't include the string
390                // that was written at the current position, so the `fmt::Debug`
391                // output doesn't match! Return an error signalling that this
392                // doesn't match.
393                Err(fmt::Error)
394            }
395        }
396        let mut matcher = Matcher {
397            pattern: &self.pattern,
398        };
399
400        // Try to "write" the value's `fmt::Debug` output to a `Matcher`. This
401        // returns an error if the `fmt::Debug` implementation wrote any
402        // characters that did not match the expected pattern.
403        write!(matcher, "{:?}", d).is_ok()
404    }
405}
406
407impl fmt::Display for MatchDebug {
408    #[inline]
409    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
410        fmt::Display::fmt(&*self.pattern, f)
411    }
412}
413
414impl AsRef<str> for MatchDebug {
415    #[inline]
416    fn as_ref(&self) -> &str {
417        self.pattern.as_ref()
418    }
419}
420
421impl PartialEq for MatchDebug {
422    #[inline]
423    fn eq(&self, other: &Self) -> bool {
424        self.pattern == other.pattern
425    }
426}
427
428impl Eq for MatchDebug {}
429
430impl PartialOrd for MatchDebug {
431    #[inline]
432    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
433        Some(self.pattern.cmp(&other.pattern))
434    }
435}
436
437impl Ord for MatchDebug {
438    #[inline]
439    fn cmp(&self, other: &Self) -> Ordering {
440        self.pattern.cmp(&other.pattern)
441    }
442}
443
444// === impl BadName ===
445
446impl Error for BadName {}
447
448impl fmt::Display for BadName {
449    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
450        write!(f, "invalid field name `{}`", self.name)
451    }
452}
453
454impl CallsiteMatch {
455    pub(crate) fn to_span_match(&self) -> SpanMatch {
456        let fields = self
457            .fields
458            .iter()
459            .map(|(k, v)| (k.clone(), (v.clone(), AtomicBool::new(false))))
460            .collect();
461        SpanMatch {
462            fields,
463            level: self.level,
464            has_matched: AtomicBool::new(false),
465        }
466    }
467}
468
469impl SpanMatch {
470    pub(crate) fn visitor(&self) -> MatchVisitor<'_> {
471        MatchVisitor { inner: self }
472    }
473
474    #[inline]
475    pub(crate) fn is_matched(&self) -> bool {
476        if self.has_matched.load(Acquire) {
477            return true;
478        }
479        self.is_matched_slow()
480    }
481
482    #[inline(never)]
483    fn is_matched_slow(&self) -> bool {
484        let matched = self
485            .fields
486            .values()
487            .all(|(_, matched)| matched.load(Acquire));
488        if matched {
489            self.has_matched.store(true, Release);
490        }
491        matched
492    }
493
494    #[inline]
495    pub(crate) fn filter(&self) -> Option<LevelFilter> {
496        if self.is_matched() {
497            Some(self.level)
498        } else {
499            None
500        }
501    }
502}
503
504impl Visit for MatchVisitor<'_> {
505    fn record_f64(&mut self, field: &Field, value: f64) {
506        match self.inner.fields.get(field) {
507            Some((ValueMatch::NaN, ref matched)) if value.is_nan() => {
508                matched.store(true, Release);
509            }
510            Some((ValueMatch::F64(ref e), ref matched))
511                if (value - *e).abs() < f64::EPSILON =>
512            {
513                matched.store(true, Release);
514            }
515            _ => {}
516        }
517    }
518
519    fn record_i64(&mut self, field: &Field, value: i64) {
520        use std::convert::TryInto;
521
522        match self.inner.fields.get(field) {
523            Some((ValueMatch::I64(ref e), ref matched)) if value == *e => {
524                matched.store(true, Release);
525            }
526            Some((ValueMatch::U64(ref e), ref matched)) if Ok(value) == (*e).try_into() => {
527                matched.store(true, Release);
528            }
529            _ => {}
530        }
531    }
532
533    fn record_u64(&mut self, field: &Field, value: u64) {
534        match self.inner.fields.get(field) {
535            Some((ValueMatch::U64(ref e), ref matched)) if value == *e => {
536                matched.store(true, Release);
537            }
538            _ => {}
539        }
540    }
541
542    fn record_bool(&mut self, field: &Field, value: bool) {
543        match self.inner.fields.get(field) {
544            Some((ValueMatch::Bool(ref e), ref matched)) if value == *e => {
545                matched.store(true, Release);
546            }
547            _ => {}
548        }
549    }
550
551    fn record_str(&mut self, field: &Field, value: &str) {
552        match self.inner.fields.get(field) {
553            Some((ValueMatch::Pat(ref e), ref matched)) if e.str_matches(&value) => {
554                matched.store(true, Release);
555            }
556            Some((ValueMatch::Debug(ref e), ref matched)) if e.debug_matches(&value) => {
557                matched.store(true, Release)
558            }
559            _ => {}
560        }
561    }
562
563    fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
564        match self.inner.fields.get(field) {
565            Some((ValueMatch::Pat(ref e), ref matched)) if e.debug_matches(&value) => {
566                matched.store(true, Release);
567            }
568            Some((ValueMatch::Debug(ref e), ref matched)) if e.debug_matches(&value) => {
569                matched.store(true, Release)
570            }
571            _ => {}
572        }
573    }
574}
575
576#[cfg(test)]
577mod tests {
578    use super::*;
579    #[derive(Debug)]
580    #[allow(dead_code)]
581    struct MyStruct {
582        answer: usize,
583        question: &'static str,
584    }
585
586    #[test]
587    fn debug_struct_match() {
588        let my_struct = MyStruct {
589            answer: 42,
590            question: "life, the universe, and everything",
591        };
592
593        let pattern = "MyStruct { answer: 42, question: \"life, the universe, and everything\" }";
594
595        assert_eq!(
596            format!("{:?}", my_struct),
597            pattern,
598            "`MyStruct`'s `Debug` impl doesn't output the expected string"
599        );
600
601        let matcher = MatchDebug {
602            pattern: pattern.into(),
603        };
604        assert!(matcher.debug_matches(&my_struct))
605    }
606
607    #[test]
608    fn debug_struct_not_match() {
609        let my_struct = MyStruct {
610            answer: 42,
611            question: "what shall we have for lunch?",
612        };
613
614        let pattern = "MyStruct { answer: 42, question: \"life, the universe, and everything\" }";
615
616        assert_eq!(
617            format!("{:?}", my_struct),
618            "MyStruct { answer: 42, question: \"what shall we have for lunch?\" }",
619            "`MyStruct`'s `Debug` impl doesn't output the expected string"
620        );
621
622        let matcher = MatchDebug {
623            pattern: pattern.into(),
624        };
625        assert!(!matcher.debug_matches(&my_struct))
626    }
627}