mz_tracing/
lib.rs

1// Copyright Materialize, Inc. and contributors. All rights reserved.
2//
3// Use of this software is governed by the Business Source License
4// included in the LICENSE file.
5//
6// As of the Change Date specified in that file, in accordance with
7// the Business Source License, use of this software will be governed
8// by the Apache License, Version 2.0.
9
10//! Mz-specific library for interacting with `tracing`.
11
12use proptest::arbitrary::Arbitrary;
13use proptest::prelude::{BoxedStrategy, Strategy};
14use serde::{Deserialize, Serializer, de};
15use std::fmt::Formatter;
16use std::str::FromStr;
17use tracing_subscriber::EnvFilter;
18
19pub mod params;
20
21#[derive(Debug, Clone)]
22struct ValidatedEnvFilterString(String);
23
24/// Wraps [`EnvFilter`] to provide a [`Clone`] implementation.
25pub struct CloneableEnvFilter {
26    filter: EnvFilter,
27    validated: ValidatedEnvFilterString,
28}
29
30impl AsRef<EnvFilter> for CloneableEnvFilter {
31    fn as_ref(&self) -> &EnvFilter {
32        &self.filter
33    }
34}
35
36impl From<CloneableEnvFilter> for EnvFilter {
37    fn from(value: CloneableEnvFilter) -> Self {
38        value.filter
39    }
40}
41
42impl PartialEq for CloneableEnvFilter {
43    fn eq(&self, other: &Self) -> bool {
44        format!("{}", self) == format!("{}", other)
45    }
46}
47
48impl Eq for CloneableEnvFilter {}
49
50impl Clone for CloneableEnvFilter {
51    fn clone(&self) -> Self {
52        // TODO: implement Clone on `EnvFilter` upstream
53        Self {
54            // While EnvFilter has the undocumented property of roundtripping through
55            // its String format, it seems safer to always create a new EnvFilter from
56            // the same validated input when cloning.
57            filter: EnvFilter::from_str(&self.validated.0).expect("validated"),
58            validated: self.validated.clone(),
59        }
60    }
61}
62
63impl FromStr for CloneableEnvFilter {
64    type Err = tracing_subscriber::filter::ParseError;
65
66    fn from_str(s: &str) -> Result<Self, Self::Err> {
67        let filter: EnvFilter = s.parse()?;
68        Ok(CloneableEnvFilter {
69            filter,
70            validated: ValidatedEnvFilterString(s.to_string()),
71        })
72    }
73}
74
75impl std::fmt::Display for CloneableEnvFilter {
76    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
77        write!(f, "{}", self.filter)
78    }
79}
80
81impl std::fmt::Debug for CloneableEnvFilter {
82    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
83        write!(f, "{}", self.filter)
84    }
85}
86
87impl Arbitrary for CloneableEnvFilter {
88    type Strategy = BoxedStrategy<Self>;
89    type Parameters = ();
90
91    fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
92        // there are much more complex EnvFilters we could try building if that seems
93        // worthwhile to explore
94        proptest::sample::select(vec!["info", "debug", "warn", "error", "off"])
95            .prop_map(|x| CloneableEnvFilter::from_str(x).expect("valid EnvFilter"))
96            .boxed()
97    }
98}
99
100impl serde::Serialize for CloneableEnvFilter {
101    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
102    where
103        S: Serializer,
104    {
105        serializer.serialize_str(&format!("{}", self))
106    }
107}
108
109impl<'de> Deserialize<'de> for CloneableEnvFilter {
110    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
111    where
112        D: serde::Deserializer<'de>,
113    {
114        let s = String::deserialize(deserializer)?;
115        Self::from_str(s.as_str()).map_err(|x| de::Error::custom(x.to_string()))
116    }
117}
118use tracing_subscriber::filter::Directive;
119
120/// Wraps [`Directive`] to provide a serde implementations.
121#[derive(PartialEq, Eq, Clone, Debug)]
122pub struct SerializableDirective(Directive);
123
124impl From<SerializableDirective> for Directive {
125    fn from(value: SerializableDirective) -> Self {
126        value.0
127    }
128}
129
130impl From<Directive> for SerializableDirective {
131    fn from(value: Directive) -> Self {
132        SerializableDirective(value)
133    }
134}
135
136impl FromStr for SerializableDirective {
137    type Err = tracing_subscriber::filter::ParseError;
138
139    fn from_str(s: &str) -> Result<Self, Self::Err> {
140        let dir: Directive = s.parse()?;
141        Ok(SerializableDirective(dir))
142    }
143}
144
145impl std::fmt::Display for SerializableDirective {
146    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
147        write!(f, "{}", self.0)
148    }
149}
150
151impl Arbitrary for SerializableDirective {
152    type Strategy = BoxedStrategy<Self>;
153    type Parameters = ();
154
155    fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
156        // there are much more complex EnvFilters we could try building if that seems
157        // worthwhile to explore
158        proptest::sample::select(vec!["info", "debug", "warn", "error", "off"])
159            .prop_map(|x| SerializableDirective::from_str(x).expect("valid Directive"))
160            .boxed()
161    }
162}
163
164impl serde::Serialize for SerializableDirective {
165    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
166    where
167        S: Serializer,
168    {
169        serializer.serialize_str(&format!("{}", self))
170    }
171}
172
173impl<'de> Deserialize<'de> for SerializableDirective {
174    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
175    where
176        D: serde::Deserializer<'de>,
177    {
178        let s = String::deserialize(deserializer)?;
179        Self::from_str(s.as_str()).map_err(|x| de::Error::custom(x.to_string()))
180    }
181}
182
183#[cfg(test)]
184mod test {
185    use crate::{CloneableEnvFilter, SerializableDirective};
186    use std::str::FromStr;
187
188    // TODO(guswynn): we probably want to test round-tripping through the
189    // `RustType` impl as well
190
191    #[mz_ore::test]
192    fn roundtrips() {
193        let filter = CloneableEnvFilter::from_str(
194            "abc=debug,def=trace,[123],foo,baz[bar{a=b}]=debug,[{13=37}]=trace,info",
195        )
196        .expect("valid");
197        assert_eq!(
198            format!("{}", filter),
199            format!(
200                "{}",
201                CloneableEnvFilter::from_str(&format!("{}", filter)).expect("valid")
202            )
203        );
204    }
205
206    #[mz_ore::test]
207    fn roundtrips_directive() {
208        let dir = SerializableDirective::from_str("abc=debug").expect("valid");
209        assert_eq!(
210            format!("{}", dir),
211            format!(
212                "{}",
213                SerializableDirective::from_str(&format!("{}", dir)).expect("valid")
214            )
215        );
216    }
217}