Skip to content

Commit 01d65f2

Browse files
committed
Use state machine to parse directives
1 parent bfca546 commit 01d65f2

File tree

1 file changed

+108
-90
lines changed

1 file changed

+108
-90
lines changed

tracing-subscriber/src/filter/env/directive.rs

Lines changed: 108 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ use crate::filter::{
44
env::{field, FieldMap},
55
level::LevelFilter,
66
};
7-
use once_cell::sync::Lazy;
8-
use regex::Regex;
97
use std::{cmp::Ordering, fmt, iter::FromIterator, str::FromStr};
108
use tracing_core::{span, Level, Metadata};
119

@@ -120,99 +118,119 @@ impl Directive {
120118
}
121119

122120
pub(super) fn parse(from: &str, regex: bool) -> Result<Self, ParseError> {
123-
static DIRECTIVE_RE: Lazy<Regex> = Lazy::new(|| {
124-
Regex::new(
125-
r"(?x)
126-
^(?P<global_level>(?i:trace|debug|info|warn|error|off|[0-5]))$ |
127-
# ^^^.
128-
# `note: we match log level names case-insensitively
129-
^
130-
(?: # target name or span name
131-
(?P<target>[\w:-]+)|(?P<span>\[[^\]]*\])
132-
){1,2}
133-
(?: # level or nothing
134-
=(?P<level>(?i:trace|debug|info|warn|error|off|[0-5]))?
135-
# ^^^.
136-
# `note: we match log level names case-insensitively
137-
)?
138-
$
139-
",
140-
)
141-
.unwrap()
142-
});
143-
static SPAN_PART_RE: Lazy<Regex> =
144-
Lazy::new(|| Regex::new(r"(?P<name>[^\]\{]+)?(?:\{(?P<fields>[^\}]*)\})?").unwrap());
145-
static FIELD_FILTER_RE: Lazy<Regex> =
146-
// TODO(eliza): this doesn't _currently_ handle value matchers that include comma
147-
// characters. We should fix that.
148-
Lazy::new(|| {
149-
Regex::new(
150-
r"(?x)
151-
(
152-
# field name
153-
[[:word:]][[[:word:]]\.]*
154-
# value part (optional)
155-
(?:=[^,]+)?
156-
)
157-
# trailing comma or EOS
158-
(?:,\s?|$)
159-
",
160-
)
161-
.unwrap()
162-
});
163-
164-
let caps = DIRECTIVE_RE.captures(from).ok_or_else(ParseError::new)?;
121+
let mut cur = Self {
122+
level: LevelFilter::TRACE,
123+
target: None,
124+
in_span: None,
125+
fields: Vec::new(),
126+
};
127+
128+
#[derive(Debug)]
129+
enum ParseState {
130+
Start,
131+
LevelOrTarget { start: usize },
132+
Span { span_start: usize },
133+
Field { field_start: usize },
134+
Fields,
135+
Target,
136+
Level { level_start: usize },
137+
Complete,
138+
}
165139

