diff options
author | Chelsea Holland Komlo <me@chelseakomlo.com> | 2017-11-27 22:59:54 -0500 |
---|---|---|
committer | Chelsea Holland Komlo <me@chelseakomlo.com> | 2017-12-21 15:29:33 -0500 |
commit | 3dfe8e6522460817100582a33a382be3c3efd988 (patch) | |
tree | bfb60d07ac6e4d66fded35794980dd268bc7f2eb /src/rust/tor_log | |
parent | 719db28f54ad1fa957999f2a6256e07bdb412e4f (diff) | |
download | tor-3dfe8e6522460817100582a33a382be3c3efd988.tar.gz tor-3dfe8e6522460817100582a33a382be3c3efd988.zip |
add minimal rust module for logging to tor's logger
Allows an optional no-op for testing purposes
Diffstat (limited to 'src/rust/tor_log')
-rw-r--r-- | src/rust/tor_log/Cargo.toml | 18 | ||||
-rw-r--r-- | src/rust/tor_log/lib.rs | 17 | ||||
-rw-r--r-- | src/rust/tor_log/tor_log.rs | 236 |
3 files changed, 271 insertions, 0 deletions
diff --git a/src/rust/tor_log/Cargo.toml b/src/rust/tor_log/Cargo.toml new file mode 100644 index 0000000000..f31d27b045 --- /dev/null +++ b/src/rust/tor_log/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "tor_log" +version = "0.1.0" +authors = ["The Tor Project"] + +[lib] +name = "tor_log" +path = "lib.rs" +crate_type = ["rlib", "staticlib"] + +[features] +testing = [] + +[dependencies] +libc = "0.2.22" + +[dependencies.tor_allocate] +path = "../tor_allocate" diff --git a/src/rust/tor_log/lib.rs b/src/rust/tor_log/lib.rs new file mode 100644 index 0000000000..915910d003 --- /dev/null +++ b/src/rust/tor_log/lib.rs @@ -0,0 +1,17 @@ +//! Copyright (c) 2016-2017, The Tor Project, Inc. */ +//! See LICENSE for licensing information */ + +//! Logging wrapper for Rust to utilize Tor's logger, found at +//! src/common/log.c and src/common/torlog.h +//! +//! Exposes different interfaces depending on whether we are running in test +//! or non-test mode. When testing, we use a no-op implementation, +//! otherwise we link directly to C. + +extern crate libc; +extern crate tor_allocate; + +mod tor_log; + +pub use tor_log::*; +pub use tor_log::log::*; diff --git a/src/rust/tor_log/tor_log.rs b/src/rust/tor_log/tor_log.rs new file mode 100644 index 0000000000..394e232442 --- /dev/null +++ b/src/rust/tor_log/tor_log.rs @@ -0,0 +1,236 @@ +// Copyright (c) 2016-2017, The Tor Project, Inc. */ +// See LICENSE for licensing information */ + + +/// The related domain which the logging message is relevant. For example, +/// log messages relevant to networking would use LogDomain::LdNet, whereas +/// general messages can use LdGeneral. +#[derive(Eq, PartialEq)] +pub enum LogDomain { + LdNet, + LdGeneral, +} + +/// The severity level at which to log messages. +#[derive(Eq, PartialEq)] +pub enum LogSeverity { + Notice, + Warn, +} + +/// Main entry point for Rust modules to log messages. +/// +/// # Inputs +/// +/// * A `severity` of type LogSeverity, which defines the level of severity the +/// message will be logged. +/// * A `domain` of type LogDomain, which defines the domain the log message +/// will be associated with. +/// * A `function` of type &str, which defines the name of the function where +/// the message is being logged. There is a current RFC for a macro that +/// defines function names. When it is, we should use it. See +/// https://github.com/rust-lang/rfcs/pull/1719 +/// * A `message` of type &str, which is the log message itself. +#[macro_export] +macro_rules! tor_log_msg { + ($severity: path, + $domain: path, + $function: expr, + $($message:tt)*) => + { + { + use std::ffi::CString; + + /// Default function name to log in case of errors when converting + /// a function name to a CString + const ERR_LOG_FUNCTION: &'static str = "tor_log_msg"; + + /// Default message to log in case of errors when converting a log + /// message to a CString + const ERR_LOG_MSG: &'static str = "Unable to log message from Rust + module due to error when converting to CString"; + + let func = match CString::new($function) { + Ok(n) => n, + Err(_) => CString::new(ERR_LOG_FUNCTION).unwrap(), + }; + + let msg = match CString::new(format!($($message)*)) { + Ok(n) => n, + Err(_) => CString::new(ERR_LOG_MSG).unwrap(), + }; + + let func_ptr = func.as_ptr(); + let msg_ptr = msg.as_ptr(); + + unsafe { + tor_log_string(translate_severity($severity), + translate_domain($domain), + func_ptr, msg_ptr + ) + } + } + }; +} + +/// This module exposes no-op functionality purely for the purpose of testing +/// Rust at the module level. +#[cfg(any(test, feature = "testing"))] +pub mod log { + use libc::{c_char, c_int}; + use super::LogDomain; + use super::LogSeverity; + + /// Expose a no-op logging interface purely for testing Rust modules at the + /// module level. + pub fn tor_log_string<'a>( + severity: c_int, + domain: u32, + function: *const c_char, + message: *const c_char, + ) -> (c_int, u32, String, String) { + use std::ffi::CStr; + + let func = unsafe { CStr::from_ptr(function) }.to_str().unwrap(); + let func_allocated = String::from(func); + + let msg = unsafe { CStr::from_ptr(message) }.to_str().unwrap(); + let msg_allocated = String::from(msg); + (severity, domain, func_allocated, msg_allocated) + } + + pub unsafe fn translate_domain(_domain: LogDomain) -> u32 { + 1 + } + + pub unsafe fn translate_severity(_severity: LogSeverity) -> c_int { + 1 + } +} + +/// This implementation is used when compiling for actual use, as opposed to +/// testing. +#[cfg(all(not(test), not(feature = "testing")))] +pub mod log { + use libc::{c_char, c_int}; + use super::LogDomain; + use super::LogSeverity; + + /// Severity log types. These mirror definitions in /src/common/torlog.h + /// C_RUST_COUPLED: src/common/log.c, log domain types + extern "C" { + #[no_mangle] + static _LOG_WARN: c_int; + static _LOG_NOTICE: c_int; + } + + /// Domain log types. These mirror definitions in /src/common/torlog.h + /// C_RUST_COUPLED: src/common/log.c, log severity types + extern "C" { + #[no_mangle] + static _LD_NET: u32; + static _LD_GENERAL: u32; + } + + /// Translate Rust defintions of log domain levels to C. This exposes a 1:1 + /// mapping between types. + /// + /// Allow for default cases in case Rust and C log types get out of sync + #[allow(unreachable_patterns)] + pub unsafe fn translate_domain(domain: LogDomain) -> u32 { + match domain { + LogDomain::LdNet => _LD_NET, + LogDomain::LdGeneral => _LD_GENERAL, + _ => _LD_GENERAL, + } + } + + /// Translate Rust defintions of log severity levels to C. This exposes a + /// 1:1 mapping between types. + /// + /// Allow for default cases in case Rust and C log types get out of sync + #[allow(unreachable_patterns)] + pub unsafe fn translate_severity(severity: LogSeverity) -> c_int { + match severity { + LogSeverity::Warn => _LOG_WARN, + LogSeverity::Notice => _LOG_NOTICE, + _ => _LOG_NOTICE, + } + } + + /// The main entry point into Tor's logger. When in non-test mode, this + /// will link directly with `tor_log_string` in /src/or/log.c + extern "C" { + pub fn tor_log_string( + severity: c_int, + domain: u32, + function: *const c_char, + string: *const c_char, + ); + } +} + +#[cfg(test)] +mod test { + use tor_log::*; + use tor_log::log::*; + + use libc::c_int; + + #[test] + fn test_get_log_message() { + + fn test_macro<'a>() -> (c_int, u32, String, String) { + let (x, y, z, a) = + tor_log_msg!( + LogSeverity::Warn, + LogDomain::LdNet, + "test_macro", + "test log message {}", + "a", + ); + (x, y, z, a) + } + + let (severity, domain, function_name, log_msg) = test_macro(); + + let expected_severity = + unsafe { translate_severity(LogSeverity::Warn) }; + assert_eq!(severity, expected_severity); + + let expected_domain = unsafe { translate_domain(LogDomain::LdNet) }; + assert_eq!(domain, expected_domain); + + assert_eq!("test_macro", function_name); + assert_eq!("test log message a", log_msg); + } + + #[test] + fn test_get_log_message_multiple_values() { + fn test_macro<'a>() -> (c_int, u32, String, String) { + let (x, y, z, a) = tor_log_msg!( + LogSeverity::Warn, + LogDomain::LdNet, + "test_macro 2", + "test log message {} {} {} {}", + 10, + 9, + 8, + 7 + ); + (x, y, z, a) + } + + let (severity, domain, function_name, log_msg) = test_macro(); + + let expected_severity = + unsafe { translate_severity(LogSeverity::Warn) }; + assert_eq!(severity, expected_severity); + + let expected_domain = unsafe { translate_domain(LogDomain::LdNet) }; + assert_eq!(domain, expected_domain); + + assert_eq!("test_macro 2", function_name); + assert_eq!("test log message 10 9 8 7", log_msg); + } +} |