1use saluki_error::{generic_error, GenericError};
4use tracing_subscriber::filter::Targets;
5
6const RUST_LOG_ENV_VAR: &str = "RUST_LOG";
8
9pub fn parse_filter_directives(directives: &str) -> Result<Targets, GenericError> {
21 let directives = directives.split(',').filter(|d| !d.is_empty()).collect::<Vec<_>>();
24 if let Some(directive) = directives.iter().find(|d| d.contains('[')) {
25 return Err(generic_error!(
26 "Invalid log filter directive `{}`: span and field filters are not supported.",
27 directive
28 ));
29 }
30
31 if directives.is_empty() {
32 return Ok(Targets::new());
33 }
34
35 directives
36 .join(",")
37 .parse()
38 .map_err(|e| generic_error!("Invalid log filter directives: {}", e))
39}
40
41pub fn filter_from_env(default: Targets) -> Targets {
47 filter_from_env_value(std::env::var(RUST_LOG_ENV_VAR).ok(), default)
48}
49
50fn filter_from_env_value(value: Option<String>, default: Targets) -> Targets {
51 match value {
52 Some(directives) if !directives.is_empty() => match parse_filter_directives(&directives) {
53 Ok(filter) => filter,
54 Err(e) => {
55 eprintln!(
56 "warning: ignoring {}=`{}` ({}); using default log filter `{}`",
57 RUST_LOG_ENV_VAR, directives, e, default
58 );
59 default
60 }
61 },
62 _ => default,
63 }
64}
65
66#[cfg(test)]
67mod tests {
68 use tracing_subscriber::filter::LevelFilter;
69
70 use super::*;
71
72 #[test]
73 fn filter_directives_combine_default_and_per_target_levels() {
74 let targets = parse_filter_directives("info,saluki=debug,saluki::io=trace").expect("valid directives");
75
76 assert_eq!(targets.default_level(), Some(LevelFilter::INFO));
77 assert_eq!(targets.to_string(), "saluki::io=trace,saluki=debug,info");
78 }
79
80 #[test]
81 fn filter_directives_ignore_empty_directives() {
82 let targets = parse_filter_directives("saluki=debug,,").expect("valid directives");
84 assert_eq!(targets.default_level(), None);
85 assert_eq!(targets.to_string(), "saluki=debug");
86
87 let targets = parse_filter_directives(",").expect("no directives");
88 assert_eq!(targets.default_level(), None);
89 assert_eq!(targets.to_string(), "");
90 }
91
92 #[test]
93 fn filter_directives_reject_span_and_field_filters() {
94 for directives in [
95 "[span]=debug",
96 "saluki[span]=debug",
97 "info,saluki[{field}]=debug",
98 "saluki[span{field=value}]=debug",
99 ] {
100 let error = match parse_filter_directives(directives) {
101 Ok(targets) => panic!("`{directives}` should be rejected, got `{targets}`"),
102 Err(error) => error,
103 };
104 assert!(
105 error.to_string().contains("span and field filters are not supported"),
106 "unexpected error message for `{directives}`: {error}"
107 );
108 }
109 }
110
111 #[test]
112 fn filter_directives_reject_invalid_levels() {
113 assert!(parse_filter_directives("saluki=verbose").is_err());
114 assert!(parse_filter_directives("saluki=debug=trace").is_err());
115 }
116
117 #[test]
118 fn filter_from_env_uses_default_when_unset_or_empty() {
119 let default = Targets::new().with_default(LevelFilter::INFO);
120
121 assert_eq!(filter_from_env_value(None, default.clone()), default);
122 assert_eq!(filter_from_env_value(Some(String::new()), default.clone()), default);
123 }
124
125 #[test]
126 fn filter_from_env_replaces_default_with_valid_directives() {
127 let default = Targets::new().with_default(LevelFilter::INFO);
128
129 let filter = filter_from_env_value(Some("saluki=debug".to_string()), default);
130 assert_eq!(filter.to_string(), "saluki=debug");
131 }
132
133 #[test]
134 fn filter_from_env_falls_back_to_default_on_invalid_directives() {
135 let default = Targets::new().with_default(LevelFilter::INFO);
136
137 let filter = filter_from_env_value(Some("saluki[span]=debug".to_string()), default.clone());
138 assert_eq!(filter, default);
139 }
140}