@@ -4,8 +4,6 @@ use crate::filter::{
4
4
env:: { field, FieldMap } ,
5
5
level:: LevelFilter ,
6
6
} ;
7
- use once_cell:: sync:: Lazy ;
8
- use regex:: Regex ;
9
7
use std:: { cmp:: Ordering , fmt, iter:: FromIterator , str:: FromStr } ;
10
8
use tracing_core:: { span, Level , Metadata } ;
11
9
@@ -120,99 +118,122 @@ impl Directive {
120
118
}
121
119
122
120
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
+ }
165
139
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
+ for ( i, c) in from. trim ( ) . char_indices ( ) {
143
+ state = match ( state, c) {
144
+ ( Start , '[' ) => Span { span_start : i + 1 } ,
145
+ ( Start , c) if ![ '-' , ':' , '_' ] . contains ( & c) && !c. is_alphanumeric ( ) => {
146
+ return Err ( ParseError :: new ( ) )
147
+ }
148
+ ( Start , _) => LevelOrTarget { start : i } ,
149
+ ( LevelOrTarget { start } , '=' ) => {
150
+ cur. target = Some ( from[ start..i] . to_owned ( ) ) ;
151
+ Level { level_start : i + 1 }
152
+ }
153
+ ( LevelOrTarget { start } , '[' ) => {
154
+ cur. target = Some ( from[ start..i] . to_owned ( ) ) ;
155
+ Span { span_start : i + 1 }
156
+ }
157
+ ( LevelOrTarget { start } , ',' ) => {
158
+ let ( level, target) = match & from[ start..] {
159
+ "" => ( LevelFilter :: TRACE , None ) ,
160
+ level_or_target => match LevelFilter :: from_str ( level_or_target) {
161
+ Ok ( level) => ( level, None ) ,
162
+ Err ( _) => ( LevelFilter :: TRACE , Some ( level_or_target. to_owned ( ) ) ) ,
163
+ } ,
164
+ } ;
165
+
166
+ cur. level = level;
167
+ cur. target = target;
168
+ Complete
169
+ }
170
+ ( state @ LevelOrTarget { .. } , _) => state,
171
+ ( Target , '=' ) => Level { level_start : i + 1 } ,
172
+ ( Span { span_start } , ']' ) => {
173
+ cur. in_span = Some ( from[ span_start..i] . to_owned ( ) ) ;
174
+ Target
175
+ }
176
+ ( Span { span_start } , '{' ) => {
177
+ cur. in_span = match & from[ span_start..i] {
178
+ "" => None ,
179
+ _ => Some ( from[ span_start..i] . to_owned ( ) ) ,
180
+ } ;
181
+ Field { field_start : i + 1 }
182
+ }
183
+ ( state @ Span { .. } , _) => state,
184
+ ( Field { field_start } , '}' ) => {
185
+ cur. fields . push ( match & from[ field_start..i] {
186
+ "" => return Err ( ParseError :: new ( ) ) ,
187
+ field => field:: Match :: parse ( field, regex) ?,
188
+ } ) ;
189
+ Fields
190
+ }
191
+ ( Field { field_start } , ',' ) => {
192
+ cur. fields . push ( match & from[ field_start..i] {
193
+ "" => return Err ( ParseError :: new ( ) ) ,
194
+ field => field:: Match :: parse ( field, regex) ?,
195
+ } ) ;
196
+ Field { field_start : i + 1 }
197
+ }
198
+ ( state @ Field { .. } , _) => state,
199
+ ( Fields , ']' ) => Target ,
200
+ ( Level { level_start } , ',' ) => {
201
+ cur. level = match & from[ level_start..i] {
202
+ "" => LevelFilter :: TRACE ,
203
+ level => LevelFilter :: from_str ( level) ?,
204
+ } ;
205
+ Complete
206
+ }
207
+ ( state @ Level { .. } , _) => state,
208
+ _ => return Err ( ParseError :: new ( ) ) ,
209
+ } ;
174
210
}
175
211
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 ( ) )
212
+ match state {
213
+ LevelOrTarget { start } => {
214
+ let ( level, target) = match & from[ start..] {
215
+ "" => ( LevelFilter :: TRACE , None ) ,
216
+ level_or_target => match LevelFilter :: from_str ( level_or_target) {
217
+ Ok ( level) => ( level, None ) ,
218
+ // Setting the target without the level enables every level for that target
219
+ Err ( _) => ( LevelFilter :: TRACE , Some ( level_or_target. to_owned ( ) ) ) ,
220
+ } ,
221
+ } ;
222
+
223
+ cur. level = level;
224
+ cur. target = target;
182
225
}
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 ) ;
226
+ Level { level_start } => {
227
+ cur. level = match & from[ level_start..] {
228
+ "" => LevelFilter :: TRACE ,
229
+ level => LevelFilter :: from_str ( level) ?,
230
+ } ;
231
+ }
232
+ Target | Complete => { }
233
+ _ => return Err ( ParseError :: new ( ) ) ,
234
+ } ;
209
235
210
- Ok ( Self {
211
- level,
212
- target,
213
- in_span,
214
- fields : fields?,
215
- } )
236
+ Ok ( cur)
216
237
}
217
238
}
218
239
0 commit comments