aboutsummaryrefslogtreecommitdiff
path: root/src/rust/tor_log
diff options
context:
space:
mode:
authorChelsea Holland Komlo <me@chelseakomlo.com>2017-11-27 22:59:54 -0500
committerChelsea Holland Komlo <me@chelseakomlo.com>2017-12-21 15:29:33 -0500
commit3dfe8e6522460817100582a33a382be3c3efd988 (patch)
treebfb60d07ac6e4d66fded35794980dd268bc7f2eb /src/rust/tor_log
parent719db28f54ad1fa957999f2a6256e07bdb412e4f (diff)
downloadtor-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.toml18
-rw-r--r--src/rust/tor_log/lib.rs17
-rw-r--r--src/rust/tor_log/tor_log.rs236
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);
+ }
+}