1 use matchers::Pattern;
2 use 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 
13 use super::{FieldMap, LevelFilter};
14 use tracing_core::field::{Field, Visit};
15 
16 #[derive(Clone, Debug, Eq, PartialEq)]
17 pub(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)]
23 pub(crate) struct CallsiteMatch {
24     pub(crate) fields: FieldMap<ValueMatch>,
25     pub(crate) level: LevelFilter,
26 }
27 
28 #[derive(Debug)]
29 pub(crate) struct SpanMatch {
30     fields: FieldMap<(ValueMatch, AtomicBool)>,
31     level: LevelFilter,
32     has_matched: AtomicBool,
33 }
34 
35 pub(crate) struct MatchVisitor<'a> {
36     inner: &'a SpanMatch,
37 }
38 
39 #[derive(Debug, Clone)]
40 pub(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 
58 impl Eq for ValueMatch {}
59 
60 impl PartialEq for ValueMatch {
eq(&self, other: &Self) -> bool61     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 
80 impl Ord for ValueMatch {
cmp(&self, other: &Self) -> Ordering81     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 
116 impl PartialOrd for ValueMatch {
partial_cmp(&self, other: &Self) -> Option<Ordering>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)]
127 pub(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)]
137 pub(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")))]
144 pub struct BadName {
145     name: String,
146 }
147 
148 // === impl Match ===
149 
150 impl Match {
has_value(&self) -> bool151     pub(crate) fn has_value(&self) -> bool {
152         self.value.is_some()
153     }
154 
155     // TODO: reference count these strings?
name(&self) -> String156     pub(crate) fn name(&self) -> String {
157         self.name.clone()
158     }
159 
parse(s: &str, regex: bool) -> Result<Self, Box<dyn Error + Send + Sync>>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 
180 impl fmt::Display for Match {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result181     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 
190 impl Ord for Match {
cmp(&self, other: &Self) -> Ordering191     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 
214 impl PartialOrd for Match {
partial_cmp(&self, other: &Self) -> Option<Ordering>215     fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
216         Some(self.cmp(other))
217     }
218 }
219 
220 // === impl ValueMatch ===
221 
value_match_f64(v: f64) -> ValueMatch222 fn value_match_f64(v: f64) -> ValueMatch {
223     if v.is_nan() {
224         ValueMatch::NaN
225     } else {
226         ValueMatch::F64(v)
227     }
228 }
229 
230 impl 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.
parse_regex(s: &str) -> Result<Self, matchers::Error>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.
parse_non_regex(s: &str) -> Self255     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 
265 impl fmt::Display for ValueMatch {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result266     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(&std::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 
281 impl FromStr for MatchPattern {
282     type Err = matchers::Error;
from_str(s: &str) -> Result<Self, Self::Err>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 
292 impl fmt::Display for MatchPattern {
293     #[inline]
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result294     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
295         fmt::Display::fmt(&*self.pattern, f)
296     }
297 }
298 
299 impl AsRef<str> for MatchPattern {
300     #[inline]
as_ref(&self) -> &str301     fn as_ref(&self) -> &str {
302         self.pattern.as_ref()
303     }
304 }
305 
306 impl MatchPattern {
307     #[inline]
str_matches(&self, s: &impl AsRef<str>) -> bool308     fn str_matches(&self, s: &impl AsRef<str>) -> bool {
309         self.matcher.matches(s)
310     }
311 
312     #[inline]
debug_matches(&self, d: &impl fmt::Debug) -> bool313     fn debug_matches(&self, d: &impl fmt::Debug) -> bool {
314         self.matcher.debug_matches(d)
315     }
316 
into_debug_match(self) -> MatchDebug317     pub(super) fn into_debug_match(self) -> MatchDebug {
318         MatchDebug {
319             pattern: self.pattern,
320         }
321     }
322 }
323 
324 impl PartialEq for MatchPattern {
325     #[inline]
eq(&self, other: &Self) -> bool326     fn eq(&self, other: &Self) -> bool {
327         self.pattern == other.pattern
328     }
329 }
330 
331 impl Eq for MatchPattern {}
332 
333 impl PartialOrd for MatchPattern {
334     #[inline]
partial_cmp(&self, other: &Self) -> Option<Ordering>335     fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
336         Some(self.pattern.cmp(&other.pattern))
337     }
338 }
339 
340 impl Ord for MatchPattern {
341     #[inline]
cmp(&self, other: &Self) -> Ordering342     fn cmp(&self, other: &Self) -> Ordering {
343         self.pattern.cmp(&other.pattern)
344     }
345 }
346 
347 // === impl MatchDebug ===
348 
349 impl MatchDebug {
new(s: &str) -> Self350     fn new(s: &str) -> Self {
351         Self {
352             pattern: s.to_owned().into(),
353         }
354     }
355 
356     #[inline]
debug_matches(&self, d: &impl fmt::Debug) -> bool357     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 
406 impl fmt::Display for MatchDebug {
407     #[inline]
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result408     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
409         fmt::Display::fmt(&*self.pattern, f)
410     }
411 }
412 
413 impl AsRef<str> for MatchDebug {
414     #[inline]
as_ref(&self) -> &str415     fn as_ref(&self) -> &str {
416         self.pattern.as_ref()
417     }
418 }
419 
420 impl PartialEq for MatchDebug {
421     #[inline]
eq(&self, other: &Self) -> bool422     fn eq(&self, other: &Self) -> bool {
423         self.pattern == other.pattern
424     }
425 }
426 
427 impl Eq for MatchDebug {}
428 
429 impl PartialOrd for MatchDebug {
430     #[inline]
partial_cmp(&self, other: &Self) -> Option<Ordering>431     fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
432         Some(self.pattern.cmp(&other.pattern))
433     }
434 }
435 
436 impl Ord for MatchDebug {
437     #[inline]
cmp(&self, other: &Self) -> Ordering438     fn cmp(&self, other: &Self) -> Ordering {
439         self.pattern.cmp(&other.pattern)
440     }
441 }
442 
443 // === impl BadName ===
444 
445 impl Error for BadName {}
446 
447 impl fmt::Display for BadName {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result448     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
449         write!(f, "invalid field name `{}`", self.name)
450     }
451 }
452 
453 impl CallsiteMatch {
to_span_match(&self) -> SpanMatch454     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 
468 impl SpanMatch {
visitor(&self) -> MatchVisitor<'_>469     pub(crate) fn visitor(&self) -> MatchVisitor<'_> {
470         MatchVisitor { inner: self }
471     }
472 
473     #[inline]
is_matched(&self) -> bool474     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)]
is_matched_slow(&self) -> bool482     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]
filter(&self) -> Option<LevelFilter>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 
503 impl<'a> Visit for MatchVisitor<'a> {
record_f64(&mut self, field: &Field, value: f64)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() < std::f64::EPSILON =>
511             {
512                 matched.store(true, Release);
513             }
514             _ => {}
515         }
516     }
517 
record_i64(&mut self, field: &Field, value: i64)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 
record_u64(&mut self, field: &Field, value: u64)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 
record_bool(&mut self, field: &Field, value: bool)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 
record_str(&mut self, field: &Field, value: &str)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 
record_debug(&mut self, field: &Field, value: &dyn fmt::Debug)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)]
576 mod 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]
debug_struct_match()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]
debug_struct_not_match()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 }
627