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!("cannot parse level for default logger: '{kv_str}'"));
82 }
83 }
84 (Some(logger_name), Some(level), None) => {
85 if let Some(level) = LevelFilter::from_str_for_env(level) {
86 (EnvLevelLogger::from_key(logger_name), level)
87 } else {
88 return Err(format!(
89 "cannot parse level for logger '{logger_name}': '{kv_str}'"
90 ));
91 }
92 }
93 _ => {
94 return Err(format!("invalid kv: '{kv_str}'"));
95 }
96 };
97
98 match env_level.entry(logger) {
99 Entry::Occupied(_) => {
100 return Err(format!("specified level multiple times: '{kv_str}'"));
101 }
102 Entry::Vacant(entry) => entry.insert(level),
103 };
104 }
105
106 Ok(env_level)
107 })()
108 .map_err(EnvLevelError::ParseEnvVar)
109}
110
111#[must_use]
112pub(crate) fn logger_level(kind: LoggerKind) -> Option<LevelFilter> {
113 logger_level_inner(ENV_LEVEL.read_expect().as_ref()?, kind)
114}
115
116#[derive(Clone, Eq, PartialEq, Debug)]
117pub(crate) enum LoggerKind<'a> {
118 Default,
119 Other(Option<&'a str>),
120}
121
122#[must_use]
123pub(crate) fn logger_level_inner(env_level: &EnvLevel, kind: LoggerKind) -> Option<LevelFilter> {
124 let level = match kind {
125 LoggerKind::Default => env_level.get(&EnvLevelLogger::Default)?,
126 LoggerKind::Other(logger_name) => env_level
127 .get(&EnvLevelLogger::from_logger(logger_name))
128 .or_else(|| env_level.get(&EnvLevelLogger::AllExceptDefault))?,
129 };
130 Some(*level)
131}
132
133#[cfg(test)]
134mod tests {
135 use super::*;
136 use crate::Level;
137
138 #[test]
139 fn validation() {
140 macro_rules! assert_levels {
141 ($env_level:expr, DEFAULT => $default:expr, UNNAMED => $unnamed:expr, NAMED($name:literal) => $named:expr $(,)?) => {
142 assert_eq!(
143 logger_level_inner(&$env_level, LoggerKind::Default),
144 $default
145 );
146 assert_eq!(
147 logger_level_inner(&$env_level, LoggerKind::Other(None)),
148 $unnamed
149 );
150 assert_eq!(
151 logger_level_inner(&$env_level, LoggerKind::Other(Some($name))),
152 $named
153 );
154 };
155 }
156
157 {
158 let mut env_level = HashMap::new();
159 env_level.insert(
160 EnvLevelLogger::Default,
161 LevelFilter::MoreSevereEqual(Level::Debug),
162 );
163 assert_eq!(from_str_inner("dEBUg").unwrap(), env_level);
164
165 assert_levels!(
166 env_level,
167 DEFAULT => Some(LevelFilter::MoreSevereEqual(Level::Debug)),
168 UNNAMED => None,
169 NAMED("name") => None,
170 );
171 }
172
173 {
174 let mut env_level = HashMap::new();
175 env_level.insert(EnvLevelLogger::Default, LevelFilter::All);
176 env_level.insert(
177 EnvLevelLogger::Unnamed,
178 LevelFilter::MoreSevereEqual(Level::Info),
179 );
180 assert_eq!(from_str_inner("aLl,=inFo").unwrap(), env_level);
181
182 assert_levels!(
183 env_level,
184 DEFAULT => Some(LevelFilter::All),
185 UNNAMED => Some(LevelFilter::MoreSevereEqual(Level::Info)),
186 NAMED("name") => None,
187 );
188 }
189
190 {
191 let mut env_level = HashMap::new();
192 env_level.insert(EnvLevelLogger::Default, LevelFilter::Off);
193 env_level.insert(
194 EnvLevelLogger::Unnamed,
195 LevelFilter::MoreSevereEqual(Level::Info),
196 );
197 env_level.insert(
198 EnvLevelLogger::AllExceptDefault,
199 LevelFilter::MoreSevereEqual(Level::Error),
200 );
201 assert_eq!(from_str_inner("oFf,=iNfo,*=erRor").unwrap(), env_level);
202
203 assert_levels!(
204 env_level,
205 DEFAULT => Some(LevelFilter::Off),
206 UNNAMED => Some(LevelFilter::MoreSevereEqual(Level::Info)),
207 NAMED("name") => Some(LevelFilter::MoreSevereEqual(Level::Error)),
208 );
209 }
210
211 {
212 let mut env_level = HashMap::new();
213 env_level.insert(
214 EnvLevelLogger::Unnamed,
215 LevelFilter::MoreSevereEqual(Level::Warn),
216 );
217 env_level.insert(
218 EnvLevelLogger::Named("name".into()),
219 LevelFilter::MoreSevereEqual(Level::Trace),
220 );
221 assert_eq!(from_str_inner("=wArn,name=trAce").unwrap(), env_level);
222
223 assert_levels!(
224 env_level,
225 DEFAULT => None,
226 UNNAMED => Some(LevelFilter::MoreSevereEqual(Level::Warn)),
227 NAMED("name") => Some(LevelFilter::MoreSevereEqual(Level::Trace)),
228 );
229 }
230
231 {
232 let mut env_level = HashMap::new();
233 env_level.insert(
234 EnvLevelLogger::AllExceptDefault,
235 LevelFilter::MoreSevereEqual(Level::Warn),
236 );
237 env_level.insert(
238 EnvLevelLogger::Named("name".into()),
239 LevelFilter::MoreSevereEqual(Level::Trace),
240 );
241 assert_eq!(from_str_inner("*=wArn,name=trAce").unwrap(), env_level);
242
243 assert_levels!(
244 env_level,
245 DEFAULT => None,
246 UNNAMED => Some(LevelFilter::MoreSevereEqual(Level::Warn)),
247 NAMED("name") => Some(LevelFilter::MoreSevereEqual(Level::Trace)),
248 );
249 }
250
251 {
252 let mut env_level = HashMap::new();
253 env_level.insert(EnvLevelLogger::Default, LevelFilter::All);
254 env_level.insert(EnvLevelLogger::AllExceptDefault, LevelFilter::All);
255 assert_eq!(from_str_inner("all,*=all").unwrap(), env_level);
256
257 assert_levels!(
258 env_level,
259 DEFAULT => Some(LevelFilter::All),
260 UNNAMED => Some(LevelFilter::All),
261 NAMED("name") => Some(LevelFilter::All),
262 );
263 }
264
265 {
266 let mut env_level = HashMap::new();
267 env_level.insert(EnvLevelLogger::Default, LevelFilter::Off);
268 env_level.insert(EnvLevelLogger::AllExceptDefault, LevelFilter::All);
269 assert_eq!(from_str_inner("off,*=all").unwrap(), env_level);
270
271 assert_levels!(
272 env_level,
273 DEFAULT => Some(LevelFilter::Off),
274 UNNAMED => Some(LevelFilter::All),
275 NAMED("name") => Some(LevelFilter::All),
276 );
277 }
278 }
279}