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