From 698f8c2f01ea549d77d7dc3338a12e04c11057b9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:02:58 +0200 Subject: Adding upstream version 1.64.0+dfsg1. Signed-off-by: Daniel Baumann --- vendor/tracing-subscriber/src/filter/env/field.rs | 626 ++++++++++++++++++++++ 1 file changed, 626 insertions(+) create mode 100644 vendor/tracing-subscriber/src/filter/env/field.rs (limited to 'vendor/tracing-subscriber/src/filter/env/field.rs') diff --git a/vendor/tracing-subscriber/src/filter/env/field.rs b/vendor/tracing-subscriber/src/filter/env/field.rs new file mode 100644 index 000000000..1394fd04a --- /dev/null +++ b/vendor/tracing-subscriber/src/filter/env/field.rs @@ -0,0 +1,626 @@ +use matchers::Pattern; +use std::{ + cmp::Ordering, + error::Error, + fmt::{self, Write}, + str::FromStr, + sync::{ + atomic::{AtomicBool, Ordering::*}, + Arc, + }, +}; + +use super::{FieldMap, LevelFilter}; +use tracing_core::field::{Field, Visit}; + +#[derive(Clone, Debug, Eq, PartialEq)] +pub(crate) struct Match { + pub(crate) name: String, // TODO: allow match patterns for names? + pub(crate) value: Option, +} + +#[derive(Debug, Eq, PartialEq)] +pub(crate) struct CallsiteMatch { + pub(crate) fields: FieldMap, + pub(crate) level: LevelFilter, +} + +#[derive(Debug)] +pub(crate) struct SpanMatch { + fields: FieldMap<(ValueMatch, AtomicBool)>, + level: LevelFilter, + has_matched: AtomicBool, +} + +pub(crate) struct MatchVisitor<'a> { + inner: &'a SpanMatch, +} + +#[derive(Debug, Clone)] +pub(crate) enum ValueMatch { + /// Matches a specific `bool` value. + Bool(bool), + /// Matches a specific `f64` value. + F64(f64), + /// Matches a specific `u64` value. + U64(u64), + /// Matches a specific `i64` value. + I64(i64), + /// Matches any `NaN` `f64` value. + NaN, + /// Matches any field whose `fmt::Debug` output is equal to a fixed string. + Debug(MatchDebug), + /// Matches any field whose `fmt::Debug` output matches a regular expression + /// pattern. + Pat(Box), +} + +impl Eq for ValueMatch {} + +impl PartialEq for ValueMatch { + fn eq(&self, other: &Self) -> bool { + use ValueMatch::*; + match (self, other) { + (Bool(a), Bool(b)) => a.eq(b), + (F64(a), F64(b)) => { + debug_assert!(!a.is_nan()); + debug_assert!(!b.is_nan()); + + a.eq(b) + } + (U64(a), U64(b)) => a.eq(b), + (I64(a), I64(b)) => a.eq(b), + (NaN, NaN) => true, + (Pat(a), Pat(b)) => a.eq(b), + _ => false, + } + } +} + +impl Ord for ValueMatch { + fn cmp(&self, other: &Self) -> Ordering { + use ValueMatch::*; + match (self, other) { + (Bool(this), Bool(that)) => this.cmp(that), + (Bool(_), _) => Ordering::Less, + + (F64(this), F64(that)) => this + .partial_cmp(that) + .expect("`ValueMatch::F64` may not contain `NaN` values"), + (F64(_), Bool(_)) => Ordering::Greater, + (F64(_), _) => Ordering::Less, + + (NaN, NaN) => Ordering::Equal, + (NaN, Bool(_)) | (NaN, F64(_)) => Ordering::Greater, + (NaN, _) => Ordering::Less, + + (U64(this), U64(that)) => this.cmp(that), + (U64(_), Bool(_)) | (U64(_), F64(_)) | (U64(_), NaN) => Ordering::Greater, + (U64(_), _) => Ordering::Less, + + (I64(this), I64(that)) => this.cmp(that), + (I64(_), Bool(_)) | (I64(_), F64(_)) | (I64(_), NaN) | (I64(_), U64(_)) => { + Ordering::Greater + } + (I64(_), _) => Ordering::Less, + + (Pat(this), Pat(that)) => this.cmp(that), + (Pat(_), _) => Ordering::Greater, + + (Debug(this), Debug(that)) => this.cmp(that), + (Debug(_), _) => Ordering::Greater, + } + } +} + +impl PartialOrd for ValueMatch { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +/// Matches a field's `fmt::Debug` output against a regular expression pattern. +/// +/// This is used for matching all non-literal field value filters when regular +/// expressions are enabled. +#[derive(Debug, Clone)] +pub(crate) struct MatchPattern { + pub(crate) matcher: Pattern, + pattern: Arc, +} + +/// Matches a field's `fmt::Debug` output against a fixed string pattern. +/// +/// This is used for matching all non-literal field value filters when regular +/// expressions are disabled. +#[derive(Debug, Clone)] +pub(crate) struct MatchDebug { + pattern: Arc, +} + +/// Indicates that a field name specified in a filter directive was invalid. +#[derive(Clone, Debug)] +#[cfg_attr(docsrs, doc(cfg(feature = "env-filter")))] +pub struct BadName { + name: String, +} + +// === impl Match === + +impl Match { + pub(crate) fn has_value(&self) -> bool { + self.value.is_some() + } + + // TODO: reference count these strings? + pub(crate) fn name(&self) -> String { + self.name.clone() + } + + pub(crate) fn parse(s: &str, regex: bool) -> Result> { + let mut parts = s.split('='); + let name = parts + .next() + .ok_or_else(|| BadName { + name: "".to_string(), + })? + // TODO: validate field name + .to_string(); + let value = parts + .next() + .map(|part| match regex { + true => ValueMatch::parse_regex(part), + false => Ok(ValueMatch::parse_non_regex(part)), + }) + .transpose()?; + Ok(Match { name, value }) + } +} + +impl fmt::Display for Match { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.name, f)?; + if let Some(ref value) = self.value { + write!(f, "={}", value)?; + } + Ok(()) + } +} + +impl Ord for Match { + fn cmp(&self, other: &Self) -> Ordering { + // Ordering for `Match` directives is based first on _whether_ a value + // is matched or not. This is semantically meaningful --- we would + // prefer to check directives that match values first as they are more + // specific. + let has_value = match (self.value.as_ref(), other.value.as_ref()) { + (Some(_), None) => Ordering::Greater, + (None, Some(_)) => Ordering::Less, + _ => Ordering::Equal, + }; + // If both directives match a value, we fall back to the field names in + // length + lexicographic ordering, and if these are equal as well, we + // compare the match directives. + // + // This ordering is no longer semantically meaningful but is necessary + // so that the directives can be stored in the `BTreeMap` in a defined + // order. + has_value + .then_with(|| self.name.cmp(&other.name)) + .then_with(|| self.value.cmp(&other.value)) + } +} + +impl PartialOrd for Match { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +// === impl ValueMatch === + +fn value_match_f64(v: f64) -> ValueMatch { + if v.is_nan() { + ValueMatch::NaN + } else { + ValueMatch::F64(v) + } +} + +impl ValueMatch { + /// Parse a `ValueMatch` that will match `fmt::Debug` fields using regular + /// expressions. + /// + /// This returns an error if the string didn't contain a valid `bool`, + /// `u64`, `i64`, or `f64` literal, and couldn't be parsed as a regular + /// expression. + fn parse_regex(s: &str) -> Result { + s.parse::() + .map(ValueMatch::Bool) + .or_else(|_| s.parse::().map(ValueMatch::U64)) + .or_else(|_| s.parse::().map(ValueMatch::I64)) + .or_else(|_| s.parse::().map(value_match_f64)) + .or_else(|_| { + s.parse::() + .map(|p| ValueMatch::Pat(Box::new(p))) + }) + } + + /// Parse a `ValueMatch` that will match `fmt::Debug` against a fixed + /// string. + /// + /// This does *not* return an error, because any string that isn't a valid + /// `bool`, `u64`, `i64`, or `f64` literal is treated as expected + /// `fmt::Debug` output. + fn parse_non_regex(s: &str) -> Self { + s.parse::() + .map(ValueMatch::Bool) + .or_else(|_| s.parse::().map(ValueMatch::U64)) + .or_else(|_| s.parse::().map(ValueMatch::I64)) + .or_else(|_| s.parse::().map(value_match_f64)) + .unwrap_or_else(|_| ValueMatch::Debug(MatchDebug::new(s))) + } +} + +impl fmt::Display for ValueMatch { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ValueMatch::Bool(ref inner) => fmt::Display::fmt(inner, f), + ValueMatch::F64(ref inner) => fmt::Display::fmt(inner, f), + ValueMatch::NaN => fmt::Display::fmt(&std::f64::NAN, f), + ValueMatch::I64(ref inner) => fmt::Display::fmt(inner, f), + ValueMatch::U64(ref inner) => fmt::Display::fmt(inner, f), + ValueMatch::Debug(ref inner) => fmt::Display::fmt(inner, f), + ValueMatch::Pat(ref inner) => fmt::Display::fmt(inner, f), + } + } +} + +// === impl MatchPattern === + +impl FromStr for MatchPattern { + type Err = matchers::Error; + fn from_str(s: &str) -> Result { + let matcher = s.parse::()?; + Ok(Self { + matcher, + pattern: s.to_owned().into(), + }) + } +} + +impl fmt::Display for MatchPattern { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&*self.pattern, f) + } +} + +impl AsRef for MatchPattern { + #[inline] + fn as_ref(&self) -> &str { + self.pattern.as_ref() + } +} + +impl MatchPattern { + #[inline] + fn str_matches(&self, s: &impl AsRef) -> bool { + self.matcher.matches(s) + } + + #[inline] + fn debug_matches(&self, d: &impl fmt::Debug) -> bool { + self.matcher.debug_matches(d) + } + + pub(super) fn into_debug_match(self) -> MatchDebug { + MatchDebug { + pattern: self.pattern, + } + } +} + +impl PartialEq for MatchPattern { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.pattern == other.pattern + } +} + +impl Eq for MatchPattern {} + +impl PartialOrd for MatchPattern { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.pattern.cmp(&other.pattern)) + } +} + +impl Ord for MatchPattern { + #[inline] + fn cmp(&self, other: &Self) -> Ordering { + self.pattern.cmp(&other.pattern) + } +} + +// === impl MatchDebug === + +impl MatchDebug { + fn new(s: &str) -> Self { + Self { + pattern: s.to_owned().into(), + } + } + + #[inline] + fn debug_matches(&self, d: &impl fmt::Debug) -> bool { + // Naively, we would probably match a value's `fmt::Debug` output by + // formatting it to a string, and then checking if the string is equal + // to the expected pattern. However, this would require allocating every + // time we want to match a field value against a `Debug` matcher, which + // can be avoided. + // + // Instead, we implement `fmt::Write` for a type that, rather than + // actually _writing_ the strings to something, matches them against the + // expected pattern, and returns an error if the pattern does not match. + struct Matcher<'a> { + pattern: &'a str, + } + + impl fmt::Write for Matcher<'_> { + fn write_str(&mut self, s: &str) -> fmt::Result { + // If the string is longer than the remaining expected string, + // we know it won't match, so bail. + if s.len() > self.pattern.len() { + return Err(fmt::Error); + } + + // If the expected string begins with the string that was + // written, we are still potentially a match. Advance the + // position in the expected pattern to chop off the matched + // output, and continue. + if self.pattern.starts_with(s) { + self.pattern = &self.pattern[s.len()..]; + return Ok(()); + } + + // Otherwise, the expected string doesn't include the string + // that was written at the current position, so the `fmt::Debug` + // output doesn't match! Return an error signalling that this + // doesn't match. + Err(fmt::Error) + } + } + let mut matcher = Matcher { + pattern: &self.pattern, + }; + + // Try to "write" the value's `fmt::Debug` output to a `Matcher`. This + // returns an error if the `fmt::Debug` implementation wrote any + // characters that did not match the expected pattern. + write!(matcher, "{:?}", d).is_ok() + } +} + +impl fmt::Display for MatchDebug { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&*self.pattern, f) + } +} + +impl AsRef for MatchDebug { + #[inline] + fn as_ref(&self) -> &str { + self.pattern.as_ref() + } +} + +impl PartialEq for MatchDebug { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.pattern == other.pattern + } +} + +impl Eq for MatchDebug {} + +impl PartialOrd for MatchDebug { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.pattern.cmp(&other.pattern)) + } +} + +impl Ord for MatchDebug { + #[inline] + fn cmp(&self, other: &Self) -> Ordering { + self.pattern.cmp(&other.pattern) + } +} + +// === impl BadName === + +impl Error for BadName {} + +impl fmt::Display for BadName { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "invalid field name `{}`", self.name) + } +} + +impl CallsiteMatch { + pub(crate) fn to_span_match(&self) -> SpanMatch { + let fields = self + .fields + .iter() + .map(|(k, v)| (k.clone(), (v.clone(), AtomicBool::new(false)))) + .collect(); + SpanMatch { + fields, + level: self.level, + has_matched: AtomicBool::new(false), + } + } +} + +impl SpanMatch { + pub(crate) fn visitor(&self) -> MatchVisitor<'_> { + MatchVisitor { inner: self } + } + + #[inline] + pub(crate) fn is_matched(&self) -> bool { + if self.has_matched.load(Acquire) { + return true; + } + self.is_matched_slow() + } + + #[inline(never)] + fn is_matched_slow(&self) -> bool { + let matched = self + .fields + .values() + .all(|(_, matched)| matched.load(Acquire)); + if matched { + self.has_matched.store(true, Release); + } + matched + } + + #[inline] + pub(crate) fn filter(&self) -> Option { + if self.is_matched() { + Some(self.level) + } else { + None + } + } +} + +impl<'a> Visit for MatchVisitor<'a> { + fn record_f64(&mut self, field: &Field, value: f64) { + match self.inner.fields.get(field) { + Some((ValueMatch::NaN, ref matched)) if value.is_nan() => { + matched.store(true, Release); + } + Some((ValueMatch::F64(ref e), ref matched)) + if (value - *e).abs() < std::f64::EPSILON => + { + matched.store(true, Release); + } + _ => {} + } + } + + fn record_i64(&mut self, field: &Field, value: i64) { + use std::convert::TryInto; + + match self.inner.fields.get(field) { + Some((ValueMatch::I64(ref e), ref matched)) if value == *e => { + matched.store(true, Release); + } + Some((ValueMatch::U64(ref e), ref matched)) if Ok(value) == (*e).try_into() => { + matched.store(true, Release); + } + _ => {} + } + } + + fn record_u64(&mut self, field: &Field, value: u64) { + match self.inner.fields.get(field) { + Some((ValueMatch::U64(ref e), ref matched)) if value == *e => { + matched.store(true, Release); + } + _ => {} + } + } + + fn record_bool(&mut self, field: &Field, value: bool) { + match self.inner.fields.get(field) { + Some((ValueMatch::Bool(ref e), ref matched)) if value == *e => { + matched.store(true, Release); + } + _ => {} + } + } + + fn record_str(&mut self, field: &Field, value: &str) { + match self.inner.fields.get(field) { + Some((ValueMatch::Pat(ref e), ref matched)) if e.str_matches(&value) => { + matched.store(true, Release); + } + Some((ValueMatch::Debug(ref e), ref matched)) if e.debug_matches(&value) => { + matched.store(true, Release) + } + _ => {} + } + } + + fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) { + match self.inner.fields.get(field) { + Some((ValueMatch::Pat(ref e), ref matched)) if e.debug_matches(&value) => { + matched.store(true, Release); + } + Some((ValueMatch::Debug(ref e), ref matched)) if e.debug_matches(&value) => { + matched.store(true, Release) + } + _ => {} + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + #[derive(Debug)] + #[allow(dead_code)] + struct MyStruct { + answer: usize, + question: &'static str, + } + + #[test] + fn debug_struct_match() { + let my_struct = MyStruct { + answer: 42, + question: "life, the universe, and everything", + }; + + let pattern = "MyStruct { answer: 42, question: \"life, the universe, and everything\" }"; + + assert_eq!( + format!("{:?}", my_struct), + pattern, + "`MyStruct`'s `Debug` impl doesn't output the expected string" + ); + + let matcher = MatchDebug { + pattern: pattern.into(), + }; + assert!(matcher.debug_matches(&my_struct)) + } + + #[test] + fn debug_struct_not_match() { + let my_struct = MyStruct { + answer: 42, + question: "what shall we have for lunch?", + }; + + let pattern = "MyStruct { answer: 42, question: \"life, the universe, and everything\" }"; + + assert_eq!( + format!("{:?}", my_struct), + "MyStruct { answer: 42, question: \"what shall we have for lunch?\" }", + "`MyStruct`'s `Debug` impl doesn't output the expected string" + ); + + let matcher = MatchDebug { + pattern: pattern.into(), + }; + assert!(!matcher.debug_matches(&my_struct)) + } +} -- cgit v1.2.3