diff --git a/src/async_client.rs b/src/async_client.rs index a36c3fd..1cadb7a 100644 --- a/src/async_client.rs +++ b/src/async_client.rs @@ -70,7 +70,7 @@ use std::{ ptr, slice, str, sync::{ atomic::{AtomicU32, Ordering}, - Arc, Mutex, + Arc, Mutex, Once, }, time::Duration, }; @@ -135,6 +135,9 @@ struct CallbackContext { on_message_arrived: Option>, } +// Runs code to initialize the underlying C library +static C_LIB_INIT: Once = Once::new(); + impl AsyncClient { /// Creates a new MQTT client which can connect to an MQTT broker. /// @@ -146,6 +149,18 @@ impl AsyncClient { where T: Into, { + // Do any initialization of the C lib + C_LIB_INIT.call_once(|| { + if let Some(lvl) = crate::paho_c_trace_level() { + debug!("Setting Paho C log level to {}", lvl); + unsafe { + ffi::MQTTAsync_setTraceCallback(Some(Self::on_trace)); + ffi::MQTTAsync_setTraceLevel(lvl); + } + } + }); + + // Create the client let mut opts = opts.into(); debug!("Create options: {:?}", opts); @@ -230,6 +245,41 @@ impl AsyncClient { self.inner.handle } + // Low-level callback from the C library for log/trace messages. + // We send the + unsafe extern "C" fn on_trace(lvl: ffi::MQTTASYNC_TRACE_LEVELS, msg: *mut c_char) { + if msg.is_null() { + return; + } + + let cmsg = CStr::from_ptr(msg); + let msg = match cmsg.to_str() { + Ok(s) => s, + Err(_) => return, + }; + + let lvl = match lvl { + ffi::MQTTASYNC_TRACE_LEVELS_MQTTASYNC_TRACE_FATAL | + ffi::MQTTASYNC_TRACE_LEVELS_MQTTASYNC_TRACE_SEVERE => + log::Level::Error, + + ffi::MQTTASYNC_TRACE_LEVELS_MQTTASYNC_TRACE_ERROR => + log::Level::Warn, + + ffi::MQTTASYNC_TRACE_LEVELS_MQTTASYNC_TRACE_PROTOCOL => + log::Level::Info, + + ffi::MQTTASYNC_TRACE_LEVELS_MQTTASYNC_TRACE_MINIMUM | + ffi::MQTTASYNC_TRACE_LEVELS_MQTTASYNC_TRACE_MEDIUM => + log::Level::Debug, + + ffi::MQTTASYNC_TRACE_LEVELS_MQTTASYNC_TRACE_MAXIMUM | _ => + log::Level::Trace, + }; + + log!(target: "paho_mqtt_c", lvl, "{}", msg); + } + // Low-level callback from the C library when the client is connected. // We just pass the call on to the handler registered with the client, if any. unsafe extern "C" fn on_connected(context: *mut c_void, _cause: *mut c_char) { diff --git a/src/lib.rs b/src/lib.rs index 31194b3..f2b6cf5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -145,6 +145,68 @@ pub fn from_c_bool(on: c_int) -> bool { on != 0 } +/// The log target (module) for the Paho C trace logs +const PAHO_C_LOG_TARGET: &str = "paho_mqtt_c"; + +/// Converts a Paho C trace level into a Rust log level. +pub fn from_c_trace_level(level: ffi::MQTTASYNC_TRACE_LEVELS) -> log::Level { + match level { + ffi::MQTTASYNC_TRACE_LEVELS_MQTTASYNC_TRACE_FATAL | + ffi::MQTTASYNC_TRACE_LEVELS_MQTTASYNC_TRACE_SEVERE => + log::Level::Error, + + ffi::MQTTASYNC_TRACE_LEVELS_MQTTASYNC_TRACE_ERROR => + log::Level::Warn, + + ffi::MQTTASYNC_TRACE_LEVELS_MQTTASYNC_TRACE_PROTOCOL => + log::Level::Info, + + ffi::MQTTASYNC_TRACE_LEVELS_MQTTASYNC_TRACE_MINIMUM | + ffi::MQTTASYNC_TRACE_LEVELS_MQTTASYNC_TRACE_MEDIUM => + log::Level::Debug, + + ffi::MQTTASYNC_TRACE_LEVELS_MQTTASYNC_TRACE_MAXIMUM | _ => + log::Level::Trace, + } +} + +/// Converts a Rust log level filer into a Paho C trace level. +/// This gives the most verbose C trace level for the log level. +pub fn log_into_c_trace_level(level: log::LevelFilter) -> Option { + use log::LevelFilter::*; + match level { + Off => None, + Error => Some(ffi::MQTTASYNC_TRACE_LEVELS_MQTTASYNC_TRACE_SEVERE), + Warn => Some(ffi::MQTTASYNC_TRACE_LEVELS_MQTTASYNC_TRACE_ERROR), + Info => Some(ffi::MQTTASYNC_TRACE_LEVELS_MQTTASYNC_TRACE_PROTOCOL), + Debug => Some(ffi::MQTTASYNC_TRACE_LEVELS_MQTTASYNC_TRACE_MEDIUM), + Trace => Some(ffi::MQTTASYNC_TRACE_LEVELS_MQTTASYNC_TRACE_MAXIMUM), + } +} + +/// Gets the trace level, if any, to set the Paho C library +pub fn paho_c_trace_level() -> Option { + use log::Level::*; + if log_enabled!(target: PAHO_C_LOG_TARGET, Trace) { + Some(ffi::MQTTASYNC_TRACE_LEVELS_MQTTASYNC_TRACE_MAXIMUM) + } + else if log_enabled!(target: PAHO_C_LOG_TARGET, Debug) { + Some(ffi::MQTTASYNC_TRACE_LEVELS_MQTTASYNC_TRACE_MEDIUM) + } + else if log_enabled!(target: PAHO_C_LOG_TARGET, Info) { + Some(ffi::MQTTASYNC_TRACE_LEVELS_MQTTASYNC_TRACE_PROTOCOL) + } + else if log_enabled!(target: PAHO_C_LOG_TARGET, Warn) { + Some(ffi::MQTTASYNC_TRACE_LEVELS_MQTTASYNC_TRACE_ERROR) + } + else if log_enabled!(target: PAHO_C_LOG_TARGET, Error) { + Some(ffi::MQTTASYNC_TRACE_LEVELS_MQTTASYNC_TRACE_SEVERE) + } + else { + None + } +} + ///////////////////////////////////////////////////////////////////////////// // Unit Tests /////////////////////////////////////////////////////////////////////////////