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), Bool(b: &bool)) => a.eq(b),
65 (F64(a: &f64), F64(b: &f64)) => {
66 debug_assert!(!a.is_nan());
67 debug_assert!(!b.is_nan());
68
69 a.eq(b)
70 }
71 (U64(a: &u64), U64(b: &u64)) => a.eq(b),
72 (I64(a: &i64), I64(b: &i64)) => a.eq(b),
73 (NaN, NaN) => true,
74 (Pat(a: &Box), Pat(b: &Box)) => 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: &ValueMatch) = 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: Ordering = 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_valueOrdering
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: &bool) => fmt::Display::fmt(self:inner, f),
269 ValueMatch::F64(ref inner: &f64) => fmt::Display::fmt(self:inner, f),
270 ValueMatch::NaN => fmt::Display::fmt(&std::f64::NAN, f),
271 ValueMatch::I64(ref inner: &i64) => fmt::Display::fmt(self:inner, f),
272 ValueMatch::U64(ref inner: &u64) => fmt::Display::fmt(self:inner, f),
273 ValueMatch::Debug(ref inner: &MatchDebug) => fmt::Display::fmt(self:inner, f),
274 ValueMatch::Pat(ref inner: &Box) => fmt::Display::fmt(self: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: Pattern = 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: HashMap = self
456 .fields
457 .iter()
458 .map(|(k: &Field, v: &ValueMatch)| (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<'a> Visit for MatchVisitor<'a> {
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
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}
627