sqlmodel_console/
logging.rs1use std::env;
32use std::sync::atomic::{AtomicBool, AtomicU8, Ordering};
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
36#[repr(u8)]
37pub enum LogLevel {
38 Trace = 0,
40 Debug = 1,
42 Info = 2,
44 Warn = 3,
46 Error = 4,
48 Off = 5,
50}
51
52impl LogLevel {
53 #[must_use]
57 pub fn from_str(s: &str) -> Option<Self> {
58 match s.to_lowercase().as_str() {
59 "trace" => Some(Self::Trace),
60 "debug" => Some(Self::Debug),
61 "info" => Some(Self::Info),
62 "warn" | "warning" => Some(Self::Warn),
63 "error" => Some(Self::Error),
64 "off" | "none" => Some(Self::Off),
65 _ => None,
66 }
67 }
68
69 #[must_use]
71 pub const fn as_str(&self) -> &'static str {
72 match self {
73 Self::Trace => "TRACE",
74 Self::Debug => "DEBUG",
75 Self::Info => "INFO",
76 Self::Warn => "WARN",
77 Self::Error => "ERROR",
78 Self::Off => "OFF",
79 }
80 }
81}
82
83impl std::fmt::Display for LogLevel {
84 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
85 f.write_str(self.as_str())
86 }
87}
88
89static LOGGING_ENABLED: AtomicBool = AtomicBool::new(false);
91static MIN_LOG_LEVEL: AtomicU8 = AtomicU8::new(LogLevel::Info as u8);
92
93#[must_use]
95pub fn is_logging_enabled() -> bool {
96 LOGGING_ENABLED.load(Ordering::Relaxed)
97}
98
99#[must_use]
101pub fn min_log_level() -> LogLevel {
102 match MIN_LOG_LEVEL.load(Ordering::Relaxed) {
103 0 => LogLevel::Trace,
104 1 => LogLevel::Debug,
105 2 => LogLevel::Info,
106 3 => LogLevel::Warn,
107 4 => LogLevel::Error,
108 _ => LogLevel::Off,
109 }
110}
111
112pub fn init_logging() {
117 let enabled = env::var("SQLMODEL_LOG").is_ok_and(|v| {
118 let v = v.to_lowercase();
119 v == "1" || v == "true" || v == "yes" || v == "on"
120 });
121
122 LOGGING_ENABLED.store(enabled, Ordering::Relaxed);
123
124 if let Ok(level_str) = env::var("SQLMODEL_LOG_LEVEL") {
125 if let Some(level) = LogLevel::from_str(&level_str) {
126 MIN_LOG_LEVEL.store(level as u8, Ordering::Relaxed);
127 }
128 }
129}
130
131pub fn enable_logging() {
133 LOGGING_ENABLED.store(true, Ordering::Relaxed);
134}
135
136pub fn disable_logging() {
138 LOGGING_ENABLED.store(false, Ordering::Relaxed);
139}
140
141pub fn set_log_level(level: LogLevel) {
143 MIN_LOG_LEVEL.store(level as u8, Ordering::Relaxed);
144}
145
146pub fn with_logging_enabled<F, R>(f: F) -> R
150where
151 F: FnOnce() -> R,
152{
153 let was_enabled = LOGGING_ENABLED.swap(true, Ordering::Relaxed);
154 let prev_level = MIN_LOG_LEVEL.swap(LogLevel::Trace as u8, Ordering::Relaxed);
155 let result = f();
156 LOGGING_ENABLED.store(was_enabled, Ordering::Relaxed);
157 MIN_LOG_LEVEL.store(prev_level, Ordering::Relaxed);
158 result
159}
160
161#[doc(hidden)]
163pub fn log_impl(level: LogLevel, module: &str, message: &str) {
164 if !is_logging_enabled() {
165 return;
166 }
167 if level < min_log_level() {
168 return;
169 }
170 eprintln!("[sqlmodel-console] [{level}] [{module}] {message}");
171}
172
173#[macro_export]
175macro_rules! log_trace {
176 ($($arg:tt)*) => {
177 $crate::logging::log_impl(
178 $crate::logging::LogLevel::Trace,
179 module_path!(),
180 &format!($($arg)*)
181 )
182 };
183}
184
185#[macro_export]
187macro_rules! log_debug {
188 ($($arg:tt)*) => {
189 $crate::logging::log_impl(
190 $crate::logging::LogLevel::Debug,
191 module_path!(),
192 &format!($($arg)*)
193 )
194 };
195}
196
197#[macro_export]
199macro_rules! log_info {
200 ($($arg:tt)*) => {
201 $crate::logging::log_impl(
202 $crate::logging::LogLevel::Info,
203 module_path!(),
204 &format!($($arg)*)
205 )
206 };
207}
208
209#[macro_export]
211macro_rules! log_warn {
212 ($($arg:tt)*) => {
213 $crate::logging::log_impl(
214 $crate::logging::LogLevel::Warn,
215 module_path!(),
216 &format!($($arg)*)
217 )
218 };
219}
220
221#[macro_export]
223macro_rules! log_error {
224 ($($arg:tt)*) => {
225 $crate::logging::log_impl(
226 $crate::logging::LogLevel::Error,
227 module_path!(),
228 &format!($($arg)*)
229 )
230 };
231}
232
233pub use log_debug;
235pub use log_error;
236pub use log_info;
237pub use log_trace;
238pub use log_warn;
239
240#[cfg(test)]
241mod tests {
242 use super::*;
243
244 #[test]
245 fn test_log_level_ordering() {
246 assert!(LogLevel::Trace < LogLevel::Debug);
247 assert!(LogLevel::Debug < LogLevel::Info);
248 assert!(LogLevel::Info < LogLevel::Warn);
249 assert!(LogLevel::Warn < LogLevel::Error);
250 assert!(LogLevel::Error < LogLevel::Off);
251 }
252
253 #[test]
254 fn test_log_level_from_str() {
255 assert_eq!(LogLevel::from_str("trace"), Some(LogLevel::Trace));
256 assert_eq!(LogLevel::from_str("DEBUG"), Some(LogLevel::Debug));
257 assert_eq!(LogLevel::from_str("Info"), Some(LogLevel::Info));
258 assert_eq!(LogLevel::from_str("WARN"), Some(LogLevel::Warn));
259 assert_eq!(LogLevel::from_str("warning"), Some(LogLevel::Warn));
260 assert_eq!(LogLevel::from_str("error"), Some(LogLevel::Error));
261 assert_eq!(LogLevel::from_str("off"), Some(LogLevel::Off));
262 assert_eq!(LogLevel::from_str("invalid"), None);
263 }
264
265 #[test]
266 fn test_log_level_as_str() {
267 assert_eq!(LogLevel::Trace.as_str(), "TRACE");
268 assert_eq!(LogLevel::Debug.as_str(), "DEBUG");
269 assert_eq!(LogLevel::Info.as_str(), "INFO");
270 assert_eq!(LogLevel::Warn.as_str(), "WARN");
271 assert_eq!(LogLevel::Error.as_str(), "ERROR");
272 assert_eq!(LogLevel::Off.as_str(), "OFF");
273 }
274
275 #[test]
276 fn test_with_logging_enabled() {
277 let was_enabled = is_logging_enabled();
278
279 with_logging_enabled(|| {
280 assert!(is_logging_enabled());
281 });
282
283 assert_eq!(is_logging_enabled(), was_enabled);
284 }
285
286 #[test]
287 fn test_enable_disable_logging() {
288 let original = is_logging_enabled();
289
290 enable_logging();
291 assert!(is_logging_enabled());
292
293 disable_logging();
294 assert!(!is_logging_enabled());
295
296 if original {
298 enable_logging();
299 } else {
300 disable_logging();
301 }
302 }
303
304 #[test]
305 fn test_set_log_level() {
306 let original = min_log_level();
307
308 set_log_level(LogLevel::Debug);
309 assert_eq!(min_log_level(), LogLevel::Debug);
310
311 set_log_level(LogLevel::Error);
312 assert_eq!(min_log_level(), LogLevel::Error);
313
314 set_log_level(original);
316 }
317
318 #[test]
319 fn test_log_macros_compile() {
320 log_trace!("trace message: {}", 42);
323 log_debug!("debug message: {}", "test");
324 log_info!("info message");
325 log_warn!("warn message");
326 log_error!("error message: {:?}", vec![1, 2, 3]);
327 }
328}