1use std::{
2 collections::{hash_map::Entry, HashMap},
3 env::VarError,
4 error::Error as StdError,
5 fmt,
6};
7
8use crate::{sync::*, LevelFilter};
9
10pub(crate) type EnvLevel = HashMap<EnvLevelLogger, LevelFilter>;
11
12static ENV_LEVEL: Lazy<RwLock<Option<EnvLevel>>> = Lazy::new(|| RwLock::new(None));
13
14#[derive(Clone, Eq, PartialEq, Hash, Debug)]
15pub(crate) enum EnvLevelLogger {
16 Default,
17 Named(String),
18 Unnamed,
19 AllExceptDefault,
20}
21
22#[derive(Debug)]
24pub enum EnvLevelError {
25 FetchEnvVar(VarError),
27
28 ParseEnvVar(
30 String,
32 ),
33}
34
35impl StdError for EnvLevelError {}
36
37impl fmt::Display for EnvLevelError {
38 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39 match self {
40 EnvLevelError::FetchEnvVar(err) => write!(f, "fetch environment variable error: {err}"),
41 EnvLevelError::ParseEnvVar(description) => {
42 write!(f, "parse environment variable error: {description}")
43 }
44 }
45 }
46}
47
48impl EnvLevelLogger {
49 #[must_use]
50 fn from_key(logger_name: &str) -> Self {
51 if logger_name.is_empty() {
52 EnvLevelLogger::Unnamed
53 } else if logger_name == "*" {
54 EnvLevelLogger::AllExceptDefault
55 } else {
56 EnvLevelLogger::Named(logger_name.into())
57 }
58 }
59
60 #[must_use]
61 fn from_logger(logger_name: Option<&str>) -> Self {
62 match logger_name {
63 None => Self::Unnamed,
64 Some(name) => Self::Named(name.into()),
65 }
66 }
67}
68
69pub(crate) fn from_str(var: &str) -> Result<(), EnvLevelError> {
70 let env_level = from_str_inner(var)?;
71 *ENV_LEVEL.write_expect() = Some(env_level);
72 Ok(())
73}
74
75pub(crate) fn from_str_inner(var: &str) -> Result<EnvLevel, EnvLevelError> {
76 (|| {
77 let mut env_level = EnvLevel::new();
78
79 for kv_str in var.split(',').map(str::trim) {
80 if kv_str.is_empty() {
81 continue;
82 }
83
84 let mut kv = kv_str.split('=');
85 let (left, right) = (kv.next().map(str::trim), kv.next().map(str::trim));
86
87 let (logger, level) = match (left, right, kv.next()) {
88 (Some(default_logger_level), None, None) => {
89 if let Some(level) = LevelFilter::from_str_for_env(default_logger_level) {
90 (EnvLevelLogger::Default, level)
91 } else {
92 return Err(format!("cannot parse level for default logger: '{kv_str}'"));
93 }
94 }
95 (Some(logger_name), Some(level), None) => {
96 if let Some(level) = LevelFilter::from_str_for_env(level) {
97 (EnvLevelLogger::from_key(logger_name), level)
98 } else {
99 return Err(format!(
100 "cannot parse level for logger '{logger_name}': '{kv_str}'"
101 ));
102 }
103 }
104 _ => {
105 return Err(format!("invalid kv: '{kv_str}'"));
106 }
107 };
108
109 match env_level.entry(logger) {
110 Entry::Occupied(_) => {
111 return Err(format!("specified level multiple times: '{kv_str}'"));
112 }
113 Entry::Vacant(entry) => entry.insert(level),
114 };
115 }
116
117 Ok(env_level)
118 })()
119 .map_err(EnvLevelError::ParseEnvVar)
120}
121
122#[must_use]
123pub(crate) fn logger_level(kind: LoggerKind) -> Option<LevelFilter> {
124 logger_level_inner(ENV_LEVEL.read_expect().as_ref()?, kind)
125}
126
127#[derive(Clone, Eq, PartialEq, Debug)]
128pub(crate) enum LoggerKind<'a> {
129 Default,
130 Other(Option<&'a str>),
131}
132
133#[must_use]
134pub(crate) fn logger_level_inner(env_level: &EnvLevel, kind: LoggerKind) -> Option<LevelFilter> {
135 let level = match kind {
136 LoggerKind::Default => env_level.get(&EnvLevelLogger::Default)?,
137 LoggerKind::Other(logger_name) => env_level
138 .get(&EnvLevelLogger::from_logger(logger_name))
139 .or_else(|| env_level.get(&EnvLevelLogger::AllExceptDefault))?,
140 };
141 Some(*level)
142}
143
144#[cfg(test)]
145mod tests {
146 use super::*;
147 use crate::Level;
148
149 #[test]
150 fn validation() {
151 macro_rules! assert_levels {
152 ($env_level:expr, DEFAULT => $default:expr, UNNAMED => $unnamed:expr, NAMED($name:literal) => $named:expr $(,)?) => {
153 assert_eq!(
154 logger_level_inner(&$env_level, LoggerKind::Default),
155 $default
156 );
157 assert_eq!(
158 logger_level_inner(&$env_level, LoggerKind::Other(None)),
159 $unnamed
160 );
161 assert_eq!(
162 logger_level_inner(&$env_level, LoggerKind::Other(Some($name))),
163 $named
164 );
165 };
166 }
167
168 {
169 let mut env_level = HashMap::new();
170 env_level.insert(
171 EnvLevelLogger::Default,
172 LevelFilter::MoreSevereEqual(Level::Debug),
173 );
174 assert_eq!(from_str_inner("dEBUg").unwrap(), env_level);
175
176 assert_levels!(
177 env_level,
178 DEFAULT => Some(LevelFilter::MoreSevereEqual(Level::Debug)),
179 UNNAMED => None,
180 NAMED("name") => None,
181 );
182 }
183
184 {
185 let mut env_level = HashMap::new();
186 env_level.insert(EnvLevelLogger::Default, LevelFilter::All);
187 env_level.insert(
188 EnvLevelLogger::Unnamed,
189 LevelFilter::MoreSevereEqual(Level::Info),
190 );
191 assert_eq!(from_str_inner("aLl,=inFo").unwrap(), env_level);
192
193 assert_levels!(
194 env_level,
195 DEFAULT => Some(LevelFilter::All),
196 UNNAMED => Some(LevelFilter::MoreSevereEqual(Level::Info)),
197 NAMED("name") => None,
198 );
199 }
200
201 {
202 let mut env_level = HashMap::new();
203 env_level.insert(EnvLevelLogger::Default, LevelFilter::Off);
204 env_level.insert(
205 EnvLevelLogger::Unnamed,
206 LevelFilter::MoreSevereEqual(Level::Info),
207 );
208 env_level.insert(
209 EnvLevelLogger::AllExceptDefault,
210 LevelFilter::MoreSevereEqual(Level::Error),
211 );
212 assert_eq!(from_str_inner("oFf,=iNfo,*=erRor").unwrap(), env_level);
213
214 assert_levels!(
215 env_level,
216 DEFAULT => Some(LevelFilter::Off),
217 UNNAMED => Some(LevelFilter::MoreSevereEqual(Level::Info)),
218 NAMED("name") => Some(LevelFilter::MoreSevereEqual(Level::Error)),
219 );
220 }
221
222 {
223 let mut env_level = HashMap::new();
224 env_level.insert(
225 EnvLevelLogger::Unnamed,
226 LevelFilter::MoreSevereEqual(Level::Warn),
227 );
228 env_level.insert(
229 EnvLevelLogger::Named("name".into()),
230 LevelFilter::MoreSevereEqual(Level::Trace),
231 );
232 assert_eq!(from_str_inner("=wArn,name=trAce").unwrap(), env_level);
233
234 assert_levels!(
235 env_level,
236 DEFAULT => None,
237 UNNAMED => Some(LevelFilter::MoreSevereEqual(Level::Warn)),
238 NAMED("name") => Some(LevelFilter::MoreSevereEqual(Level::Trace)),
239 );
240 }
241
242 {
243 let mut env_level = HashMap::new();
244 env_level.insert(
245 EnvLevelLogger::AllExceptDefault,
246 LevelFilter::MoreSevereEqual(Level::Warn),
247 );
248 env_level.insert(
249 EnvLevelLogger::Named("name".into()),
250 LevelFilter::MoreSevereEqual(Level::Trace),
251 );
252 assert_eq!(from_str_inner("*=wArn,name=trAce").unwrap(), env_level);
253
254 assert_levels!(
255 env_level,
256 DEFAULT => None,
257 UNNAMED => Some(LevelFilter::MoreSevereEqual(Level::Warn)),
258 NAMED("name") => Some(LevelFilter::MoreSevereEqual(Level::Trace)),
259 );
260 }
261
262 {
263 let mut env_level = HashMap::new();
264 env_level.insert(EnvLevelLogger::Default, LevelFilter::All);
265 env_level.insert(EnvLevelLogger::AllExceptDefault, LevelFilter::All);
266 assert_eq!(from_str_inner("all,*=all").unwrap(), env_level);
267
268 assert_levels!(
269 env_level,
270 DEFAULT => Some(LevelFilter::All),
271 UNNAMED => Some(LevelFilter::All),
272 NAMED("name") => Some(LevelFilter::All),
273 );
274 }
275
276 {
277 let mut env_level = HashMap::new();
278 env_level.insert(EnvLevelLogger::Default, LevelFilter::Off);
279 env_level.insert(EnvLevelLogger::AllExceptDefault, LevelFilter::All);
280 assert_eq!(from_str_inner("off,*=all").unwrap(), env_level);
281
282 assert_levels!(
283 env_level,
284 DEFAULT => Some(LevelFilter::Off),
285 UNNAMED => Some(LevelFilter::All),
286 NAMED("name") => Some(LevelFilter::All),
287 );
288 }
289 }
290}