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(Clone, Debug, Eq, PartialEq)]
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    fn parse_regex(s: &str) -> Result<Self, matchers::Error> {
238        s.parse::<bool>()
239            .map(ValueMatch::Bool)
240            .or_else(|_| s.parse::<u64>().map(ValueMatch::U64))
241            .or_else(|_| s.parse::<i64>().map(ValueMatch::I64))
242            .or_else(|_| s.parse::<f64>().map(value_match_f64))
243            .or_else(|_| {
244                s.parse::<MatchPattern>()
245                    .map(|p| ValueMatch::Pat(Box::new(p)))
246            })
247    }
248
249    /// Parse a `ValueMatch` that will match `fmt::Debug` against a fixed
250    /// string.
251    ///
252    /// This does *not* return an error, because any string that isn't a valid
253    /// `bool`, `u64`, `i64`, or `f64` literal is treated as expected
254    /// `fmt::Debug` output.
255    fn parse_non_regex(s: &str) -> Self {
256        s.parse::<bool>()
257            .map(ValueMatch::Bool)
258            .or_else(|_| s.parse::<u64>().map(ValueMatch::U64))
259            .or_else(|_| s.parse::<i64>().map(ValueMatch::I64))
260            .or_else(|_| s.parse::<f64>().map(value_match_f64))
261            .unwrap_or_else(|_| ValueMatch::Debug(MatchDebug::new(s)))
262    }
263}
264
265impl fmt::Display for ValueMatch {
266    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
267        match self {
268            ValueMatch::Bool(ref inner) => fmt::Display::fmt(inner, f),
269            ValueMatch::F64(ref inner) => fmt::Display::fmt(inner, f),
270            ValueMatch::NaN => fmt::Display::fmt(&f64::NAN, f),
271            ValueMatch::I64(ref inner) => fmt::Display::fmt(inner, f),
272            ValueMatch::U64(ref inner) => fmt::Display::fmt(inner, f),
273            ValueMatch::Debug(ref inner) => fmt::Display::fmt(inner, f),
274            ValueMatch::Pat(ref inner) => fmt::Display::fmt(inner, f),
275        }
276    }
277}
278
279// === impl MatchPattern ===
280
281impl FromStr for MatchPattern {
282    type Err = matchers::Error;
283    fn from_str(s: &str) -> Result<Self, Self::Err> {
284        let matcher = s.parse::<Pattern>()?;
285        Ok(Self {
286            matcher,
287            pattern: s.to_owned().into(),
288        })
289    }
290}
291
292impl fmt::Display for MatchPattern {
293    #[inline]
294    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
295        fmt::Display::fmt(&*self.pattern, f)
296    }
297}
298
299impl AsRef<str> for MatchPattern {
300    #[inline]
301    fn as_ref(&self) -> &str {
302        self.pattern.as_ref()
303    }
304}
305
306impl MatchPattern {
307    #[inline]
308    fn str_matches(&self, s: &impl AsRef<str>) -> bool {
309        self.matcher.matches(s)
310    }
311
312    #[inline]
313    fn debug_matches(&self, d: &impl fmt::Debug) -> bool {
314        self.matcher.debug_matches(d)
315    }
316
317    pub(super) fn into_debug_match(self) -> MatchDebug {
318        MatchDebug {
319            pattern: self.pattern,
320        }
321    }
322}
323
324impl PartialEq for MatchPattern {
325    #[inline]
326    fn eq(&self, other: &Self) -> bool {
327        self.pattern == other.pattern
328    }
329}
330
331impl Eq for MatchPattern {}
332
333impl PartialOrd for MatchPattern {
334    #[inline]
335    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
336        Some(self.pattern.cmp(&other.pattern))
337    }
338}
339
340impl Ord for MatchPattern {
341    #[inline]
342    fn cmp(&self, other: &Self) -> Ordering {
343        self.pattern.cmp(&other.pattern)
344    }
345}
346
347// === impl MatchDebug ===
348
349impl MatchDebug {
350    fn new(s: &str) -> Self {
351        Self {
352            pattern: s.to_owned().into(),
353        }
354    }
355
356    #[inline]
357    fn debug_matches(&self, d: &impl fmt::Debug) -> bool {
358        // Naively, we would probably match a value's `fmt::Debug` output by
359        // formatting it to a string, and then checking if the string is equal
360        // to the expected pattern. However, this would require allocating every
361        // time we want to match a field value against a `Debug` matcher, which
362        // can be avoided.
363        //
364        // Instead, we implement `fmt::Write` for a type that, rather than
365        // actually _writing_ the strings to something, matches them against the
366        // expected pattern, and returns an error if the pattern does not match.
367        struct Matcher<'a> {
368            pattern: &'a str,
369        }
370
371        impl fmt::Write for Matcher<'_> {
372            fn write_str(&mut self, s: &str) -> fmt::Result {
373                // If the string is longer than the remaining expected string,
374                // we know it won't match, so bail.
375                if s.len() > self.pattern.len() {
376                    return Err(fmt::Error);
377                }
378
379                // If the expected string begins with the string that was
380                // written, we are still potentially a match. Advance the
381                // position in the expected pattern to chop off the matched
382                // output, and continue.
383                if self.pattern.starts_with(s) {
384                    self.pattern = &self.pattern[s.len()..];
385                    return Ok(());
386                }
387
388                // Otherwise, the expected string doesn't include the string
389                // that was written at the current position, so the `fmt::Debug`
390                // output doesn't match! Return an error signalling that this
391                // doesn't match.
392                Err(fmt::Error)
393            }
394        }
395        let mut matcher = Matcher {
396            pattern: &self.pattern,
397        };
398
399        // Try to "write" the value's `fmt::Debug` output to a `Matcher`. This
400        // returns an error if the `fmt::Debug` implementation wrote any
401        // characters that did not match the expected pattern.
402        write!(matcher, "{:?}", d).is_ok()
403    }
404}
405
406impl fmt::Display for MatchDebug {
407    #[inline]
408    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
409        fmt::Display::fmt(&*self.pattern, f)
410    }
411}
412
413impl AsRef<str> for MatchDebug {
414    #[inline]
415    fn as_ref(&self) -> &str {
416        self.pattern.as_ref()
417    }
418}
419
420impl PartialEq for MatchDebug {
421    #[inline]
422    fn eq(&self, other: &Self) -> bool {
423        self.pattern == other.pattern
424    }
425}
426
427impl Eq for MatchDebug {}
428
429impl PartialOrd for MatchDebug {
430    #[inline]
431    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
432        Some(self.pattern.cmp(&other.pattern))
433    }
434}
435
436impl Ord for MatchDebug {
437    #[inline]
438    fn cmp(&self, other: &Self) -> Ordering {
439        self.pattern.cmp(&other.pattern)
440    }
441}
442
443// === impl BadName ===
444
445impl Error for BadName {}
446
447impl fmt::Display for BadName {
448    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
449        write!(f, "invalid field name `{}`", self.name)
450    }
451}
452
453impl CallsiteMatch {
454    pub(crate) fn to_span_match(&self) -> SpanMatch {
455        let fields = self
456            .fields
457            .iter()
458            .map(|(k, v)| (k.clone(), (v.clone(), AtomicBool::new(false))))
459            .collect();
460        SpanMatch {
461            fields,
462            level: self.level,
463            has_matched: AtomicBool::new(false),
464        }
465    }
466}
467
468impl SpanMatch {
469    pub(crate) fn visitor(&self) -> MatchVisitor<'_> {
470        MatchVisitor { inner: self }
471    }
472
473    #[inline]
474    pub(crate) fn is_matched(&self) -> bool {
475        if self.has_matched.load(Acquire) {
476            return true;
477        }
478        self.is_matched_slow()
479    }
480
481    #[inline(never)]
482    fn is_matched_slow(&self) -> bool {
483        let matched = self
484            .fields
485            .values()
486            .all(|(_, matched)| matched.load(Acquire));
487        if matched {
488            self.has_matched.store(true, Release);
489        }
490        matched
491    }
492
493    #[inline]
494    pub(crate) fn filter(&self) -> Option<LevelFilter> {
495        if self.is_matched() {
496            Some(self.level)
497        } else {
498            None
499        }
500    }
501}
502
503impl Visit for MatchVisitor<'_> {
504    fn record_f64(&mut self, field: &Field, value: f64) {
505        match self.inner.fields.get(field) {
506            Some((ValueMatch::NaN, ref matched)) if value.is_nan() => {
507                matched.store(true, Release);
508            }
509            Some((ValueMatch::F64(ref e), ref matched))
510                if (value - *e).abs() < f64::EPSILON =>
511            {
512                matched.store(true, Release);
513            }
514            _ => {}
515        }
516    }
517
518    fn record_i64(&mut self, field: &Field, value: i64) {
519        use std::convert::TryInto;
520
521        match self.inner.fields.get(field) {
522            Some((ValueMatch::I64(ref e), ref matched)) if value == *e => {
523                matched.store(true, Release);
524            }
525            Some((ValueMatch::U64(ref e), ref matched)) if Ok(value) == (*e).try_into() => {
526                matched.store(true, Release);
527            }
528            _ => {}
529        }
530    }
531
532    fn record_u64(&mut self, field: &Field, value: u64) {
533        match self.inner.fields.get(field) {
534            Some((ValueMatch::U64(ref e), ref matched)) if value == *e => {
535                matched.store(true, Release);
536            }
537            _ => {}
538        }
539    }
540
541    fn record_bool(&mut self, field: &Field, value: bool) {
542        match self.inner.fields.get(field) {
543            Some((ValueMatch::Bool(ref e), ref matched)) if value == *e => {
544                matched.store(true, Release);
545            }
546            _ => {}
547        }
548    }
549
550    fn record_str(&mut self, field: &Field, value: &str) {
551        match self.inner.fields.get(field) {
552            Some((ValueMatch::Pat(ref e), ref matched)) if e.str_matches(&value) => {
553                matched.store(true, Release);
554            }
555            Some((ValueMatch::Debug(ref e), ref matched)) if e.debug_matches(&value) => {
556                matched.store(true, Release)
557            }
558            _ => {}
559        }
560    }
561
562    fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
563        match self.inner.fields.get(field) {
564            Some((ValueMatch::Pat(ref e), ref matched)) if e.debug_matches(&value) => {
565                matched.store(true, Release);
566            }
567            Some((ValueMatch::Debug(ref e), ref matched)) if e.debug_matches(&value) => {
568                matched.store(true, Release)
569            }
570            _ => {}
571        }
572    }
573}
574
575#[cfg(test)]
576mod tests {
577    use super::*;
578    #[derive(Debug)]
579    #[allow(dead_code)]
580    struct MyStruct {
581        answer: usize,
582        question: &'static str,
583    }
584
585    #[test]
586    fn debug_struct_match() {
587        let my_struct = MyStruct {
588            answer: 42,
589            question: "life, the universe, and everything",
590        };
591
592        let pattern = "MyStruct { answer: 42, question: \"life, the universe, and everything\" }";
593
594        assert_eq!(
595            format!("{:?}", my_struct),
596            pattern,
597            "`MyStruct`'s `Debug` impl doesn't output the expected string"
598        );
599
600        let matcher = MatchDebug {
601            pattern: pattern.into(),
602        };
603        assert!(matcher.debug_matches(&my_struct))
604    }
605
606    #[test]
607    fn debug_struct_not_match() {
608        let my_struct = MyStruct {
609            answer: 42,
610            question: "what shall we have for lunch?",
611        };
612
613        let pattern = "MyStruct { answer: 42, question: \"life, the universe, and everything\" }";
614
615        assert_eq!(
616            format!("{:?}", my_struct),
617            "MyStruct { answer: 42, question: \"what shall we have for lunch?\" }",
618            "`MyStruct`'s `Debug` impl doesn't output the expected string"
619        );
620
621        let matcher = MatchDebug {
622            pattern: pattern.into(),
623        };
624        assert!(!matcher.debug_matches(&my_struct))
625    }
626}