Mercurial > crates > nonstick
view src/logging.rs @ 105:13b4d2a19674
Support Rust v1.75.0.
This is the version included in Ubuntu 24.04 LTS and Debian Trixie,
so it's old enough to have wide penetration without being too old
to get new features (Debian Stable, I love you but v1.63 is just
not going to work out).
author | Paul Fisher <paul@pfish.zone> |
---|---|
date | Thu, 26 Jun 2025 00:48:51 -0400 |
parents | dfcd96a74ac4 |
children | e97534be35e3 |
line wrap: on
line source
//! PAM logging variables and macros. //! //! PAM implementations usually include the ability to log to syslog in a way //! that is associated with the log entry itself. This module defines the enums //! and macros for logging. //! //! For more details, see [`PamShared::log`](crate::PamShared::log). //! //! We can't use [the `log` crate](https://docs.rs/log) because that requires //! that any `Log` implementors be `Sync` and `Send`, and a PAM handle //! may be neither. Furthermore, PAM handles are passed to PAM modules in //! dynamic libraries, and `log` doesn't work across dynamic linking boundaries. //! //! A `PamShared` implementation may still use the `log` crate on the backend, //! and may even itself implement `log::Log`, but that interface is not exposed //! to the generic PAM user. #[cfg(feature = "link")] mod levels { pub use internal::*; #[cfg(pam_impl = "linux-pam")] mod internal { use crate::libpam::pam_ffi; pub const ERROR: u32 = pam_ffi::LOG_ERR; pub const WARN: u32 = pam_ffi::LOG_WARNING; pub const INFO: u32 = pam_ffi::LOG_INFO; pub const DEBUG: u32 = pam_ffi::LOG_DEBUG; } #[cfg(pam_impl = "openpam")] mod internal { use crate::libpam::pam_ffi; pub const ERROR: u32 = pam_ffi::PAM_LOG_ERROR; pub const WARN: u32 = pam_ffi::PAM_LOG_NOTICE; pub const INFO: u32 = pam_ffi::PAM_LOG_VERBOSE; pub const DEBUG: u32 = pam_ffi::PAM_LOG_DEBUG; } } #[cfg(not(feature = "link"))] mod levels { pub const ERROR: u32 = 2255887; pub const WARN: u32 = 7265000; pub const INFO: u32 = 7762323; pub const DEBUG: u32 = 8675309; } /// An entry to be added to the log. /// /// The levels are in descending order of importance and correspond roughly /// to the similarly-named levels in the `log` crate. /// /// In all implementations, these are ordered such that `Error`, `Warning`, /// `Info`, and `Debug` are in ascending order. #[derive(Debug, PartialEq, Ord, PartialOrd, Eq)] #[repr(u32)] pub enum Level { Error = levels::ERROR, Warning = levels::WARN, Info = levels::INFO, Debug = levels::DEBUG, } /// 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)+)); } } /// Logs a message at error level via the given PAM handle. /// /// This supports `format!`-style formatting. /// /// # Example /// /// ```no_run /// # fn _test(pam_handle: impl nonstick::PamShared) { /// # let load_error = "xxx"; /// nonstick::error!( /// pam_handle, /// "error loading data from data source: {load_error}" /// ); /// // Will log a message like "error loading data from data source: timed out" /// // at ERROR level on syslog. /// # } /// ``` #[macro_export] macro_rules! error { ($handle:expr, $($arg:tt)+) => { $crate::__log_internal!($handle, Error, $($arg)+);}} /// Logs a message at warning level via the given PAM handle. /// /// This supports `format!`-style formatting. /// /// # Example /// /// ```no_run /// # fn _test(pam_handle: impl nonstick::PamShared) { /// # let (start, finish) = (0, 0); /// nonstick::warn!( /// pam_handle, /// "loading took too long: {latency_ms} ms", /// latency_ms = start - finish /// ); /// // Will log a message like "loading took too long: 495 ms" /// // at WARN level on syslog. /// # } /// ``` #[macro_export] macro_rules! warn { ($handle:expr, $($arg:tt)+) => { $crate::__log_internal!($handle, Warning, $($arg)+);}} /// Logs a message at info level via the given PAM handle. /// /// This supports `format!`-style formatting. /// /// # Example /// /// ```no_run /// # fn _test(pam_handle: impl nonstick::PamShared) { /// nonstick::info!(pam_handle, "using remote backend to load user data"); /// // Will log a message like "using remote backend to load user data" /// // at INFO level on syslog. /// # } /// ``` #[macro_export] macro_rules! info { ($handle:expr, $($arg:tt)+) => { $crate::__log_internal!($handle, Info, $($arg)+);}} /// Logs a message at debug level via the given PAM handle. /// /// This level specially includes file/line/column information. /// This supports `format!`-style formatting. /// /// # Example /// /// ```no_run /// # fn _test(pam_handle: impl nonstick::PamShared) { /// # 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/" /// // 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)+), ); }} #[cfg(test)] mod tests { use super::*; use regex::Regex; use std::cell::RefCell; #[test] fn test_order() { assert!(Level::Error < Level::Warning); assert!(Level::Warning < Level::Info); assert!(Level::Info < Level::Debug); } #[test] fn test_logging() { struct Logger(RefCell<Vec<(Level, String)>>); impl Logger { fn log(&self, level: Level, text: &str) { self.0.borrow_mut().push((level, text.to_owned())) } } let logger = Logger(Default::default()); let something = Level::Error; error!(logger, "here is another thing: {}", 99); warn!(logger, "watch out!"); 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:?}" ); 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()), ], logged ); } }