|
@@ -0,0 +1,270 @@
|
|
|
+// Copyright (c) 2016-2017, The Tor Project, Inc. */
|
|
|
+// See LICENSE for licensing information */
|
|
|
+
|
|
|
+// Note that these functions are untested due to the fact that there are no
|
|
|
+// return variables to test and they are calling into a C API.
|
|
|
+
|
|
|
+/// 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 {
|
|
|
+ Net,
|
|
|
+ General,
|
|
|
+}
|
|
|
+
|
|
|
+/// 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)*) =>
|
|
|
+ {
|
|
|
+ {
|
|
|
+ let msg = format!($($message)*);
|
|
|
+ $crate::tor_log_msg_impl($severity, $domain, $function, msg)
|
|
|
+ }
|
|
|
+ };
|
|
|
+}
|
|
|
+
|
|
|
+#[inline]
|
|
|
+pub fn tor_log_msg_impl(
|
|
|
+ severity: LogSeverity,
|
|
|
+ domain: LogDomain,
|
|
|
+ function: &str,
|
|
|
+ message: String,
|
|
|
+) {
|
|
|
+ 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: &str = "tor_log_msg";
|
|
|
+
|
|
|
+ /// Default message to log in case of errors when converting a log
|
|
|
+ /// message to a CString
|
|
|
+ const ERR_LOG_MSG: &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(message) {
|
|
|
+ Ok(n) => n,
|
|
|
+ Err(_) => CString::new(ERR_LOG_MSG).unwrap(),
|
|
|
+ };
|
|
|
+
|
|
|
+ // Bind to a local variable to preserve ownership. This is essential so
|
|
|
+ // that ownership is guaranteed until these local variables go out of scope
|
|
|
+ let func_ptr = func.as_ptr();
|
|
|
+ let msg_ptr = msg.as_ptr();
|
|
|
+
|
|
|
+ let c_severity = unsafe { log::translate_severity(severity) };
|
|
|
+ let c_domain = unsafe { log::translate_domain(domain) };
|
|
|
+
|
|
|
+ unsafe { log::tor_log_string(c_severity, c_domain, func_ptr, msg_ptr) }
|
|
|
+}
|
|
|
+
|
|
|
+/// 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" {
|
|
|
+ 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" {
|
|
|
+ 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.
|
|
|
+ #[inline]
|
|
|
+ pub unsafe fn translate_domain(domain: LogDomain) -> u32 {
|
|
|
+ match domain {
|
|
|
+ LogDomain::Net => LD_NET_,
|
|
|
+ LogDomain::General => LD_GENERAL_,
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Translate Rust defintions of log severity levels to C. This exposes a
|
|
|
+ /// 1:1 mapping between types.
|
|
|
+ #[inline]
|
|
|
+ pub unsafe fn translate_severity(severity: LogSeverity) -> c_int {
|
|
|
+ match severity {
|
|
|
+ LogSeverity::Warn => LOG_WARN_,
|
|
|
+ LogSeverity::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,
|
|
|
+ );
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/// This module exposes no-op functionality for testing other Rust modules
|
|
|
+/// without linking to C.
|
|
|
+#[cfg(any(test, feature = "testing"))]
|
|
|
+pub mod log {
|
|
|
+ use libc::{c_char, c_int};
|
|
|
+ use super::LogDomain;
|
|
|
+ use super::LogSeverity;
|
|
|
+
|
|
|
+ pub static mut LAST_LOGGED_FUNCTION: *mut String = 0 as *mut String;
|
|
|
+ pub static mut LAST_LOGGED_MESSAGE: *mut String = 0 as *mut String;
|
|
|
+
|
|
|
+ pub unsafe fn tor_log_string(
|
|
|
+ _severity: c_int,
|
|
|
+ _domain: u32,
|
|
|
+ function: *const c_char,
|
|
|
+ message: *const c_char,
|
|
|
+ ) {
|
|
|
+ use std::ffi::CStr;
|
|
|
+
|
|
|
+ let f = CStr::from_ptr(function);
|
|
|
+ let fct = match f.to_str() {
|
|
|
+ Ok(n) => n,
|
|
|
+ Err(_) => "",
|
|
|
+ };
|
|
|
+ LAST_LOGGED_FUNCTION = Box::into_raw(Box::new(String::from(fct)));
|
|
|
+
|
|
|
+ let m = CStr::from_ptr(message);
|
|
|
+ let msg = match m.to_str() {
|
|
|
+ Ok(n) => n,
|
|
|
+ Err(_) => "",
|
|
|
+ };
|
|
|
+ LAST_LOGGED_MESSAGE = Box::into_raw(Box::new(String::from(msg)));
|
|
|
+ }
|
|
|
+
|
|
|
+ pub unsafe fn translate_domain(_domain: LogDomain) -> u32 {
|
|
|
+ 1
|
|
|
+ }
|
|
|
+
|
|
|
+ pub unsafe fn translate_severity(_severity: LogSeverity) -> c_int {
|
|
|
+ 1
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+#[cfg(test)]
|
|
|
+mod test {
|
|
|
+ use tor_log::*;
|
|
|
+ use tor_log::log::{LAST_LOGGED_FUNCTION, LAST_LOGGED_MESSAGE};
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn test_get_log_message() {
|
|
|
+ {
|
|
|
+ fn test_macro() {
|
|
|
+ tor_log_msg!(
|
|
|
+ LogSeverity::Warn,
|
|
|
+ LogDomain::Net,
|
|
|
+ "test_macro",
|
|
|
+ "test log message {}",
|
|
|
+ "a",
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ test_macro();
|
|
|
+
|
|
|
+ let function = unsafe { Box::from_raw(LAST_LOGGED_FUNCTION) };
|
|
|
+ assert_eq!("test_macro", *function);
|
|
|
+
|
|
|
+ let message = unsafe { Box::from_raw(LAST_LOGGED_MESSAGE) };
|
|
|
+ assert_eq!("test log message a", *message);
|
|
|
+ }
|
|
|
+
|
|
|
+ // test multiple inputs into the log message
|
|
|
+ {
|
|
|
+ fn test_macro() {
|
|
|
+ tor_log_msg!(
|
|
|
+ LogSeverity::Warn,
|
|
|
+ LogDomain::Net,
|
|
|
+ "next_test_macro",
|
|
|
+ "test log message {} {} {} {} {}",
|
|
|
+ 1,
|
|
|
+ 2,
|
|
|
+ 3,
|
|
|
+ 4,
|
|
|
+ 5
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ test_macro();
|
|
|
+
|
|
|
+ let function = unsafe { Box::from_raw(LAST_LOGGED_FUNCTION) };
|
|
|
+ assert_eq!("next_test_macro", *function);
|
|
|
+
|
|
|
+ let message = unsafe { Box::from_raw(LAST_LOGGED_MESSAGE) };
|
|
|
+ assert_eq!("test log message 1 2 3 4 5", *message);
|
|
|
+ }
|
|
|
+
|
|
|
+ // test how a long log message will be formatted
|
|
|
+ {
|
|
|
+ fn test_macro() {
|
|
|
+ tor_log_msg!(
|
|
|
+ LogSeverity::Warn,
|
|
|
+ LogDomain::Net,
|
|
|
+ "test_macro",
|
|
|
+ "{}",
|
|
|
+ "All the world's a stage, and all the men and women \
|
|
|
+ merely players: they have their exits and their \
|
|
|
+ entrances; and one man in his time plays many parts, his \
|
|
|
+ acts being seven ages."
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ test_macro();
|
|
|
+
|
|
|
+ let expected_string = "All the world's a \
|
|
|
+ stage, and all the men \
|
|
|
+ and women merely players: \
|
|
|
+ they have their exits and \
|
|
|
+ their entrances; and one man \
|
|
|
+ in his time plays many parts, \
|
|
|
+ his acts being seven ages.";
|
|
|
+
|
|
|
+ let function = unsafe { Box::from_raw(LAST_LOGGED_FUNCTION) };
|
|
|
+ assert_eq!("test_macro", *function);
|
|
|
+
|
|
|
+ let message = unsafe { Box::from_raw(LAST_LOGGED_MESSAGE) };
|
|
|
+ assert_eq!(expected_string, *message);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|