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 }