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 serde::{Deserialize, Serializer, de};
13use std::fmt::Formatter;
14use std::str::FromStr;
15use tracing_subscriber::EnvFilter;
16
17pub mod params;
18
19#[derive(Debug, Clone)]
20struct ValidatedEnvFilterString(String);
21
22/// Wraps [`EnvFilter`] to provide a [`Clone`] implementation.
23pub struct CloneableEnvFilter {
24    filter: EnvFilter,
25    validated: ValidatedEnvFilterString,
26}
27
28impl AsRef<EnvFilter> for CloneableEnvFilter {
29    fn as_ref(&self) -> &EnvFilter {
30        &self.filter
31    }
32}
33
34impl From<CloneableEnvFilter> for EnvFilter {
35    fn from(value: CloneableEnvFilter) -> Self {
36        value.filter
37    }
38}
39
40impl PartialEq for CloneableEnvFilter {
41    fn eq(&self, other: &Self) -> bool {
42        format!("{}", self) == format!("{}", other)
43    }
44}
45
46impl Eq for CloneableEnvFilter {}
47
48impl Clone for CloneableEnvFilter {
49    fn clone(&self) -> Self {
50        // TODO: implement Clone on `EnvFilter` upstream
51        Self {
52            // While EnvFilter has the undocumented property of roundtripping through
53            // its String format, it seems safer to always create a new EnvFilter from
54            // the same validated input when cloning.
55            filter: EnvFilter::from_str(&self.validated.0).expect("validated"),
56            validated: self.validated.clone(),
57        }
58    }
59}
60
61impl FromStr for CloneableEnvFilter {
62    type Err = tracing_subscriber::filter::ParseError;
63
64    fn from_str(s: &str) -> Result<Self, Self::Err> {
65        let filter: EnvFilter = s.parse()?;
66        Ok(CloneableEnvFilter {
67            filter,
68            validated: ValidatedEnvFilterString(s.to_string()),
69        })
70    }
71}
72
73impl std::fmt::Display for CloneableEnvFilter {
74    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
75        write!(f, "{}", self.filter)
76    }
77}
78
79impl std::fmt::Debug for CloneableEnvFilter {
80    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
81        write!(f, "{}", self.filter)
82    }
83}
84
85impl serde::Serialize for CloneableEnvFilter {
86    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
87    where
88        S: Serializer,
89    {
90        serializer.serialize_str(&format!("{}", self))
91    }
92}
93
94impl<'de> Deserialize<'de> for CloneableEnvFilter {
95    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
96    where
97        D: serde::Deserializer<'de>,
98    {
99        let s = String::deserialize(deserializer)?;
100        Self::from_str(s.as_str()).map_err(|x| de::Error::custom(x.to_string()))
101    }
102}
103use tracing_subscriber::filter::Directive;
104
105/// Wraps [`Directive`] to provide a serde implementations.
106#[derive(PartialEq, Eq, Clone, Debug)]
107pub struct SerializableDirective(Directive);
108
109impl From<SerializableDirective> for Directive {
110    fn from(value: SerializableDirective) -> Self {
111        value.0
112    }
113}
114
115impl From<Directive> for SerializableDirective {
116    fn from(value: Directive) -> Self {
117        SerializableDirective(value)
118    }
119}
120
121impl FromStr for SerializableDirective {
122    type Err = tracing_subscriber::filter::ParseError;
123
124    fn from_str(s: &str) -> Result<Self, Self::Err> {
125        let dir: Directive = s.parse()?;
126        Ok(SerializableDirective(dir))
127    }
128}
129
130impl std::fmt::Display for SerializableDirective {
131    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
132        write!(f, "{}", self.0)
133    }
134}
135
136impl serde::Serialize for SerializableDirective {
137    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
138    where
139        S: Serializer,
140    {
141        serializer.serialize_str(&format!("{}", self))
142    }
143}
144
145impl<'de> Deserialize<'de> for SerializableDirective {
146    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
147    where
148        D: serde::Deserializer<'de>,
149    {
150        let s = String::deserialize(deserializer)?;
151        Self::from_str(s.as_str()).map_err(|x| de::Error::custom(x.to_string()))
152    }
153}
154
155#[cfg(test)]
156mod test {
157    use crate::{CloneableEnvFilter, SerializableDirective};
158    use std::str::FromStr;
159
160    // TODO(guswynn): we probably want to test round-tripping through the
161    // `RustType` impl as well
162
163    #[mz_ore::test]
164    fn roundtrips() {
165        let filter = CloneableEnvFilter::from_str(
166            "abc=debug,def=trace,[123],foo,baz[bar{a=b}]=debug,[{13=37}]=trace,info",
167        )
168        .expect("valid");
169        assert_eq!(
170            format!("{}", filter),
171            format!(
172                "{}",
173                CloneableEnvFilter::from_str(&format!("{}", filter)).expect("valid")
174            )
175        );
176    }
177
178    #[mz_ore::test]
179    fn roundtrips_directive() {
180        let dir = SerializableDirective::from_str("abc=debug").expect("valid");
181        assert_eq!(
182            format!("{}", dir),
183            format!(
184                "{}",
185                SerializableDirective::from_str(&format!("{}", dir)).expect("valid")
186            )
187        );
188    }
189}