tracing_filter/simple/
mod.rs1use {
4 crate::{Diagnostics, DEFAULT_ENV},
5 compact_str::CompactString,
6 std::{borrow::Cow, cmp, env, ffi::OsStr, fmt},
7 tracing_core::{Interest, LevelFilter, Metadata},
8 tracing_subscriber::layer::Context,
9};
10
11mod parse;
12
13#[derive(Debug, Default)]
43pub struct Filter {
44 directives: Vec<Directive>,
45 regex: Option<regex::Regex>,
46}
47
48#[derive(Debug, PartialEq, Eq)]
49struct Directive {
50 target: Option<CompactString>,
51 level: LevelFilter,
52}
53
54impl fmt::Display for Filter {
55 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56 for directive in &*self.directives {
57 if let Some(target) = &directive.target {
58 write!(f, "{}=", target)?;
59 }
60 write!(f, "{},", directive.level)?;
61 }
62
63 if let Some(regex) = &self.regex {
64 write!(f, "/{}", regex)?;
65 }
66
67 Ok(())
68 }
69}
70
71impl Filter {
72 pub fn new(spec: &str) -> Self {
76 spec.parse().unwrap_or_else(|_| Self::empty())
77 }
78
79 pub const fn empty() -> Self {
81 Self {
82 directives: Vec::new(),
83 regex: None,
84 }
85 }
86
87 pub fn from_default_env() -> Option<(Self, Option<Diagnostics<'static>>)> {
89 Self::from_env(DEFAULT_ENV)
90 }
91
92 pub fn from_env(key: impl AsRef<OsStr>) -> Option<(Self, Option<Diagnostics<'static>>)> {
94 let s = env::var(key).ok()?;
95 let (filter, err) = Self::parse(&s);
96 Some((
97 filter.unwrap_or_default(),
98 match err {
99 None => None,
100 Some(x) => Some({
101 Diagnostics {
102 error: x.error,
103 ignored: x.ignored,
104 disabled: x.disabled,
105 source: s.into(),
106 }
107 }),
108 },
109 ))
110 }
111
112 pub fn layer(self) -> crate::FilterLayer<Self> {
114 crate::FilterLayer::new(self)
115 }
116}
117
118impl Filter {
119 pub fn add_directive<'a>(
123 &mut self,
124 target: Option<impl Into<Cow<'a, str>>>,
125 level: impl Into<LevelFilter>,
126 ) {
127 let target = target.map(Into::into).map(Into::into);
128 let level = level.into();
129 let directive = Directive { target, level };
130 let ix = self.directives.binary_search_by(|x: &Directive| {
131 let a = x.target.as_ref().map(|x| x.len()).unwrap_or(0);
132 let b = directive.target.as_ref().map(|x| x.len()).unwrap_or(0);
133 match a.cmp(&b) {
134 cmp::Ordering::Equal => x.target.cmp(&directive.target),
135 ordering => ordering,
136 }
137 });
138 match ix {
139 Ok(ix) => self.directives[ix] = directive,
140 Err(ix) => self.directives.insert(ix, directive),
141 }
142 }
143
144 pub fn with_directive<'a>(
146 mut self,
147 target: Option<impl Into<Cow<'a, str>>>,
148 level: impl Into<LevelFilter>,
149 ) -> Self {
150 self.add_directive(target, level);
151 self
152 }
153
154 pub fn add_level(&mut self, level: impl Into<LevelFilter>) {
156 self.add_directive(None::<&str>, level);
157 }
158
159 pub fn with_level(mut self, level: impl Into<LevelFilter>) -> Self {
161 self.add_level(level);
162 self
163 }
164
165 pub fn add_target<'a>(
167 &mut self,
168 target: impl Into<Cow<'a, str>>,
169 level: impl Into<LevelFilter>,
170 ) {
171 self.add_directive(Some(target), level);
172 }
173
174 pub fn with_target<'a>(
176 mut self,
177 target: impl Into<Cow<'a, str>>,
178 level: impl Into<LevelFilter>,
179 ) -> Self {
180 self.add_directive(Some(target), level);
181 self
182 }
183
184 pub fn add_regex(&mut self, regex: regex::Regex) {
190 match &self.regex {
191 Some(_) => panic!("set `tracing_filter::simple::Filter` regex that was already set"),
192 None => self.regex = Some(regex),
193 }
194 }
195
196 pub fn with_regex(mut self, regex: regex::Regex) -> Self {
198 self.add_regex(regex);
199 self
200 }
201
202 fn is_enabled(&self, metadata: &Metadata<'_>) -> bool {
203 let level = *metadata.level();
207 let target = metadata.target();
208
209 if self.directives.is_empty() {
210 return level <= LevelFilter::ERROR;
211 }
212
213 for directive in self.directives.iter().rev() {
214 match &directive.target {
215 Some(name) if !target.starts_with(&**name) => {},
216 Some(..) | None => return level <= directive.level,
217 }
218 }
219
220 false
221 }
222}
223
224impl<S> crate::Filter<S> for Filter {
225 fn callsite_enabled(&self, metadata: &Metadata<'_>) -> Interest {
226 if self.is_enabled(metadata) {
227 Interest::always()
228 } else {
229 Interest::never()
230 }
231 }
232
233 fn enabled(&self, metadata: &Metadata<'_>, _ctx: &Context<'_, S>) -> bool {
234 self.is_enabled(metadata)
235 }
236}