• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use super::{
2     directive::{self, Directive},
3     EnvFilter, FromEnvError,
4 };
5 use crate::sync::RwLock;
6 use std::env;
7 use thread_local::ThreadLocal;
8 use tracing::level_filters::STATIC_MAX_LEVEL;
9 
10 /// A [builder] for constructing new [`EnvFilter`]s.
11 ///
12 /// [builder]: https://rust-unofficial.github.io/patterns/patterns/creational/builder.html
13 #[derive(Debug, Clone)]
14 #[must_use]
15 pub struct Builder {
16     regex: bool,
17     env: Option<String>,
18     default_directive: Option<Directive>,
19 }
20 
21 impl Builder {
22     /// Sets whether span field values can be matched with regular expressions.
23     ///
24     /// If this is `true`, field filter directives will be interpreted as
25     /// regular expressions if they are not able to be interpreted as a `bool`,
26     /// `i64`, `u64`, or `f64` literal. If this is `false,` those field values
27     /// will be interpreted as literal [`std::fmt::Debug`] output instead.
28     ///
29     /// By default, regular expressions are enabled.
30     ///
31     /// **Note**: when [`EnvFilter`]s are constructed from untrusted inputs,
32     /// disabling regular expressions is strongly encouraged.
with_regex(self, regex: bool) -> Self33     pub fn with_regex(self, regex: bool) -> Self {
34         Self { regex, ..self }
35     }
36 
37     /// Sets a default [filtering directive] that will be added to the filter if
38     /// the parsed string or environment variable contains no filter directives.
39     ///
40     /// By default, there is no default directive.
41     ///
42     /// # Examples
43     ///
44     /// If [`parse`], [`parse_lossy`], [`from_env`], or [`from_env_lossy`] are
45     /// called with an empty string or environment variable, the default
46     /// directive is used instead:
47     ///
48     /// ```rust
49     /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
50     /// use tracing_subscriber::filter::{EnvFilter, LevelFilter};
51     ///
52     /// let filter = EnvFilter::builder()
53     ///     .with_default_directive(LevelFilter::INFO.into())
54     ///     .parse("")?;
55     ///
56     /// assert_eq!(format!("{}", filter), "info");
57     /// # Ok(()) }
58     /// ```
59     ///
60     /// Note that the `lossy` variants ([`parse_lossy`] and [`from_env_lossy`])
61     /// will ignore any invalid directives. If all directives in a filter
62     /// string or environment variable are invalid, those methods will also use
63     /// the default directive:
64     ///
65     /// ```rust
66     /// use tracing_subscriber::filter::{EnvFilter, LevelFilter};
67     ///
68     /// let filter = EnvFilter::builder()
69     ///     .with_default_directive(LevelFilter::INFO.into())
70     ///     .parse_lossy("some_target=fake level,foo::bar=lolwut");
71     ///
72     /// assert_eq!(format!("{}", filter), "info");
73     /// ```
74     ///
75     ///
76     /// If the string or environment variable contains valid filtering
77     /// directives, the default directive is not used:
78     ///
79     /// ```rust
80     /// use tracing_subscriber::filter::{EnvFilter, LevelFilter};
81     ///
82     /// let filter = EnvFilter::builder()
83     ///     .with_default_directive(LevelFilter::INFO.into())
84     ///     .parse_lossy("foo=trace");
85     ///
86     /// // The default directive is *not* used:
87     /// assert_eq!(format!("{}", filter), "foo=trace");
88     /// ```
89     ///
90     /// Parsing a more complex default directive from a string:
91     ///
92     /// ```rust
93     /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
94     /// use tracing_subscriber::filter::{EnvFilter, LevelFilter};
95     ///
96     /// let default = "myapp=debug".parse()
97     ///     .expect("hard-coded default directive should be valid");
98     ///
99     /// let filter = EnvFilter::builder()
100     ///     .with_default_directive(default)
101     ///     .parse("")?;
102     ///
103     /// assert_eq!(format!("{}", filter), "myapp=debug");
104     /// # Ok(()) }
105     /// ```
106     ///
107     /// [`parse_lossy`]: Self::parse_lossy
108     /// [`from_env_lossy`]: Self::from_env_lossy
109     /// [`parse`]: Self::parse
110     /// [`from_env`]: Self::from_env
with_default_directive(self, default_directive: Directive) -> Self111     pub fn with_default_directive(self, default_directive: Directive) -> Self {
112         Self {
113             default_directive: Some(default_directive),
114             ..self
115         }
116     }
117 
118     /// Sets the name of the environment variable used by the [`from_env`],
119     /// [`from_env_lossy`], and [`try_from_env`] methods.
120     ///
121     /// By default, this is the value of [`EnvFilter::DEFAULT_ENV`]
122     /// (`RUST_LOG`).
123     ///
124     /// [`from_env`]: Self::from_env
125     /// [`from_env_lossy`]: Self::from_env_lossy
126     /// [`try_from_env`]: Self::try_from_env
with_env_var(self, var: impl ToString) -> Self127     pub fn with_env_var(self, var: impl ToString) -> Self {
128         Self {
129             env: Some(var.to_string()),
130             ..self
131         }
132     }
133 
134     /// Returns a new [`EnvFilter`] from the directives in the given string,
135     /// *ignoring* any that are invalid.
parse_lossy<S: AsRef<str>>(&self, dirs: S) -> EnvFilter136     pub fn parse_lossy<S: AsRef<str>>(&self, dirs: S) -> EnvFilter {
137         let directives = dirs
138             .as_ref()
139             .split(',')
140             .filter(|s| !s.is_empty())
141             .filter_map(|s| match Directive::parse(s, self.regex) {
142                 Ok(d) => Some(d),
143                 Err(err) => {
144                     eprintln!("ignoring `{}`: {}", s, err);
145                     None
146                 }
147             });
148         self.from_directives(directives)
149     }
150 
151     /// Returns a new [`EnvFilter`] from the directives in the given string,
152     /// or an error if any are invalid.
parse<S: AsRef<str>>(&self, dirs: S) -> Result<EnvFilter, directive::ParseError>153     pub fn parse<S: AsRef<str>>(&self, dirs: S) -> Result<EnvFilter, directive::ParseError> {
154         let dirs = dirs.as_ref();
155         if dirs.is_empty() {
156             return Ok(self.from_directives(std::iter::empty()));
157         }
158         let directives = dirs
159             .split(',')
160             .filter(|s| !s.is_empty())
161             .map(|s| Directive::parse(s, self.regex))
162             .collect::<Result<Vec<_>, _>>()?;
163         Ok(self.from_directives(directives))
164     }
165 
166     /// Returns a new [`EnvFilter`] from the directives in the configured
167     /// environment variable, ignoring any directives that are invalid.
from_env_lossy(&self) -> EnvFilter168     pub fn from_env_lossy(&self) -> EnvFilter {
169         let var = env::var(self.env_var_name()).unwrap_or_default();
170         self.parse_lossy(var)
171     }
172 
173     /// Returns a new [`EnvFilter`] from the directives in the in the configured
174     /// environment variable, or an error if the environment variable is not set
175     /// or contains invalid directives.
from_env(&self) -> Result<EnvFilter, FromEnvError>176     pub fn from_env(&self) -> Result<EnvFilter, FromEnvError> {
177         let var = env::var(self.env_var_name()).unwrap_or_default();
178         self.parse(var).map_err(Into::into)
179     }
180 
181     /// Returns a new [`EnvFilter`] from the directives in the in the configured
182     /// environment variable, or an error if the environment variable is not set
183     /// or contains invalid directives.
try_from_env(&self) -> Result<EnvFilter, FromEnvError>184     pub fn try_from_env(&self) -> Result<EnvFilter, FromEnvError> {
185         let var = env::var(self.env_var_name())?;
186         self.parse(var).map_err(Into::into)
187     }
188 
189     // TODO(eliza): consider making this a public API?
190     // Clippy doesn't love this naming, because it suggests that `from_` methods
191     // should not take a `Self`...but in this case, it's the `EnvFilter` that is
192     // being constructed "from" the directives, rather than the builder itself.
193     #[allow(clippy::wrong_self_convention)]
from_directives( &self, directives: impl IntoIterator<Item = Directive>, ) -> EnvFilter194     pub(super) fn from_directives(
195         &self,
196         directives: impl IntoIterator<Item = Directive>,
197     ) -> EnvFilter {
198         use tracing::Level;
199 
200         let mut directives: Vec<_> = directives.into_iter().collect();
201         let mut disabled = Vec::new();
202         for directive in &mut directives {
203             if directive.level > STATIC_MAX_LEVEL {
204                 disabled.push(directive.clone());
205             }
206             if !self.regex {
207                 directive.deregexify();
208             }
209         }
210 
211         if !disabled.is_empty() {
212             #[cfg(feature = "ansi_term")]
213             use ansi_term::{Color, Style};
214             // NOTE: We can't use a configured `MakeWriter` because the EnvFilter
215             // has no knowledge of any underlying subscriber or collector, which
216             // may or may not use a `MakeWriter`.
217             let warn = |msg: &str| {
218                 #[cfg(not(feature = "ansi_term"))]
219                 let msg = format!("warning: {}", msg);
220                 #[cfg(feature = "ansi_term")]
221                 let msg = {
222                     let bold = Style::new().bold();
223                     let mut warning = Color::Yellow.paint("warning");
224                     warning.style_ref_mut().is_bold = true;
225                     format!("{}{} {}", warning, bold.paint(":"), bold.paint(msg))
226                 };
227                 eprintln!("{}", msg);
228             };
229             let ctx_prefixed = |prefix: &str, msg: &str| {
230                 #[cfg(not(feature = "ansi_term"))]
231                 let msg = format!("{} {}", prefix, msg);
232                 #[cfg(feature = "ansi_term")]
233                 let msg = {
234                     let mut equal = Color::Fixed(21).paint("="); // dark blue
235                     equal.style_ref_mut().is_bold = true;
236                     format!(" {} {} {}", equal, Style::new().bold().paint(prefix), msg)
237                 };
238                 eprintln!("{}", msg);
239             };
240             let ctx_help = |msg| ctx_prefixed("help:", msg);
241             let ctx_note = |msg| ctx_prefixed("note:", msg);
242             let ctx = |msg: &str| {
243                 #[cfg(not(feature = "ansi_term"))]
244                 let msg = format!("note: {}", msg);
245                 #[cfg(feature = "ansi_term")]
246                 let msg = {
247                     let mut pipe = Color::Fixed(21).paint("|");
248                     pipe.style_ref_mut().is_bold = true;
249                     format!(" {} {}", pipe, msg)
250                 };
251                 eprintln!("{}", msg);
252             };
253             warn("some trace filter directives would enable traces that are disabled statically");
254             for directive in disabled {
255                 let target = if let Some(target) = &directive.target {
256                     format!("the `{}` target", target)
257                 } else {
258                     "all targets".into()
259                 };
260                 let level = directive
261                     .level
262                     .into_level()
263                     .expect("=off would not have enabled any filters");
264                 ctx(&format!(
265                     "`{}` would enable the {} level for {}",
266                     directive, level, target
267                 ));
268             }
269             ctx_note(&format!("the static max level is `{}`", STATIC_MAX_LEVEL));
270             let help_msg = || {
271                 let (feature, filter) = match STATIC_MAX_LEVEL.into_level() {
272                     Some(Level::TRACE) => unreachable!(
273                         "if the max level is trace, no static filtering features are enabled"
274                     ),
275                     Some(Level::DEBUG) => ("max_level_debug", Level::TRACE),
276                     Some(Level::INFO) => ("max_level_info", Level::DEBUG),
277                     Some(Level::WARN) => ("max_level_warn", Level::INFO),
278                     Some(Level::ERROR) => ("max_level_error", Level::WARN),
279                     None => return ("max_level_off", String::new()),
280                 };
281                 (feature, format!("{} ", filter))
282             };
283             let (feature, earlier_level) = help_msg();
284             ctx_help(&format!(
285                 "to enable {}logging, remove the `{}` feature from the `tracing` crate",
286                 earlier_level, feature
287             ));
288         }
289 
290         let (dynamics, statics) = Directive::make_tables(directives);
291         let has_dynamics = !dynamics.is_empty();
292 
293         let mut filter = EnvFilter {
294             statics,
295             dynamics,
296             has_dynamics,
297             by_id: RwLock::new(Default::default()),
298             by_cs: RwLock::new(Default::default()),
299             scope: ThreadLocal::new(),
300             regex: self.regex,
301         };
302 
303         if !has_dynamics && filter.statics.is_empty() {
304             if let Some(ref default) = self.default_directive {
305                 filter = filter.add_directive(default.clone());
306             }
307         }
308 
309         filter
310     }
311 
env_var_name(&self) -> &str312     fn env_var_name(&self) -> &str {
313         self.env.as_deref().unwrap_or(EnvFilter::DEFAULT_ENV)
314     }
315 }
316 
317 impl Default for Builder {
default() -> Self318     fn default() -> Self {
319         Self {
320             regex: true,
321             env: None,
322             default_directive: None,
323         }
324     }
325 }
326