166-
if let Some(level) = caps
167-
.name("global_level")
168-
.and_then(|s| s.as_str().parse().ok())
169-
{
170-
return Ok(Directive {
171-
level,
172-
..Default::default()
173-
});
140+
use ParseState::*;
141+
let mut state = Start;
142+
dbg!(from);
143+
for (i, c) in from.char_indices() {
144+
state = match (state, c) {
145+
(Start, 'a'..='z' | 'A'..='Z' | '0'..='5') => LevelOrTarget { start: i },
146+
(Start, '[') => Span { span_start: i + 1 },
147+
(LevelOrTarget { start }, '=') => {
148+
cur.target = Some(from[start..i].to_owned());
149+
Level { level_start: i + 1 }
150+
}
151+
(LevelOrTarget { start }, '[') => {
152+
cur.target = Some(from[start..i].to_owned());
153+
Span { span_start: i + 1 }
154+
}
155+
(LevelOrTarget { start }, ',') => {
156+
let (level, target) = match &from[start..] {
157+
"" => (LevelFilter::TRACE, None),
158+
level_or_target => match LevelFilter::from_str(level_or_target) {
159+
Ok(level) => (level, None),
160+
Err(_) => (LevelFilter::TRACE, Some(level_or_target.to_owned())),
161+
},
162+
};
163+
164+
cur.level = level;
165+
cur.target = target;
166+
Complete
167+
}
168+
(state @ LevelOrTarget { .. }, _) => state,
169+
(Target, '=') => Level { level_start: i + 1 },
170+
(Span { span_start }, ']') => {
171+
cur.in_span = Some(from[span_start..i].to_owned());
172+
Target
173+
}
174+
(Span { span_start }, '{') => {
175+
cur.in_span = match &from[span_start..i] {
176+
"" => None,
177+
_ => Some(from[span_start..i].to_owned()),
178+
};
179+
Field { field_start: i + 1 }
180+
}
181+
(state @ Span { .. }, _) => state,
182+
(Field { field_start }, '}') => {
183+
cur.fields.push(match &from[field_start..i] {
184+
"" => return Err(ParseError::new()),
185+
field => field::Match::parse(field, regex)?,
186+
});
187+
Fields
188+
}
189+
(Field { field_start }, ',') => {
190+
cur.fields.push(match &from[field_start..i] {
191+
"" => return Err(ParseError::new()),
192+
field => field::Match::parse(field, regex)?,
193+
});
194+
Field { field_start: i + 1 }
195+
}
196+
(state @ Field { .. }, _) => state,
197+
(Fields, ']') => Target,
198+
(Level { level_start }, ',') => {
199+
cur.level = match &from[level_start..i] {
200+
"" => LevelFilter::TRACE,
201+
level => LevelFilter::from_str(level)?,
202+
};
203+
Complete
204+
}
205+
(state @ Level { .. }, _) => state,
206+
_ => return Err(ParseError::new()),
207+
};
174208
}
175209

176-
let target = caps.name("target").and_then(|c| {
177-
let s = c.as_str();
178-
if s.parse::<LevelFilter>().is_ok() {
179-
None
180-
} else {
181-
Some(s.to_owned())
210+
match state {
211+
LevelOrTarget { start } => {
212+
let (level, target) = match &from[start..] {
213+
"" => (LevelFilter::TRACE, None),
214+
level_or_target => match LevelFilter::from_str(level_or_target) {
215+
Ok(level) => (level, None),
216+
Err(_) => (LevelFilter::TRACE, Some(level_or_target.to_owned())),
217+
},
218+
};
219+
220+
cur.level = level;
221+
cur.target = target;
182222
}
183-
});
184-
185-
let (in_span, fields) = caps
186-
.name("span")
187-
.and_then(|cap| {
188-
let cap = cap.as_str().trim_matches(|c| c == '[' || c == ']');
189-
let caps = SPAN_PART_RE.captures(cap)?;
190-
let span = caps.name("name").map(|c| c.as_str().to_owned());
191-
let fields = caps
192-
.name("fields")
193-
.map(|c| {
194-
FIELD_FILTER_RE
195-
.find_iter(c.as_str())
196-
.map(|c| field::Match::parse(c.as_str(), regex))
197-
.collect::<Result<Vec<_>, _>>()
198-
})
199-
.unwrap_or_else(|| Ok(Vec::new()));
200-
Some((span, fields))
201-
})
202-
.unwrap_or_else(|| (None, Ok(Vec::new())));
203-
204-
let level = caps
205-
.name("level")
206-
.and_then(|l| l.as_str().parse().ok())
207-
// Setting the target without the level enables every level for that target
208-
.unwrap_or(LevelFilter::TRACE);
223+
Level { level_start } => {
224+
cur.level = match &from[level_start..] {
225+
"" => LevelFilter::TRACE,
226+
level => LevelFilter::from_str(level)?,
227+
};
228+
}
229+
Target | Complete => {}
230+
_ => return Err(ParseError::new()),
231+
};
209232

210-
Ok(Self {
211-
level,
212-
target,
213-
in_span,
214-
fields: fields?,
215-
})
233+
Ok(cur)
216234
}
217235
}
218236

0 commit comments

Comments
 (0)