Skip to content

Commit 9a00da6

Browse files
authored
fmt: add fallible filter parsing (#74)
## Motivation Currently, when parsing filter directives, the `EnvFilter` type in `tokio-trace-fmt` will skip any invalid directives, and return a default value if the environment variable is unset. In some cases, users may prefer to reject filter strings containing invalid directives. ## Solution This branch adds new `try_from_env`/`try_from_default_env`/`try_new` functions to `EnvFilter` which return `Result`s, and return `Err` if any filtering directives are invalid. The functions not prefixed with `try_` retain thair old behaviour. Signed-off-by: Eliza Weisman <[email protected]>
1 parent 2bf7c61 commit 9a00da6

File tree

1 file changed

+143
-11
lines changed
  • tokio-trace-fmt/src/filter

1 file changed

+143
-11
lines changed

tokio-trace-fmt/src/filter/env.rs

Lines changed: 143 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use regex::Regex;
22
use tokio_trace_core::{subscriber::Interest, Level, Metadata};
33
use {filter::Filter, span::Context};
44

5-
use std::{cmp::Ordering, env};
5+
use std::{cmp::Ordering, env, error::Error, fmt, str::FromStr};
66

77
pub const DEFAULT_FILTER_ENV: &str = "RUST_LOG";
88

@@ -13,6 +13,16 @@ pub struct EnvFilter {
1313
includes_span_directive: bool,
1414
}
1515

16+
#[derive(Debug)]
17+
pub struct FromEnvError {
18+
kind: ErrorKind,
19+
}
20+
21+
#[derive(Debug)]
22+
pub struct ParseError {
23+
directive: String,
24+
}
25+
1626
#[derive(Debug)]
1727
struct Directive {
1828
target: Option<String>,
@@ -29,21 +39,56 @@ enum LevelFilter {
2939
Level(Level),
3040
}
3141

42+
#[derive(Debug)]
43+
enum ErrorKind {
44+
Parse(ParseError),
45+
Env(env::VarError),
46+
}
47+
3248
// ===== impl EnvFilter =====
3349

3450
impl EnvFilter {
51+
/// Returns a new `EnvFilter` from the value of the `RUST_LOG` environment
52+
/// variable, ignoring any invalid filter directives.
3553
pub fn from_default_env() -> Self {
3654
Self::from_env(DEFAULT_FILTER_ENV)
3755
}
3856

57+
/// Returns a new `EnvFilter` from the value of the given environment
58+
/// variable, ignoring any invalid filter directives.
3959
pub fn from_env<A: AsRef<str>>(env: A) -> Self {
40-
let directives = env::var(env.as_ref())
41-
.map(|ref var| parse_directives(var))
42-
.unwrap_or_default();
43-
Self::new(directives)
60+
env::var(env.as_ref())
61+
.map(|ref var| Self::new(var))
62+
.unwrap_or_default()
63+
}
64+
65+
/// Returns a new `EnvFilter` from the directives in the given string,
66+
/// ignoring any that are invalid.
67+
pub fn new<S: AsRef<str>>(dirs: S) -> Self {
68+
Self::new2(parse_directives(dirs.as_ref()))
69+
}
70+
71+
/// Returns a new `EnvFilter` from the directives in the given string,
72+
/// or an error if any are invalid.
73+
pub fn try_new<S: AsRef<str>>(dirs: S) -> Result<Self, ParseError> {
74+
Ok(Self::new2(try_parse_directives(dirs.as_ref())?))
75+
}
76+
77+
/// Returns a new `EnvFilter` from the value of the `RUST_LOG` environment
78+
/// variable, or an error if the environment variable contains any invalid
79+
/// filter directives.
80+
pub fn try_from_default_env() -> Result<Self, FromEnvError> {
81+
Self::try_from_env(DEFAULT_FILTER_ENV)
4482
}
4583

46-
fn new(mut directives: Vec<Directive>) -> Self {
84+
/// Returns a new `EnvFilter` from the value of the given environment
85+
/// variable, or an error if the environment variable is unset or contains
86+
/// any invalid filter directives.
87+
pub fn try_from_env<A: AsRef<str>>(env: A) -> Result<Self, FromEnvError> {
88+
env::var(env.as_ref())?.parse().map_err(Into::into)
89+
}
90+
91+
fn new2(mut directives: Vec<Directive>) -> Self {
4792
if directives.is_empty() {
4893
directives.push(Directive::default());
4994
} else {
@@ -88,12 +133,25 @@ impl EnvFilter {
88133
}
89134
}
90135

91-
impl<A> From<A> for EnvFilter
136+
impl<S> From<S> for EnvFilter
92137
where
93-
A: AsRef<str>,
138+
S: AsRef<str>,
94139
{
95-
fn from(env: A) -> Self {
96-
Self::new(parse_directives(env.as_ref()))
140+
fn from(s: S) -> Self {
141+
Self::new(s)
142+
}
143+
}
144+
145+
impl FromStr for EnvFilter {
146+
type Err = ParseError;
147+
fn from_str(s: &str) -> Result<Self, Self::Err> {
148+
Self::try_new(s)
149+
}
150+
}
151+
152+
impl Default for EnvFilter {
153+
fn default() -> Self {
154+
Self::new2(Default::default())
97155
}
98156
}
99157

@@ -183,13 +241,19 @@ fn parse_directives(spec: &str) -> Vec<Directive> {
183241
spec.split(',')
184242
.filter_map(|dir| {
185243
Directive::parse(dir).or_else(|| {
186-
eprintln!("ignoring invalid log directive '{}'", dir);
244+
eprintln!("ignoring invalid filter directive '{}'", dir);
187245
None
188246
})
189247
})
190248
.collect()
191249
}
192250

251+
fn try_parse_directives(spec: &str) -> Result<Vec<Directive>, ParseError> {
252+
spec.split(',')
253+
.map(|dir| Directive::parse(dir).ok_or_else(|| ParseError::new(dir)))
254+
.collect()
255+
}
256+
193257
// ===== impl Directive =====
194258

195259
impl Directive {
@@ -314,6 +378,74 @@ impl Default for Directive {
314378
}
315379
}
316380

381+
// ===== impl FromEnvError =====
382+
383+
impl From<ParseError> for FromEnvError {
384+
fn from(p: ParseError) -> Self {
385+
Self {
386+
kind: ErrorKind::Parse(p),
387+
}
388+
}
389+
}
390+
391+
impl From<env::VarError> for FromEnvError {
392+
fn from(v: env::VarError) -> Self {
393+
Self {
394+
kind: ErrorKind::Env(v),
395+
}
396+
}
397+
}
398+
399+
impl fmt::Display for FromEnvError {
400+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
401+
match self.kind {
402+
ErrorKind::Parse(ref p) => p.fmt(f),
403+
ErrorKind::Env(ref e) => e.fmt(f),
404+
}
405+
}
406+
}
407+
408+
impl Error for FromEnvError {
409+
fn description(&self) -> &str {
410+
match self.kind {
411+
ErrorKind::Parse(ref p) => p.description(),
412+
ErrorKind::Env(ref e) => e.description(),
413+
}
414+
}
415+
416+
#[allow(deprecated)] // for compatibility with minimum Rust version 1.26.0
417+
fn cause(&self) -> Option<&Error> {
418+
match self.kind {
419+
ErrorKind::Parse(ref p) => Some(p),
420+
ErrorKind::Env(ref e) => Some(e),
421+
}
422+
}
423+
}
424+
425+
// ===== impl ParseError =====
426+
427+
impl ParseError {
428+
fn new(directive: &str) -> Self {
429+
ParseError {
430+
directive: directive.to_string(),
431+
}
432+
}
433+
}
434+
435+
impl fmt::Display for ParseError {
436+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
437+
write!(f, "invalid filter directive '{}'", self.directive)
438+
}
439+
}
440+
441+
impl Error for ParseError {
442+
fn description(&self) -> &str {
443+
"invalid filter directive"
444+
}
445+
}
446+
447+
// ===== impl LevelFilter =====
448+
317449
impl PartialEq<Level> for LevelFilter {
318450
fn eq(&self, other: &Level) -> bool {
319451
match self {

0 commit comments

Comments
 (0)