diff 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
line wrap: on
line diff
--- a/src/logging.rs	Sun Jun 29 03:35:59 2025 -0400
+++ b/src/logging.rs	Sun Jun 29 18:27:51 2025 -0400
@@ -38,7 +38,7 @@
 ///
 /// Their values are ordered monotonically, either increasing or decreasing,
 /// depending upon the implementation.
-#[derive(Debug, PartialEq, Eq)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 #[repr(i32)]
 pub enum Level {
     Error = levels::ERROR,
@@ -47,15 +47,52 @@
     Debug = levels::DEBUG,
 }
 
+/// The location of a log entry.
+#[derive(Clone, Copy, Debug, Default)]
+pub struct Location<'a> {
+    pub file: &'a str,
+    pub line: u32,
+    pub function: &'a str,
+    _more: (),
+}
+
+impl<'a> Location<'a> {
+    pub fn new(file: &'a str, line: u32, function: &'a str) -> Self {
+        Self {file, line, function, _more: ()}
+    }
+}
+
+/// The [`Location`] where this macro is inserted.
+#[doc(hidden)]
+#[macro_export]
+macro_rules! location {
+    () => { $crate::logging::Location::new(file!(), line!(), $crate::__function!()) }
+}
+
 /// Here's the guts of the logger thingy. You shouldn't be using this!
 #[doc(hidden)]
 #[macro_export]
 macro_rules! __log_internal {
     ($handle:expr, $level:ident, $($arg:tt)+) => {
-        $handle.log($crate::logging::Level::$level, &format!($($arg)+));
+        $handle.log($crate::logging::Level::$level, $crate::location!(), &format!($($arg)+));
     }
 }
 
+/// Ugly, hacky macro to get the current function name.
+///
+/// [Stolen from Stack Overflow.][https://stackoverflow.com/a/40234666/39808]
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __function {
+    () => {{
+        fn p() {}
+        fn f<T>(_: T) -> &'static str {
+            ::std::any::type_name::<T>()
+        }
+        f(p).trim_end_matches("::p")
+    }}
+}
+
 /// Logs a message at error level via the given PAM handle.
 ///
 /// This supports `format!`-style formatting.
@@ -125,17 +162,12 @@
 /// # let userinfo_url = "https://zombo.com/";
 /// nonstick::debug!(pam_handle, "making HTTP GET request to {userinfo_url}");
 /// // Will log a message like
-/// // "pam_http/lib.rs:39:14: making HTTP GET request to https://zombo.com/"
+/// // making HTTP GET request to https://zombo.com/"
 /// // at DEBUG level on syslog.
 /// # }
 /// ```
 #[macro_export]
-macro_rules! debug {($handle:expr, $($arg:tt)+) => {
-    $crate::__log_internal!(
-        $handle, Debug,
-        "{}:{}:{}: {}", file!(), line!(), column!(), format_args!($($arg)+),
-    );
-}}
+macro_rules! debug { ($handle:expr, $($arg:tt)+) => { $crate::__log_internal!($handle, Debug, $($arg)+);}}
 
 #[cfg(test)]
 mod tests {
@@ -148,7 +180,7 @@
         struct Logger(RefCell<Vec<(Level, String)>>);
 
         impl Logger {
-            fn log(&self, level: Level, text: &str) {
+            fn log(&self, level: Level, loc: Location<'_>, text: &str) {
                 self.0.borrow_mut().push((level, text.to_owned()))
             }
         }
@@ -161,21 +193,14 @@
         info!(logger, "here is some info: {info}", info = "information");
         debug!(logger, "here is something: {something:?}");
 
-        let mut logged = logger.0.into_inner();
-
-        let (last_level, last_string) = logged.pop().unwrap();
-        assert_eq!(Level::Debug, last_level);
-        let expr = Regex::new(r"^[^:]+:\d+:\d+: here is something: Error$").unwrap();
-        assert!(
-            expr.is_match(&last_string),
-            "{last_string:?} did not match {expr:?}"
-        );
+        let logged = logger.0.into_inner();
 
         assert_eq!(
             vec![
                 (Level::Error, "here is another thing: 99".to_owned()),
                 (Level::Warning, "watch out!".to_owned()),
                 (Level::Info, "here is some info: information".to_owned()),
+                (Level::Debug, "here is something: Error".to_owned()),
             ],
             logged
         );