Mercurial > crates > nonstick
comparison src/logging.rs @ 116:a12706e42c9d default tip
Logging, macros, and building:
- Changes logging API to accept the `Location` of the log statement.
Fixes OpenPAM implementation.
- Stops publicly exporting doc macros.
- Uses dlopen to detect the PAM library rather than header jankery.
author | Paul Fisher <paul@pfish.zone> |
---|---|
date | Sun, 29 Jun 2025 18:27:51 -0400 |
parents | 1e11a52b4665 |
children |
comparison
equal
deleted
inserted
replaced
115:1e11a52b4665 | 116:a12706e42c9d |
---|---|
36 /// The levels are in descending order of importance and correspond roughly | 36 /// The levels are in descending order of importance and correspond roughly |
37 /// to the similarly-named levels in the `log` crate. | 37 /// to the similarly-named levels in the `log` crate. |
38 /// | 38 /// |
39 /// Their values are ordered monotonically, either increasing or decreasing, | 39 /// Their values are ordered monotonically, either increasing or decreasing, |
40 /// depending upon the implementation. | 40 /// depending upon the implementation. |
41 #[derive(Debug, PartialEq, Eq)] | 41 #[derive(Clone, Copy, Debug, PartialEq, Eq)] |
42 #[repr(i32)] | 42 #[repr(i32)] |
43 pub enum Level { | 43 pub enum Level { |
44 Error = levels::ERROR, | 44 Error = levels::ERROR, |
45 Warning = levels::WARN, | 45 Warning = levels::WARN, |
46 Info = levels::INFO, | 46 Info = levels::INFO, |
47 Debug = levels::DEBUG, | 47 Debug = levels::DEBUG, |
48 } | 48 } |
49 | 49 |
50 /// The location of a log entry. | |
51 #[derive(Clone, Copy, Debug, Default)] | |
52 pub struct Location<'a> { | |
53 pub file: &'a str, | |
54 pub line: u32, | |
55 pub function: &'a str, | |
56 _more: (), | |
57 } | |
58 | |
59 impl<'a> Location<'a> { | |
60 pub fn new(file: &'a str, line: u32, function: &'a str) -> Self { | |
61 Self {file, line, function, _more: ()} | |
62 } | |
63 } | |
64 | |
65 /// The [`Location`] where this macro is inserted. | |
66 #[doc(hidden)] | |
67 #[macro_export] | |
68 macro_rules! location { | |
69 () => { $crate::logging::Location::new(file!(), line!(), $crate::__function!()) } | |
70 } | |
71 | |
50 /// Here's the guts of the logger thingy. You shouldn't be using this! | 72 /// Here's the guts of the logger thingy. You shouldn't be using this! |
51 #[doc(hidden)] | 73 #[doc(hidden)] |
52 #[macro_export] | 74 #[macro_export] |
53 macro_rules! __log_internal { | 75 macro_rules! __log_internal { |
54 ($handle:expr, $level:ident, $($arg:tt)+) => { | 76 ($handle:expr, $level:ident, $($arg:tt)+) => { |
55 $handle.log($crate::logging::Level::$level, &format!($($arg)+)); | 77 $handle.log($crate::logging::Level::$level, $crate::location!(), &format!($($arg)+)); |
56 } | 78 } |
79 } | |
80 | |
81 /// Ugly, hacky macro to get the current function name. | |
82 /// | |
83 /// [Stolen from Stack Overflow.][https://stackoverflow.com/a/40234666/39808] | |
84 #[doc(hidden)] | |
85 #[macro_export] | |
86 macro_rules! __function { | |
87 () => {{ | |
88 fn p() {} | |
89 fn f<T>(_: T) -> &'static str { | |
90 ::std::any::type_name::<T>() | |
91 } | |
92 f(p).trim_end_matches("::p") | |
93 }} | |
57 } | 94 } |
58 | 95 |
59 /// Logs a message at error level via the given PAM handle. | 96 /// Logs a message at error level via the given PAM handle. |
60 /// | 97 /// |
61 /// This supports `format!`-style formatting. | 98 /// This supports `format!`-style formatting. |
123 /// ```no_run | 160 /// ```no_run |
124 /// # fn _test(pam_handle: impl nonstick::PamShared) { | 161 /// # fn _test(pam_handle: impl nonstick::PamShared) { |
125 /// # let userinfo_url = "https://zombo.com/"; | 162 /// # let userinfo_url = "https://zombo.com/"; |
126 /// nonstick::debug!(pam_handle, "making HTTP GET request to {userinfo_url}"); | 163 /// nonstick::debug!(pam_handle, "making HTTP GET request to {userinfo_url}"); |
127 /// // Will log a message like | 164 /// // Will log a message like |
128 /// // "pam_http/lib.rs:39:14: making HTTP GET request to https://zombo.com/" | 165 /// // making HTTP GET request to https://zombo.com/" |
129 /// // at DEBUG level on syslog. | 166 /// // at DEBUG level on syslog. |
130 /// # } | 167 /// # } |
131 /// ``` | 168 /// ``` |
132 #[macro_export] | 169 #[macro_export] |
133 macro_rules! debug {($handle:expr, $($arg:tt)+) => { | 170 macro_rules! debug { ($handle:expr, $($arg:tt)+) => { $crate::__log_internal!($handle, Debug, $($arg)+);}} |
134 $crate::__log_internal!( | |
135 $handle, Debug, | |
136 "{}:{}:{}: {}", file!(), line!(), column!(), format_args!($($arg)+), | |
137 ); | |
138 }} | |
139 | 171 |
140 #[cfg(test)] | 172 #[cfg(test)] |
141 mod tests { | 173 mod tests { |
142 use super::*; | 174 use super::*; |
143 use regex::Regex; | 175 use regex::Regex; |
146 #[test] | 178 #[test] |
147 fn test_logging() { | 179 fn test_logging() { |
148 struct Logger(RefCell<Vec<(Level, String)>>); | 180 struct Logger(RefCell<Vec<(Level, String)>>); |
149 | 181 |
150 impl Logger { | 182 impl Logger { |
151 fn log(&self, level: Level, text: &str) { | 183 fn log(&self, level: Level, loc: Location<'_>, text: &str) { |
152 self.0.borrow_mut().push((level, text.to_owned())) | 184 self.0.borrow_mut().push((level, text.to_owned())) |
153 } | 185 } |
154 } | 186 } |
155 | 187 |
156 let logger = Logger(Default::default()); | 188 let logger = Logger(Default::default()); |
159 error!(logger, "here is another thing: {}", 99); | 191 error!(logger, "here is another thing: {}", 99); |
160 warn!(logger, "watch out!"); | 192 warn!(logger, "watch out!"); |
161 info!(logger, "here is some info: {info}", info = "information"); | 193 info!(logger, "here is some info: {info}", info = "information"); |
162 debug!(logger, "here is something: {something:?}"); | 194 debug!(logger, "here is something: {something:?}"); |
163 | 195 |
164 let mut logged = logger.0.into_inner(); | 196 let logged = logger.0.into_inner(); |
165 | |
166 let (last_level, last_string) = logged.pop().unwrap(); | |
167 assert_eq!(Level::Debug, last_level); | |
168 let expr = Regex::new(r"^[^:]+:\d+:\d+: here is something: Error$").unwrap(); | |
169 assert!( | |
170 expr.is_match(&last_string), | |
171 "{last_string:?} did not match {expr:?}" | |
172 ); | |
173 | 197 |
174 assert_eq!( | 198 assert_eq!( |
175 vec![ | 199 vec![ |
176 (Level::Error, "here is another thing: 99".to_owned()), | 200 (Level::Error, "here is another thing: 99".to_owned()), |
177 (Level::Warning, "watch out!".to_owned()), | 201 (Level::Warning, "watch out!".to_owned()), |
178 (Level::Info, "here is some info: information".to_owned()), | 202 (Level::Info, "here is some info: information".to_owned()), |
203 (Level::Debug, "here is something: Error".to_owned()), | |
179 ], | 204 ], |
180 logged | 205 logged |
181 ); | 206 ); |
182 } | 207 } |
183 } | 208 } |